著者アーカイブ asaba

asaba 著者:asaba

【android】ファイルのディレクトリを指定するときの注意点

androidアプリでカメラ機能の実装をするときに、bitmap加工やexifの修正の

ために撮った写真のディレクトリを取ってくる必要があるのですが

今まではEnvironment.getExternalStorageDirectoryで指定することに

よって簡単に写真のディレクトリを見つけることができたのですが、

api29からはユーザーのプライバシーを外部から悪用されないように

と厳しく取り締まった結果このメソッドが非推奨になり

開発者さんたちは泣く泣く変更を余儀なくされました。

 

ですが同じことができなくなったという訳ではなく、代わりに

getExternalFilesDirを使うことで今までと同じように

ディレクトリを指定することができます。

 

他にMediaStoreを使ってファイルを作る方法もあるのですが、

getExternalFilesDirのほうが短いコーディングで済ませられるので

こっちのほうが好みですね。

 

改めてapi29以降で共有ファイルを取得するには以下のように書く

ことで取得できるようになります。

</pre>
<pre>File imageStorageDir = new File(
        getExternalFilesDir(Environment.DIRECTORY_DCIM),
        "child"
);</pre>
<pre>

※DCIMとはandroidが持っている外部ストレージの一部なのですが、sdと
違い切り離すことができません。

なので何もマウントしていない状態で画像を保存する場合は

基本このDCIMに保存されます。

 

 

 

asaba 著者:asaba

【android】mavenCentralとjcenterの関係性

gradleでライブラリを取得するには、jcenter若しくはmavenCentralをgradle

に登録する必要があります。これらが何の役割を持っているかというと

両社とも自由にライブラリを取り出すことができるリポジトリで、androidのライブラリ

やプラグインはほとんどここから取ってきて使うことができます。

少し前まではmavenCentralを使ってライブラリを持ってくることが

普通だったのですが、後から出てきたjcenterはmavenCentralの

持っているライブラリを含めたリポジトリという位置づけになっています。

つまりこれからライブラリと上手く付き合っていくには

mavenCentralをそつぎょうしてjcenterに切り替えておいたほうが後々困らない

ということですね。

 

そうでなくても、targetSdkVersionが28の場合は

Could not find org.jetbrains.trove4j:trove4j:20160824のエラー

でjcenterに切り替えろというエラーが出てくるので、これから

アプリを開発したい・アプリに新機能を追加したい場合は

mavenCentralからjcenterに切り替えておいたほうがライブラリ関係

のエラー処理が少なくて楽そうですね。

 

</pre>
<pre>buildscript {
    repositories {
        jcenter()
        google()
    }


allprojectsのところも同じように修正↓

</pre>
<pre>allprojects {
    repositories {
        jcenter()
        google()
    }
}</pre>
<pre>
asaba 著者:asaba

【androidjava】intentで欲しい値をbundleでまとめる方法

フォームなどで確認画面に移る時にはintentで複数値をもって遷移する

必要があります。

特に特殊な方法で送る必要はなく、intent.putExtraを何回も呼び出すことで可能

です。ですが冗長になってしまう・前のputExtraが上書きされるなどバグのもとを

生みかねないのでbundleを使って遷移する必要があります。

まず、Bundleを定義した後にputStringやputIntなどを使い分けて格納

をしていきます。

<pre class="prettyprint">Intent intent = new Intent(this, MyActivity.class);
Bundle extras = new Bundle();
extras.putString("EXTRA_NAME",name);
extras.putInt("EXTRA_AGE",age);
intent.putExtras(extras);
startActivity(intent);</pre>

 

受取先ではこんな感じで書いていきます。


Intent intent = getIntent();
Bundle extras = intent.getExtras();
String username_string = extras.getString("EXTRA_NAME");
int age = extras.getInt("EXTRA_AGE");

データベースやsharedPreferencesを使って保存したものを次ページで

復元することもできますが、戻る時に値を再度使いたい時は

こっちのほうが分かりやすいかなと思いました。

asaba 著者:asaba

【android】setTextの間違った使い方について

好きなtextをコードから挿入したい時はsetTextを使いますが、setTest

で+”なんとかかんとか”という連結したコードを書くと

Do not concatenate text displayed with setText.という警告に見舞われます。

 

これは、setText内で文字列を連結させるのは推奨しませんよという警告で、

エラーには直結しませんが、javaの書き方としては汚いと見なされ

警告を受けるみたいです。デバッグには影響はありません。

 

ですが将来的にバージョンアップ時に書き方が変更になったり

デバッグがより強硬になった時に足を掬われるのでできれば

ここで潰したいところ。。。

 

このままではむずかゆいのでString.formatを使って引数の

任意の文字列をformat形式にあてはめてsetTextに投げることで

事を収めました。

 


//stateは適当な文字列</pre>
<span style="font-family: Consolas, Monaco, monospace;">@SuppressLint("DefaultLocale") String str = String.format("%sの通報", state);</span>
<pre>contentsText.setText(str);</pre>
<pre>

 

久々に別言語に入ると前の言語での癖がそのままついてくるので

難儀なところです。

自分もしばらくjavascriptばかり書いていたので、知らずにjavascriptの

コードを書いてよくandroidStudioさんに突っ込まれました。

 

他にもstring.xmlに文字列を置いてプレースホルダーとしてactivityに

表示する手もありましたが、上のやり方が一番簡単だったので

こちらを採用しました。

asaba 著者:asaba

【android】edittextの文字制限

edittextで文字制限をするときはどうするのかなと考えながらググってみると

意外と簡単にできてしまいました。

 

javaScriptだと、inputtypeでpatternで[A-Z]*を指定するだけでできてしまうのですが

こちらではxmlにそのまま書き込むことができます。

例えば、数字以外を入れてほしくない場合は、digitsプロパティで1234567890を指定します。

 

</pre>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tel"
android:hint="電話番号"
android:maxLength="11"
android:digits="1234567890"/>
<pre>

 

これを入れるだけで、数字以外の文字をキーボードで打っても文字が入らない

ようになります。

 

これだけでも上等ですが、数字を入れるからにはキーボードの起動時には

数字から入ったほうがユーザーには優しいと思うので、

今度はinputTypeでnumberを指定してみます。

</pre>
android:inputType="number"
<pre>

 

これでキーボードが起動時に数字から打てるようになります。

数字以外にも、無効な文字が打たれた時にキーボードの入力を

感知するコードを作って組み合わせれば幸せになれそう。

asaba 著者:asaba

【androidアプリ】写真情報確認アプリでexifを楽に操作する

今回のお題は、自分にとってはもう少し前に知っておきたかったなという感じの内容になります。

画像加工アプリにおいてexifの内容の取得は必須スキルですが、万一に備えてちゃんとほしい

exifが取れているか確かめたい時がありますよね。

自分は、「写真情報確認」というアプリを使っていたのですが、

どのexifが取れているかいい感じに判別してくれたので、これは良いと思い

さっそくお気に入りアプリの一つとして使っております。

アイコンはスクショにある信号機みたいなものです。

 

 

開いてみると、今まで自分が撮ってきた写真一覧が表示されます。

その中で赤・黄・青とシグナルがあるのですが、何を意味しているかというと

赤は「位置情報まで取れていますよ」、黄は「位置情報以外の情報が撮れていますよ」で、

青は「exif情報は存在しません」。の意味になります。

 

このシグナルに合わせてこのアプリでは様々なアクションを使うことができます。

画像を参照↓

 

 

 

例えば赤の状態だと、位置情報が付いたままなのでこのまま位置情報を悪用されない

ためにもexif削除機能を使うことが出来ます。

他にも、やっぱりアプリ内でexif情報を使いたいという時のためにもexif情報復旧機能も

備わっており、実際にexifを操作することもワンタッチでこなせます。

uiもわかりやすく、かなり自由の利く機能を備えていますね。

 

総合的に見てめちゃめちゃ使いやすいですが、android5.0だとちょっと重いかなと感じました。

android7.0以上だったらさくさく動けてストレスフリーでexif操作ができるので

おすすめです!

asaba 著者:asaba

【android】撮影した画像を即時にフォトアルバムに載せる

もうずいぶん前に作ったのですが、androidで写真を撮った時に速攻でフォト

アルバムに載せる関数について説明します。

androidの写真は、普通に撮影してもすぐには反映されず、撮った写真を

すぐに利用したいときはその弊害をもろに受けます。

こうなればandroidのデータベースに動的に登録してしまおうということで

データベースにアクセスしbitmapからファイルパスを生成して

そのまま保存してしまうことにしました。

<pre>// アンドロイドのデータベースへ登録する
private void registerDatabase(Bitmap bitmap) {
    ContentValues contentValues = new ContentValues();
    ContentResolver contentResolver = GetPictureActivity.this.getContentResolver();
    contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    File path = new File(MediaStore.Images.Media.insertImage(this.getContentResolver(), bitmap, "Title", null));
    contentValues.put("_data", String.valueOf(path));
    contentResolver.insert(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
}</pre>

これで、後はどこかのタイミングでbitmapを設定して写真が保存されていれば

成功です。

asaba 著者:asaba

【android・bitmp】bitmapからblobを作成する方法

bitmapとして生成したものは直接データベースには保存できません。

送るにはまずbyte配列に変換してblobに対応させる必要が

あります。blobとは、バイナリデータを格納するための型の一つで、

おおよその画像はデータベースに保存される際はblobに格納されます。

 

一般的には、ByteArrayOutputStreamを使って変換することで

データベースに格納できるようになります。


//bitmap imgには画像が入っていると仮定する
<pre>Bitmap img;</pre>
<pre>ByteArrayOutputStream baos = new ByteArrayOutputStream();
img.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] arr = baos.toByteArray();
System.out.println(arr);
values.put("image", arr);</pre>

 

変換が成功すれば後はputで自由に送ることができます。

 

追記:こちらはbitmapからUriを作成する方法です。

new Fileのところは適宜変えても大丈夫そう。

<pre>File path = new File(MediaStore.Images.Media.insertImage(this.getContentResolver(), img, "Title", null));
Uri uri = Uri.parse(String.valueOf(path));</pre>
asaba 著者:asaba

【android】onSaveInstanceStateでメンバ変数を復元する

activityは、画像の回転やブラウザの移動に伴い、破棄されて新しいactivityに

生成されるライフサイクルを持っています。

仮に遷移前にメンバ変数を持っていた場合、移動後は基本destroyされ

またonCreateされるため、持っていた値が無かったことになり

全部消えてしまいます。

 

値を保存して新しいactivity生成時にまた同じ値を使いたいという時は、

onSaveInstanceStateを使って保存する方法があります。

保存対象は主にメンバ変数で、edittextやtextViewといったview系の値はviewが独自で

復元してくれるのでそちらは考慮する必要はないです。

ではどうやって保存するかというと、まずonSaveInstanceStateメソッドをこのように

書いていきます。

 


@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("str", mName);
savedInstanceState.putInt("int", mAge);
}

 

保存する値は、Bundleというクラスが持っているput○○というメソッドに

持たせて保存します。

この他にも、浮動所数点以下の値を保存できるputFloatや画像を保存する

putParcelableがあります。

 

後はどのタイミングで呼ばれるかですが、呼ばれる順番はonparseのすぐ後になります。

onparse->onSaveInstanceState->onDestroy->onCreate(新しいactivity作成)

と覚えていれば問題ないと思います。

 

次に保存した値をどうやって復元するかですが、onSaveInstanceStateで保存した値は

onCreate内でgetStringやgetIntしてメンバ変数に入れるだけで再利用することができます。

 

</pre>
//onCreate内

if (null != savedInstanceState) {
mName = savedInstanceState.getBoolean("str");
mAge = savedInstanceState.getInt("int");
}
<pre>

 

若しくはonRestoreInstanceStateを使って復元するか

 

</pre>
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mName = savedInstanceState.getBoolean("str");
mAge = savedInstanceState.getInt("int");
}
<pre>

 

注意点ですが、画像などのデータ量が大きいものを保存しようとすると

android.os.TransactionTooLargeException: data parcel size 32445533 bytesと

エラーを受けるので、どうしても再利用したい場合は必ずデータ量を小さくしたり

Uriのまま保存することをおすすめします。

asaba 著者:asaba

【android】intentの戻り値には、帰ってきた値に合わせて細かく切り分けて対応する

intentで何かの処理をした結果は、コールバックであるonActivityResultの引数として

返ってきます。この中のrequestCodeとresultCodeの内容に合わせて処理を分けていくのですが、

例えばカメラで撮った画像を受け取りたい場合は下記のようにかきます。

 


if(requestCode == RESULT_CAMERA && resultCode == RESULT_OK)

 

左辺がカメラモードに入った状態、右辺がカメラが正常に終了した状態。

この条件が二つ合った場合のみ処理に入っていきます。

ギャラリーの場合も同じように

 

<pre>if(requestCode == REQUEST_GALLERY && resultCode == RESULT_OK)</pre>

 

と書いていきます。requestCodeの判定だけでは縛りが甘く、リクエストした

結果が得られない時があります。

 

例えば、カメラモードに入って何も撮影せずに戻ろうとした時。

上のカメラ撮影処理において、条件がrequestCode == RESULT_CAMERAのみ

だった場合、本来ならば戻る=resultCode==RESULT_CANCELと決まっているのですが

条件が一つだけなので、カメラ撮影が成功したと判断されその下の処理へと

走ってしまいます。すると当然Uriが存在しないので何もない状態のimageViewを

返してしまう羽目になるのです。

 

自分の場合は条件がrequestCode == RESULT_CAMERAのみで、更に関数にvoidが

ついているにも関わらず分岐の中でreturnを使った処理を付けてそのままにしておいた

ためカメラモードで戻るを押した際にimageviewが消失する事故が起きました。

(恥ずべき間違いとは言えない)

この沼にはまると結構な時間を取られてしまうので注意したいですね。

 

onActivityResultで結果を受け取る際はresultCodeの結果もチェックして

処理を書いていきましょう。

 

下記テンプレコード

<pre>@Override
protected void onActivityResult(int requestCode,
                                int resultCode, Intent intent) {
    
    if (requestCode == RESULT_CAMERA && resultCode == RESULT_OK) {
      //カメラの処理</pre>
<pre>      } else if(requestCode == REQUEST_GALLERY && resultCode == RESULT_OK){
   //ギャラリーの処理</pre>
<pre>        } else if(resultCode == RESULT_CANCELED){
    ※カメラ・ギャラリーモードがキャンセルされた時(重要)
        }
      }
    }
</pre>