著者アーカイブ asaba

asaba 著者:asaba

【androidJava】exifInterface:exif情報の取得

前回はexifInterfaceを使うためのgradleの設定をしたので、今回いよいよ実際にコードを書いて説明していきたいと思います。

 

前述のサポートにより、画像を扱うアプリではInputstreamを使うことでExifInterfaceがぐっと使いやすくなりました。

これにより、受け取ったURIから直接画像のexif情報を取得できるようになりました。

exifInterfaceをnewする前にInputStreamを初期化しておきましょう。

<pre>InputStream in = null;</pre>

ここでようやくexifInterfaceクラスを使うことが出来ます。次に向き(orientation)の取得ですが、ここではexifInterfaceクラスの

メソッドgetAttributeIntを使って向きを取得します。getAttributeIntの使い方ですが、取得する情報のタグを指定して値を取得する.

だけで使うことができます。例えば、何の変哲もない修正していない画像を取得する場合はExifInterface.ORIENTATION_NORMALを

指定します。

<pre>int orientation = exifInterface.getAttributeInt(
        ExifInterface.TAG_ORIENTATION,
        ExifInterface.ORIENTATION_NORMAL);</pre>

無事に取得できれば後はこのorientationを変更したい角度に合わせてswitch文にセットすれば対象の画像のexif情報を

取ってくることができますね。

<pre>switch (orientation) {
    case ExifInterface.ORIENTATION_ROTATE_90:
        rotation = 90;
        break;
    case ExifInterface.ORIENTATION_ROTATE_180:
        rotation = 180;
        break;
    case ExifInterface.ORIENTATION_ROTATE_270:
        rotation = 270;
        break;
}</pre>

ExifInterface.ORIENTATION_ROTATE_〇〇はぱっと見分かりづらいですが、ものすごくかみ砕いて「この角度にすれば正しい位置になるよ」

と解釈しておけばその内慣れてくると思います。

次回はこのメソッドで取得したrotation変数を使って実際に向きを変えて表示してみましょう。

断片的なコードだけでは戸惑うかたもいると思うのでメソッド含めた全コードを載せておきます。


public class sample extends AppCompatActivity {

private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
imageView = findViewById(R.id.image_view);
int locatate;
int rotation = 0;
Bitmap bitmap;
Uri uri;
Intent intent = getIntent();
uri = intent.getParcelableExtra("imageUri");
try {
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
//画像の向きを取得
getBmp(uri,rotation);
} catch (IOException e) {
e.printStackTrace();
}
}
public int getBmp(Uri uri,int rotation) {
  InputStream in = null;
  try {
    in = getContentResolver().openInputStream(uri);
    ExifInterface exifInterface = new ExifInterface(in);
    //緯度経度を取得
    getLatlong(exifInterface);
   //画像の向きを取得
    int orientation = exifInterface.getAttributeInt(
    ExifInterface.TAG_ORIENTATION,
    ExifInterface.ORIENTATION_NORMAL);
   switch (orientation) {
     case ExifInterface.ORIENTATION_ROTATE_90:
     rotation = 90;
     break;
     case ExifInterface.ORIENTATION_ROTATE_180:
     rotation = 180;
     break;
     case ExifInterface.ORIENTATION_ROTATE_270:
     rotation = 270;
     break;
  }
    System.out.println(rotation);
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    if (in != null) {
      try {
        in.close();
      } catch (IOException ignored) {
    }
  }
}
     return rotation;
}
}

 

 

asaba 著者:asaba

【androidJava】exifInterfaceの初期化

長い間exifInterfaceの使い方がおぼろげだったのですが、理解が深まってきたところで小出しで記事をかくことにしました。

今回はチュートリアルみたいな感じでいきなり実践はせず設定の部分だけアウトプットすることにしました。

 

本題ですが、このexifInterfaceというのは1994年に富士フィルムが開発した画像のデータを含んだ形式のことを指します。

このデータを使うことで、角度を修正したりリサイズをしたり写真を撮った位置所法を取得できたりと画像を扱うプログラム

ならほぼ網羅することができます。

 

このExifInterfaceはapiレベルが24以上と推奨されていましたが

Support Library 25.1.0 のリリースに合わせて新たに ExifInterface Support Library が追加されたので、

apiが24以下の端末でも利用できるようになりました。

 

まずはgradleでminSDKVersionが実機より高くないか確認して、高かった場合はその実機のAPIより低くする必要

があります。

例えば、使用している実機のapiが22で、minSdkVersionが24だった場合はこのままでは怒られて使用できないので

minSdkVersionのapiレベルを下げましょう。

 

しかしminSDKVersionが低すぎても警告を受ける場合があるので今実機として使っているandroidのapiレベルが同じになるように

合わせてgradleのminSDKVersionを調整してみてください。

例えば実機のapiレベルが22の場合は


<span style="font-size: 14pt;">minSDKVersion 16 ×</span>

 


<span style="font-size: 14pt;">minSDKVersion 22 〇</span>

 

念のためにぴったりにしておいたほうが間違いないのでw

 

マニフェスト部分にもしっかりとパーミッションを書いておきましょう。

<pre><uses-sdk android:minSdkVersion="22" /></pre>

 

これでexifIterfaceを使う下準備は終わりました。次回は実際にexifInterfaceを使って向きを取得してみましょう。

 

 

 

 

asaba 著者:asaba

【androidJava】カメラ・ギャラリーを同じダイアログに入れて選択させる方法

javascriptでできなかったカメラとギャラリーが同じダイアログに入った機能があっさりできてしまいました。

この場合はコードを先にみてしまった方が早いと思うので端折ります。

//一番最初にカメラ・ギャラりーを識別するための定数を記述しておく
private static final int REQUEST_CHOOSER = 1000;
private void showGallery() {

    //カメラの起動Intentの用意
    String photoName = System.currentTimeMillis() + ".jpg";
    ContentValues contentValues = new ContentValues();
    contentValues.put(MediaStore.Images.Media.TITLE, photoName);
    contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    m_uri = getContentResolver()
            .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

    Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (intentCamera.resolveActivity(getPackageManager()) != null) {
        // Create the File where the photo should go
        File photoFile = null;
        try {
            photoFile = createImageFile();
            System.out.println(photoFile);
        } catch (IOException ex) {
            // Error occurred while creating the File
            Log.d("Error", ex.getMessage());
            Toast.makeText(getBaseContext(), ex.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
    // ギャラリー用のIntent作成
    Intent intentGallery;
    if (Build.VERSION.SDK_INT &amp;amp;amp;lt; 19) {
        intentGallery = new Intent(Intent.ACTION_GET_CONTENT);
        intentGallery.setType("image/*");
    } else {
        intentGallery = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intentGallery.addCategory(Intent.CATEGORY_OPENABLE);
        intentGallery.setType("image/jpeg");
    }
    Intent intent = Intent.createChooser(intentCamera, "画像の選択");
    intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {intentGallery});
    startActivityForResult(intent, REQUEST_CHOOSER);
}
</pre>
<pre>private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "PNG_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
            imageFileName,  /* prefix */
            ".png",         /* suffix */
            storageDir      /* directory */
    );
    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = Uri.parse(image.getAbsolutePath());
    return image;
}</pre>
<pre>

カメラ・ギャラリーから取得した画像をonActivityResultで受け取って処理をします。

 

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode == REQUEST_CHOOSER) {
  //ここに書きたい処理を書く</pre>
<pre>  resultUri = (data != null ? data.getData() : m_uri);</pre>
<pre>  } 
} 

二番目の引数と定数REQUEST_CHOOSERが同じ値なので、if内に処理をしていきます。

上記の例のようにgetDataでuriを取得をすればもうご自由にuriを使うことが出来ますね。

asaba 著者:asaba

【cordova-react】またRが定義されていない・・・

androidのネタになります。

 

いつもどおりtextViewとimageViewをxmlに追加してアクティビティにもオブジェクトを追加してビルドしたのですが

またCannot resolve symbol Rに呼び止められてしまいました(怒)

 

いつもどおりゴミが溜まったのかと思いクリーンビルドしてみたのですが今度はそれが原因ではないみたいです。

ならばと思い範囲を広げてRebuild projectを実行。ログでサクセスが出てきたので今度こそ

治ったかと思ったらまたCannot resolve symbol R・・・失敗しました。

 

先人の知識を借りて画面左下のbuild Valiantsを押してデバッグモードからリリースモードに変更して

再度ビルドしてみても結果は変わらず。

 

やっぱり定義先に問題があるかと思い再度xmlのページにいって再調査。

そういえばimageViewは前のプロジェクトからそのままコピペしたな~もしかしたらこのxmlで使っていない

属性が定義されていてそれが悪さしてるのかと思いそこを重点的に探しました。

 

すると、使っていないっぽい属性を発見。

<ImageView
android:id=”@+id/image_view”
android:scaleType=”center”
android:contentDescription=”@string/description”←こいつだ!
android:layout_margin=”10dp”
android:layout_width=”match_parent”
android:layout_height=”wrap_content” />

 

前のプロジェクトでstring.xmlでボタンの名前などを定義するときに使っていたcontentDescription。

 

こいつはこのプロジェクトでは使っていなかったので迷わず削除して再ビルド。

すると見事にビルドが成功、エラーの原因を突き止めることができました。

 

前のプロジェクトから似たようなコードをコピペするときは使っていないメソッドや属性を確認してから

組み込みましょう。ということを再実感しました。

 

とりあえず同じエラーが起きることのないようリファクタリングを厳しくしましょう。

asaba 著者:asaba

【androidJava】intentでbitmapを遷移させる方法

androidではintent機能を必ずといっていいほど使います。

値を遷移させたりカメラで撮影画面に映ったりなど様々な場面で使うことが出来ます。

今回は画像をintentに持たせて遷移先でやり取りをしたかったのですが!!! FAILED BINDER TRANSACTION !!!というエラーに

ひっかかり見事に怒られてしまいました。

 

どうやらiphoneやandroidで撮影した写真や画像は加工せずに送ると重すぎるためにファイルとして扱うには不適切と見なされ

拒否されてしまうみたいです・・・。

 

少しまわりくどいですが、uriでintentに持たせbitmapに加工するという手段に落ち着きました。

とりあえず遷移先でuriを宣言していればどれだけ重い画像でも持ってこれるので同じような内容でハマっている

方は試してみてください。

 

最後にコードを掲示しておきます。

 

遷移前

<pre>Intent intent = new Intent(this, EditActivity.class);
intent.putExtra("imageUri", uri);
startActivity(intent);</pre>

 

遷移先

<pre>public void getBmp(){
    Intent intent = getIntent();
    Uri uri = intent.getParcelableExtra("imageUri");
    System.out.println(uri);
    try {
        Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
        imageView.setImageBitmap(bitmap);
    } catch (IOException e) {
        e.printStackTrace();
    }
}</pre>

 

遷移先で変数uriに“imageUri”キーを持つ値を持ってきています。

tryの中ではMediaStoreに問い合わせてuriをBitmapに変換しているところです。(ざっくりですみません)

 

asaba 著者:asaba

【cordova-react】長ったるいコードをfor文で解決させた話

前に書いた記事の後付けです。

selectタグを用いて生年月日をプルダウンする機能を実装したのですが、生年月日を全部手書きで書いたためか

エディタの機能やコーディングのスピードが著しく落ち、メンテナンスしにくいコードになってしまいました。

行を無駄に使っているのでスクロールするときもぎこちなくこの状態では効率が悪いと思い、とりあえず

生年月日をループさせてコードの行を減らすことに努めました。

生年月日のデータをreactで書くとこんな感じになります。悪い例 

 

<pre>    this.dayData = [
      {value: '', label: '▼日にち選択'},
      {value: 1, label: '1'},
      {value: 2, label: '2'},
      {value: 3, label: '3'},
      {value: 4, label: '4'},
      {value: 5, label: '5'},
                .
                .
                .</pre>

 

コンストラクタ内でthis.dayDataを初期化しています。

生年月日なので、19××~20××と幅広い西暦を設定しなければいけません。

当然これを直接書くと年齢の幅を考えると100行近くなってしまいますね・・・。

 

短いですがここでシェイプアップのためにrender()内で記述したコードを説明します。

 

//コンストラクタ内

  {value: '', label: '▼日にち選択'},


  //render内
    for(var dayNum = 1930; dayNum <=2030; dayNum++){
    this.dayData[dayNum] = {value: dayNum, label: dayNum};
    }

 

みてくれは通常のjavascriptで連想配列を新しく追加するときのような形式になっています。

これを書いたら後はmapで上のデータを基に新しく配列をつくれば完成です。

asaba 著者:asaba

【cordova-react】reactではclass属性は不適切

一般的にjavaScriptではcssをタグで定義するときはclassというオブジェクトを用いますが、reactでは”class“という属性は

他の予約語で使われている為、これをそのまま使うと他の属性を使ってくれませんかと警告をもらいます。

こんな感じです。↓

 

 

解決方法はシンプルで、classclassNameという名前に変えてあげれば万事解決。

[]javascript]

class="App-circle"

[/javascript]

 


className="App-circle"

エラーではないので重要性は少ないかもしれませんが、メンテナンスの時に煩わしいという方はJSXの仕様が

変わらないうちに直しておくことをお勧めします。

asaba 著者:asaba

【mysql】接続エラーの回数制限を超えてアクセスできなくなったときのメモ

アプリからブログを投稿するアプリで機能のプロトタイプを作っていた時に遭遇しました。

こちら

 


<span style="color: #ff0000; font-size: 14pt;">Host hogehoge.jp' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'</span>

複数回にわたる接続を確認したためブロックされました・・・初見ではなにが原因かさっぱり。

 

リファレンスを見る限り、予め許可する回数が決まっておりその上限を超えると、mysqldは送信元に問題があるとみなしホストがアクセ

スできないように遮断してしまうみたいです。

 

外部からの不正な攻撃を防ぐための対策なので、原因を一本に絞ることができなくて狼狽えましたがとりあえず

クッキーを全削除して再びアプリを使って投稿したところ正常に動きました。

 

正規ではないと思いますが、基本的にはまずクッキーを全部消してみることをお勧めします。

それがだめならブラウザを閉じてchromeを再起動をしてみてください。自分のアカウントを使っている場合はいったんログアウト

してから再度chromeを開いてみてください。

 

asaba 著者:asaba

【react】setectタグで生年月日フォームを作る

ハイブリッドアプリ必須の開発においてはhtml5の知識が必須になりますが、中でもinputタグには誰もがお世話になっていると

思います。そのinputタグのtype(属性)の一つであるdateについて語っていきます。

このdate属性なのですが、ブラウザやデバイスによって表示が変わる仕様になっており、デバイスに至ってはtext扱いに

なってしまい使えない場合があったりと中々癖が強いオブジェクトです。

iosでは、通常のdatepickerのように上下スクロールすることで数字を選べるようになっているのですが、android(特にエクスペリア)

ではカレンダーUIとして表示される仕様になっており、年配な方やせっかちな方にとってはなかなかに見にくい構成に

なっています・・・。

 

アプリの仕様上ユーザー登録画面で生年月日を出力したかったので、一番初めに目にするであろうページでいきなり

イライラさせてしまうことは開発者としては避けたいところ。

これではいけないと思い、date以外にも様々な属性を手探りで使ってみました。(ネタばれですが結果全部不採用w)

 

まず、type=”month”ですがこれは西暦と月はピッカーで表示されるのですが日にちは対象ではないので即対象外。

次にdateTime-localですが、こちらは西暦月日にち三点全部ピッカーとして表示されます。やったネ!と思ったのですが

よく見たら時刻も必ず入力しないといけないんじゃないですかー・・・。

hiddenで非表示にできないか頑張ったのですがダメでしたね、やっぱり入力しないといけないみたいです。

先ほども言った通り生年月日なので産まれた時間まではさすがに求めてないですよ・・・。

しかもIEやfirefoxでは対応していないという仕打ち。

そのほかにも試したのですが、全部撃沈しました。

 

結局selectで生年月日をずらーっと出すようなかたちに落ち着きそうです。

こちらをreactで実装するのは比較的簡単で、まずthis.stateにyear,month,dayをコンストラクタに設定して

mapでそれぞれ西暦月日にちの配列を作ってあげて表示をします。

ただ表示するだけならこれだけでできますが、2018/3/1のように生年月日が連結されたような疑似的にdateで出力される

ような見た目にしたかったのでこんな感じにしました。

<pre>    const year = this.state.year;
    const month = this.state.month;
    const day = this.state.day;
    const birthday = ( year + '/' + month + '/' + day );
</pre>

細かいことを気にしないならば連結するだけならこんな簡単にできてしまいます。

値もしっかりとれているのでandroidでカレンダー表示のUIは嫌だよーというかたは使ってみてくださいね。

 

本日はこんな感じで終了です。よいお年を!

asaba 著者:asaba

自宅のプロバイダを知る方法

自分の使っているプロバイダを変更したい、若しくは知っておきたい時に使えるtipsです。

 

確認の方法はすごく簡単で、自分の登録しているメールアドレスの@の後ろの部分を確認するだけ!です。

例えば、hogehoge@ocn.ne.jpだった場合は、プロバイダーはOCN

foofoo@ybb.ne.jpだった場合はプロバイダーはYahoo!BB

 

簡単に確認することができますね~。

 

すみません、最近はプログラミングと関係ない話題が続きますが、年明けにまたフレッシュなプログラミングの情報を

提供出来たらなと思っています。