カテゴリーアーカイブ Java

asaba 著者:asaba

【android】ContentValuesでorientationを操作する

androidは、撮影した画像を即時にファイルに保存できないので、

(時間が経ってから・若しくは再起動で保存される)開発側でcontentValue

を用意してfileの場所を登録して書き込む必要があります。

androidで画像を保存するときはこのcontentValueに用意されている

putメソッドを使うことでandroidのギャラリーにすぐに使えるようになります。

 

このライブラリを使ってandroidのデータベースに問い合わせるのですが、

こちら側ではカラムとデータを設定するだけでsqlliteでやるような

sql操作は特に何もしないのでsqlをあまり触ったことのない方でも

簡単に書くことができるのでおすすめです。が、contentValueが

受け取るのは何も加工していないファイルになるので、当然このまま

保存しても期待した結果にならない時もあります。

 

例えば、bitmapをexifIntefaceを使ってactivityに表示したのに

contentValueで経由して保存した写真見ると横向きになっていたりします。

これは、exifInterfaceを経由して向きを回転させる前のファイルを

使用せず、生成したファイルオブジェクトをそのまま送っているためです。

 

一応bitmapをbitmap->uri->fileと分解して正しい向きに向かせられるのですが

これだとexif情報が全部抜けてしまい画像としては内容がなさすぎて

使い物になりません。

 

かくいう自分もこのギャラリー内の画像のexif処理にハマっていたのですが、

contentValueではputからorientationを操作できることを知ったので

こちらを使うことにしました。

カラムとデータに設定する方法は以下の通りです。

 


contentValues.put(MediaStore.Images.Media.ORIENTATION, 90);
contentValues.put(MediaStore.Images.Media.LATITUDE, replaceLati);
contentValues.put(MediaStore.Images.Media.LONGITUDE, replaceLong);

 

このように、カラムで修正したいexif情報をMediaStoreで書き込むことで

ファイルに保存される写真にデータを追加することができます。

 

これらの存在を今日の今日まで知らずに使っていたのが怖い・・・。

 

 

  • この記事いいね! (0)
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に保存されます。

 

 

 

  • この記事いいね! (0)
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>
  • この記事いいね! (0)
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を使って保存したものを次ページで

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

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

  • この記事いいね! (0)
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に

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

こちらを採用しました。

  • この記事いいね! (0)
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>

 

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

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

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

  • この記事いいね! (0)
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>
  • この記事いいね! (0)
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のまま保存することをおすすめします。

  • この記事いいね! (0)
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>

 

 

  • この記事いいね! (0)
asaba 著者:asaba

【android】SharedPreferencedで保存した文字列とgridviewのpositionを照合する

SharedPreferencedは、アプリ開発において設定の保存・テキスト単体の読み出しの処理に

向いている便利なライブラリですが、gridviewと合わせることでpositionに合わせた

テキストを表示させることもできます。

まず最初に、ここではオブジェクトリテラルを使いたいのでputメソッドでkeyとvalueを

追加します。

 

sampleActivity↓

</pre>
<pre>Map<Integer, String> contents = new HashMap<Integer, String>();

</pre>
<pre>contents.put(0, "みかん");
contents.put(1, "りんご");
contents.put(2, "ぶどう");
contents.put(3, "すいか");
contents.put(4, "メロン");
contents.put(5, "マンゴー");
contents.put(6, "いちご");
contents.put(7, "パイナップル");
contents.put(8, "洋ナシ");
content = getSharedPreferences("content", MODE_PRIVATE);</pre>
<pre>

 

gridviewを押した時にcontentsに追加したvalueと比較をするので

onItemClickの中で処理をしていきます。

 

</pre>
<pre>//押した項目によって内容が変わる
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    System.out.println(position);
    SharedPreferences.Editor editor = content.edit();
    for(Map.Entry<Integer, String> entry : contents.entrySet()){
        System.out.println(entry.getKey() + ":" + entry.getValue());
       if(entry.getKey() == position){
           editor.putString("key", String.valueOf(entry.getValue()));
           editor.apply();
       }
    }
    Intent intent = new Intent(getApplication(), GetPictureActivity.class);
    startActivity( intent );
}</pre>
<pre>

 

新たにMap.entryでinteger・String型の配列を作ります。

4行目のforで先ほどvalueを入れたcontentsをentry配列に入れているのが分かります。

ここで一つずつgetKeyで得たキーの値と押された番号(int position)が合っているか

確認し、一致すればeditor.applyで保存をします。

値が合うまではずっと回り続けるので空の値が保存されましたーなんてことも

防ぐことができます。

 

次は、遷移先でさきほど取得したvalueの取り出し作業をします。

 

</pre>
<pre>TextView contentsText = findViewById(R.id.contents);
SharedPreferences data = getSharedPreferences("content", MODE_PRIVATE);
String state = data.getString("key", "" );
contentsText.setText(state);</pre>
<pre>

 

こちらでは従来のSharedPreferencesの取り出ししかしていないので特に説明は

しなくても大丈夫そう。

テキストだけ取得する場合はsqlliteで保存したりintentでいちいち手渡しする

必要もなさそうなのでこっちのがいいかなと思っています。

  • この記事いいね! (0)