2015年2月22日日曜日

Android4.0以降&4.4以降で、カメラとギャラリーから選択を1つのChooserへまとめて、パスを取得する

SYSTEM_KDです。
(タイトル長すぎですね。。)

Androidアプリで、「カメラ」「ギャラリー」から画像を取得する際に、1つのChooserへまとめてユーザへ選択させる方法です。

先日ググってサクッと実装しようとしたのですが、いざ実装してみると、OSのバージョンで若干処理を分けたりしないといけかったり、上手くいかなかったりしたりしたので、まとめ。

やりたいこと

アプリで画像を扱うために、「画像」&「画像のパス」を取得したい。

ようは、これ。

one_chooser

実現方法

ググったところ、カメラを呼び出すIntentと、ギャラリーを呼び出すIntentを作成して、Chooserへ設定してやれば良いらしい。

ハマったポイント

カメラを呼び出すIntentを用意する際に、撮影情報保存用のファイルをgetContentResolver経由で用意すると、キャンセルした際に空のファイルが消えてくれない。(消えないというか、ギャラリーを表示したのに空ファイルができてしまうような気がする)

ギャラリーから選択する際に、OSのバージョンによってパスの取得方法が違う。

実装

やることは、「実現方法」の内容通りですが、ちょこちょこ小細工してます。

まずは、画像(写真)の取得・設定から

private Uri photoUri;
private static final int REQUEST_CHOOSER = 1000;
private static final int KITKAT_API_LEVEL = 19;

private void showCameraGallery() {

// カメラ起動のIntent作成 */
File pathExternalPublicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
String filename = System.currentTimeMillis() + ".jpg";
File capturedFile = new File(pathExternalPublicDir, filename);
photoUri = Uri.fromFile(capturedFile);
Intent intentPhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intentPhoto.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);

// ギャラリー用のIntent作成 */
Intent intentGallery;
if (Build.VERSION.SDK_INT < KITKAT_API_LEVEL) {
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 chooserIntent = Intent.createChooser(intentGallery, "Pickup");
// カメラ起動用のIntent を追加
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {intentPhoto});

startActivityForResult(chooserIntent, REQUEST_CHOOSER);
}

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

if(requestCode == REQUEST_CHOOSER) {

if(resultCode != RESULT_OK) {
// キャンセル時
return ;
}

Uri resultUri = (data != null ? data.getData() : photoUri);

if(resultUri == null) {
// 取得失敗
return;
}

// ギャラリーへスキャンを促す
MediaScannerConnection.scanFile(
this,
new String[]{resultUri.getPath()},
new String[]{"image/jpeg"},
null
);

// 画像を設定
ImageView imageView = (ImageView)findViewById(R.id.imageView);
imageView.setImageURI(resultUri);
}
}

Chooserの表示から画像の取得・表示までの抜粋ですが、こんな感じになるかと思います。

ハマったポイントで上げた通り、カメラ画像を保存するファイルを「getContentResolver」経由で作成すると、空ファイルが残ってしまったので、「File」で用意して、「onActivityResult」で「ギャラリーへスキャンを促す」ようにしてます。

ギャラリーから選択する部分は、Kitkat以降とそれより前で、Actionを「Intent.ACTION_GET_CONTENT」「Intent.ACTION_OPEN_DOCUMENT」とわけてます。

画像のパスを取得する

続いて、画像のパスを取得する方法です。

public static String getImagePath(ContentResolver contentResolver, Uri uri) {

String retPath = "";

if(uri == null) return retPath;

File file = new File(uri.getPath());
if(file != null && file.exists()) {
return uri.getPath();
}

if(Build.VERSION.SDK_INT < 19) {

String[] columns = { MediaStore.Images.Media.DATA };
Cursor cursor = contentResolver.query(uri, columns, null, null, null);
cursor.moveToFirst();
retPath = cursor.getString(0);
cursor.close();

} else {

String id = DocumentsContract.getDocumentId(uri);
String selection = "_id=?";
String[] selectionArgs = new String[]{id.split(":")[1]};

Cursor cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.MediaColumns.DATA},
selection, selectionArgs, null);

if (cursor.moveToFirst()) {
retPath = cursor.getString(0);
}
cursor.close();
}

return retPath;
}

ContentResolver」と「Uri」を渡せば、パスを返却するようにしてます。

写真の場合は、「UriのgetPath()」でそのままとれるので、そのパスを利用。

ギャラリーから取得した画像の場合は、OSのバージョンによって呼び出すものを変えているので、取得方法をそれに沿って違ってます。


まとめ


一応、いまのところはこれで画像とパスはとれてそうなので、大丈夫かと思います。

今回、とってきた画像をそのままImageViewへ設定しており、写真の向きに合わせて回転するとかは含めてません。

1 件のコメント:

  1. カメラを呼び出すIntentを用意する際に、撮影情報保存用のファイルをgetContentResolver経由で用意すると、キャンセルした際に空のファイルが消えてくれない。(消えないというか、ギャラリーを表示したのに空ファイルができてしまうような気 ... 1カメラ.blogspot.com

    返信削除