カテゴリーアーカイブ Java

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)
asaba 著者:asaba

【android・googlemap】緯度経度から住所を測定する方法

シンプルですが、画像に含まれた緯度経度を読み込んでテキストに表示する方法です。

 

</pre>
<pre>public void getAddress(double latitude,double longitute) {
    StringBuffer strAddr = new StringBuffer();
    Geocoder gcoder = new Geocoder(this, Locale.getDefault());
    try {
        List<Address> lstAddrs = gcoder.getFromLocation(latitude, longitute, 1);
        for (Address addr : lstAddrs) {
            int idx = addr.getMaxAddressLineIndex();
            for (int i = 0; i <= idx; i++) {
                strAddr.append(addr.getAddressLine(i));
                Log.v("addr", addr.getAddressLine(i));
            }
        }
        String newStr = strAddr.toString();
        String[] names = newStr.split(" ");
        dangerText.setText(names[1]);
        Toast.makeText(this, names[1], Toast.LENGTH_LONG).show();
    } catch (IOException e) {
        e.printStackTrace();
    }
}</pre>
<pre>

緯度経度さえ用意できてれば簡単に実装することができます。

5行目のgcoder.getFromLocationで緯度経度が取れているか確認してみます。

ログでこんな感じで取れていれば取得成功です。


[Address[addressLines=[0:"日本、〒432-8002 静岡県浜松市中区富塚町1933−1 佐鳴湖パークタウンサウス 2F"]

中身を見てみると、0番目に国名・郵便番号・住所が入っているのがわかります。

今回取りたいものは0番目の情報なので、簡単なfor文で取り出していきます。

取得には成功しましたが、国名と郵便番号はいらないのでsplitで空白別に配列の中に

切り分けていきます。

0:”日本、〒432-8002と静岡県浜松市中区富塚町1933−1の間に空白が一つあるので

住所「静岡県浜松市中区富塚町1933−1」が入っているのは1番目になります。

これで取り出し・加工作業は終了。

格納場所が分かれば後はsetTextやtoastで好きなように扱うことができます。

 

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

【android】androidoneはデフォルトでorientationを修正している?

今まで様々な機種でexif情報取得してきましたが、その中でもどうもandroidoneだけorientationを

自前で修正してるっぽいです。一般的にはandroidはアプリで写真を撮った場合に

縦横を誤認識するバグがあり、いちいちexifinterfaceでorientationを拾って

加工しないと正しい向きにならないというめんどくさい仕様になっています。

がこのandroidoneは一癖ありました、はい。

 

一応XperiaとNexusでは検証済みで、どちらもorientationが正しく認識されていません

でした。本題であるkyocera製のandroidoneでカメラ・ギャラリーを取得したのですが

何度やっても正しい向きの状態でアップロードすることができました。

代わりに位置情報が取れていない、若しくは最初から欠落している状態でしたが

地図を使わないアプリであれば問題なさそう。

(位置情報がないならsetattributeで追加すればいいじゃない)

 

写真の保存場所も今までと同じ内部ストレージなのでいちいち機種に合わせたディレクトリ

取得コードも書かなくて済みそうです。

他のアクティビティに映したときやデータベースに保管して再度アップロードした

時が気になりますが、規模が小さいアプリを作る際は手間が省けて楽だなと感じました。

さすがに機種依存に関する違いはゼロではないと思いますが、デバッグ時はかなり快適

だったのでデバッグに使っても損はなさそう。

(こいつばかりでデバッグしても他の機種だとバグを引き起こす可能性があるので

やはり実機は複数で試すべきですけどね)

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

【android・googlemap】ドラッグしたマーカーの位置取得について

前回はgooglemapのパーミッションを許可して実際に使うまでの流れを説明しましたが

、今回はgooglemapをカスタマイズするのに必須であるマーカーについて説明

していきます。

googlemapは、初期状態の時はマーカーも現在地取得ボタンも何もない状態であり、

初期位置がアフリカの状態でスクリーン表示されます。

しかしながらせっかくgooglemapを使ってアプリを作るので、今いる自分の

場所をピックアップして実際に中心位置に設定していきたいと思います。

 

まず、onMaoReady関数の中にaddMarkerのコールバック関数を書いていきます。

googlemapはこれを書くだけで中心位置を決めてくれます。なお、latlongがnullだったり

数値でなかったりすると大西洋の端に勝手に位置が決まるので注意してください。


mMap.addMarker( new MarkerOptions()
.title( "ピンのタイトル" )
.draggable(true)
.position( latLng ) );

 

potisionのカッコ内はこんな感じ。お好みで緯度経度を決めてください。

 

<pre>latLng = new LatLng(latitude, longitute);</pre>

 

これでマーカーの位置は決定することができました。

主題である「どうやって動かしたマーカーの位置情報を取得するか」ですが、

マーカーのドラッグ位置を感知するための関数が用意されているのでまずは

そちらを使います。

説明のためにもうコードを出してしまいましょう。

<pre>mMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() {
    @Override
    public void onMarkerDrag(Marker marker) {
        // TODO Auto-generated method stub
        // Toast.makeText(getApplicationContext(), "マーカードラッグ中", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onMarkerDragEnd(Marker marker) {
        // TODO Auto-generated method stub
        Toast.makeText(getApplicationContext(), "マーカードラッグ終了", Toast.LENGTH_LONG).show();
        LatLng d = marker.getPosition();
        double newlat =d.latitude;
        double newlong =d.longitude;
        System.out.println(newlat);
        System.out.println(newlong);
    }

    @Override
    public void onMarkerDragStart(Marker marker) {
        // TODO Auto-generated method stub
        Toast.makeText(getApplicationContext(), "マーカードラッグ開始", Toast.LENGTH_LONG).show();
    }
});</pre>

関数にはそれぞれonMarkerDrag、onMarkerDragEnd、onMarkerDragStartと

ありますが、肝心の位置情報はマーカーを離したときに取得したい。つまり、

onMarkerDragEndの中に処理を書いていきます。

 

肝はこのmarker.getPosition()で、このメソッドでマーカーのドラッグ終了時の位置を

読み取りLatLngに格納しているのが分かります。

取得後d.latitudeとd.longitudeそれぞれの位置情報をdouble変数で抜き取れば後は

こっちのものなので自由に使うことができます。

ここまで短いですがこれでマーカーの位置取得の説明を終わります。

必要なコードもそこまでないのでgooglemap初心者のかたでもさくっとできそうです。

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

【androidjava】googlemapで使用するapi-keyが正常に読み込まれない時の対処法

2018年度からgooglemapをアプリで使う際はgoogleCnosoleDeveloperでgoogleMapApi

ライブラリを有効にしないとgooglemapが正常に起動できなくなりました。

自分が今作っているアプリも例外でなく、表示する際に灰色のスクリーン

がでてきてしまいます。登録だけならいけるだろと思っていたのですが

これがまためんどくさい仕様でかなり沼ったので書き置きしていきます。

ちなみにここでは、googleアカウントを作成してある前提で

説明していきますのでご容赦ください。

 

まず、googlemapを正しく使うには、登録の際にはパッケージ名とフィンガプリント

(証明書)の二点が必要になります。パッケージ名はactivityの一番上にあるので

分かりやすいですね。一方でフィンガプリントはどうやって見るかというと、

まずcmdに移動して

keytool -list -v -keystore “C:\Users\ユーザー名\.android\debug.keystore”

-alias androiddebugkey -storepass android -keypass android”

と打ち込みます。すると、ずららっと色々な署名系の暗号が

書いてあるのが分かります。その中でsha1:から始まるローマ字と数字が合わさった

文字列がこれからgoogleCnosoleDeveloperで使うフィンガープリントに

なるので間違えないようコピーをしておいてください。

 

 

これでフィンガプリントの確保はできたので、実際にgoogleCnosoleDeveloperでgooglemap

を使えるようにしていきたいと思います。

 

まず、ログイン後ダッシュボードに移動したら画面上にあるAPIの有効化をクリックします。

 

 

移動後にMapsSDKForAndroidをクリックします。その後管理ボタンをクリック。

この時点でAPIKeyを取得できます。

 

その後apiとサービスの認証情報で新しい認証情報を作成し、外部からの悪用を

避けるためのapiキーの制限を設定します。

設定ページに移動したら、パッケージとフィンガプリントを入れるフォームがあるので

そちらに自分のパッケージとフィンガプリントを入れていきます。完了後に保存します。

 

 

 

適用されるまで少し時間がかかりますが(最長で5分)これが終わればgoogleApiが有効に

なっているので少しづつマニフェストに書いていきます。

 

上記の設定を全て終わらせたら、androidManifestに必要事項を書いていきます。

まず、applicationタグ内にmeta-dataタグを設定していきます。

(自分はここでハマりました。なのでこちらは後で説明します。)

</pre>
<pre><meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="AIzaSyBc5xjIzF1vpeS2Kt1_vAXFRQGdeebZNLQ"/></pre>
<pre>

 

上記のmeta-dataには、先ほど取得したapiキーを入れていきます。

その後下記のようにgoogle-play-serviceのパーミッションを設けます。

 

</pre>
<pre><meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />
<meta-data</pre>
<pre>

最後に、googolemapのパーミッションを書いて終了です。

こちらの作業後にビルドしてマップが出れば成功です。が、自分の場合はマップが

グレースクリーンで返されました。一通りパーミッション等を見ても設定漏れも

パッと見ない・・・。よくわからず。

 

仕方がないのでandroidStudioで吐かれたエラーを見てみることに。

すると、googleCnosoleDeveloperに登録したパッケージ名とログで吐かれたパッケージ名

が異なることに気づきました。

 

activityの一番上にあるpackage com.なんとかかんとか;って書いてあるのが

パッケージじゃないんかい!!と突っ込みつつandroidStudioログ側のパッケージ

をgoogleCnosoleDeveloper内の新しいアイテムにて登録。

これで、晴れてマップを表示させることができました。

以上でgooglemapの使用法とエラー対処法は終わりです。

似たような悩みがある方はまずandroidStudioで吐き出されるパッケージ名と

フィンガプリントを確認してみてください。以上です。

 

androidStudioで吐かれたログがこちら↓

</pre>
<a href="https://cpoint-lab.co.jp/wp-content/uploads/2019/09/googlemaperror.jpg"><img class="alignnone size-medium wp-image-11563" src="https://cpoint-lab.co.jp/wp-content/uploads/2019/09/googlemaperror-300x33.jpg" alt="" width="300" height="33" /></a>
<pre>
  • この記事いいね! (0)
asaba 著者:asaba

【xml】textのbackgroundを調整して重複したように見せる方法

以前まではimageViewの上にテキストを載せる手法を載せたのですが、今回はテキストの

backgroundの色を変えてレイアウトが重なったようにみせかけるレイアウト方法を

載せていきます。

方法は簡単で、textviewを通常通りに定義して各方向のpaddingを使うことで

重なったようなレイアウトにすることができます。

この手法はrelativeLayoutでも使うことができるので、アプリの見出しとかにもがんがん

使えそうです。

<pre><RelativeLayout
    android:id="@+id/relativeLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:ignore="MissingConstraints">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fff"
        android:paddingBottom="10dp"
        android:paddingStart="20dp"
        android:paddingEnd="20dp"
        android:paddingTop="10dp"
        android:text="是非もないよね!"
        android:gravity="center"
        android:textSize="16sp"
        android:textStyle="bold" /></pre>
<pre></RelativeLayout></pre>

 

ポイントは各四方のpaddingで、bottomとtopの値を調整することで均等に

テキストを見せることができるので使い慣れておきましょう。

imageviewと組み合わせるよりも、書く時のコードもメンテナンス時の直すコードも

少ないので、特殊なカスタムボタンを作る時以外はこっちのが断然おすすめです。

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

【android】ExifInterfaceで複数のプロパティを取得するときに気を付けること

前々回くらいにorientationの取得の方法を書きましたが、せっかくexifinterfaceを使えるように

なったので他のプロパティもとってみようということで緯度と経度の値も

取得してみることに。しかし、今度はorientationは取れているのに緯度経度の値がnull

になっていました。stack overflowにはiuputStreamを介すとexifの値が付かないということ

でしたが、それではなぜorientationが取れるという疑問しか浮かばず沼りました。

androidについているカメラで撮影してexifを見るとしっかりついているし・・・。

やはりonActivyResultを介さずにメンバ変数でUriを定義したので多少不安はあったのですが

それでもUriファイルからexifプロパティが抜けるなんてありえないですし。

正直あまり自信はなかったのですが、BitmapとUriを引数にしている関数にcontextを追加して

無理やり解決させました。

根拠がない中での修正という一番怖い手法で取得することに成功しましたが、晴れて双方

取得することができたので載せておきます。お疲れ様でした。

</pre>
<pre>public Bitmap load(Bitmap bitmap, Context context,Uri uri){
    try {
        InputStream in = getContentResolver().openInputStream(uri);
        ExifInterface ex = new ExifInterface(in);
        int orientation = ex.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
        in.close();
        push(ex);
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return rotateImage(bitmap, 90);
            case ExifInterface.ORIENTATION_ROTATE_180:
                return rotateImage(bitmap, 180);
            case ExifInterface.ORIENTATION_ROTATE_270:
                return rotateImage(bitmap, 270);
            default:
                return bitmap;
        }

    }catch(Exception e){
        e.printStackTrace();
    }
    return bitmap;
}

</pre>
<pre>public void push(ExifInterface ex){
    String latitude = ex.getAttribute (ExifInterface.TAG_GPS_LATITUDE);
    String longitude = ex.getAttribute (ExifInterface.TAG_GPS_LONGITUDE);
    String latitudeRef = ex.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
    String longitudeRef = ex.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
    double replaceLati = ExifLatitudeToDegrees(latitudeRef,latitude);
    double replaceLong = ExifLongitudeToDegrees(longitudeRef,longitude);
    System.out.println(replaceLati);
    System.out.println(replaceLong);

}
private double ExifHourMinSecToDegrees(String exifhourminsec) {
    String hourminsec[] = exifhourminsec.split(",");
    String hour[] = hourminsec[0].split("/");
    String min[] = hourminsec[1].split("/");
    String sec[] = hourminsec[2].split("/");
    double dhour = (double)Integer.parseInt(hour[0]) / (double)Integer.parseInt(hour[1]);
    double dmin = (double)Integer.parseInt(min[0]) / (double)Integer.parseInt(min[1]);
    double dsec = (double)Integer.parseInt(sec[0]) / (double)Integer.parseInt(sec[1]);
    double degrees = dhour + dmin / 60.0 + dsec / 3600.0;
    return degrees;
}

private double ExifLatitudeToDegrees(String ref, String latitude) {
    return ref.equals("S") ? -1.0 : 1.0 * ExifHourMinSecToDegrees(latitude);
}</pre>
<pre>

P.S.これに加えて機種別の処理も加味しなければいけないし本当にカメラ周りは鬼畜すぎる・・・。

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