Androidアプリ開発 画像選択 外部アプリと連携して画像を選択、取得する

2017年01月18日(編集2017年01月18日)
このエントリーをはてなブックマークに追加

Androidアプリ開発では、外部アプリと連携して画像を取得できます。
この記事は、外部アプリと連携して画像を選択、取得する方法を記載した記事です。

環境はAndroid 7.1 (API level 25) です。

環境

  • OS X El Capitan
  • android sdk 25
  • Oracle jdk version 1.8.0_72
  • Android Studio 2.2.3

難易度

中級者向け

サンプルコード

Android-Media-Demo

外部連携

Androidは、外部アプリと連携ができます。外部アプリとの連携には、Intentの機能を使います。よく利用される外部連携は、画像アプリのメディアデータの取得や、SNS接続です。

外部アプリ起動実装

サンプルを実装します。このサンプルアプリでは、以下の機能を実装します。

  • 「画像を選択する」ボタンをクリックして画像選択可能な外部アプリを立ちあげる。
  • 立ち上げた外部アプリで選択したデータを取得する。選択した場合は、ImageViewに画像を表示する。選択しなかった場合は、Toastで「画像が選択されませんでした」とメッセージを表示する。

まずActivityの画像選択ボタンに、画像選択可能な外部アプリを立ちあげる機能を実装します。

{project_folder}/ImageSelectionDemoActivity.java
public class ImageSelectionDemoActivity extends AppCompatActivity {

    public final static int REQUEST_CODE_CHOOSER = 101;

    public static final List<String> types = Collections
            .unmodifiableList(new LinkedList<String>() {
                {
                    add("image/jpeg");
                    add("image/jpg");
                    add("image/png");
                }
            });

    private ImageView selectedImage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_selection_demo);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        selectedImage = (ImageView) findViewById(R.id.selected_image);
        Button btnSelectImage = (Button) findViewById(R.id.btn_select_image);
        btnSelectImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startExternalAppSelectableImage();
            }
        });

    }

   +/**
     * Called when the '画像を選択する' button is clicked.
     */
    private void startExternalAppSelectableImage() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

        // Filter to only show results that can be "opened", such as a
        // file (as opposed to a list of contacts or timezones)
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            intent.putExtra(Intent.EXTRA_MIME_TYPES, types.toArray());
        }
        startActivityForResult(Intent.createChooser(intent, null), ImageSelectionDemoActivity.REQUEST_CODE_CHOOSER);
    }

}
        

■ 実装の解説

1. addCategory

Intent#addCategoryメソッドは、新しいカテゴリをintentに追加します。
カテゴリ CATEGORY_OPENABLE をインテントに追加すると、結果がフィルタリングされて、画像ファイルなどの開くことができるドキュメントのみが表示されます。

intent.addCategory(Intent.CATEGORY_OPENABLE);
        

2. setType

Intent#setTypeメソッドは、明示的なMIMEデータタイプを設定します。

intent.setType("image/*");
        

上記は画像 MIME データタイプのドキュメントのみを表示します。

3. createChooser

Intent#createChooserは、起動できる複数のアプリの中から、利用したいアプリを選択する画面を表示するメソッドです。

Intent.createChooser(intent, null)
        

第2引数にはタイトルを指定します。nullの場合、タイトルは非表示になります。

4. SDKバージョン分岐

Android4.4(KITKAT)からStorage Access Frameworkを使用しているので、intentのEXTRA_MIME_TYPESに複数のMIMEタイプを明示的に追加します。

intent.putExtra(Intent.EXTRA_MIME_TYPES, types.toArray());
        

ビルド

上記のコードの動作を確認してみましょう。

図1. 外部アプリ起動

画像選択が可能な外部アプリが立ちあがりました。

外部アプリデータ取得実装

立ち上げた外部アプリで選択したデータを、自分のアプリで取得します。

{project_folder}/ImageSelectionDemoActivity.java
public class ImageSelectionDemoActivity extends AppCompatActivity {

    public final static int REQUEST_CODE_CHOOSER = 101;

    public static final List<String> types = Collections
            .unmodifiableList(new LinkedList<String>() {
                {
                    add("image/jpeg");
                    add("image/jpg");
                    add("image/png");
                }
            });

    private ImageView selectedImage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_selection_demo);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        selectedImage = (ImageView) findViewById(R.id.selected_image);
        Button btnSelectImage = (Button) findViewById(R.id.btn_select_image);
        btnSelectImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startExternalAppSelectableImage();
            }
        });

    }

    /**
     * Called when the '画像を選択する' button is clicked.
     */
    private void startExternalAppSelectableImage() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

        // Filter to only show results that can be "opened", such as a
        // file (as opposed to a list of contacts or timezones)
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            intent.putExtra(Intent.EXTRA_MIME_TYPES, types.toArray());
        }
        startActivityForResult(Intent.createChooser(intent, null), ImageSelectionDemoActivity.REQUEST_CODE_CHOOSER);
    }

   +@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case (ImageSelectionDemoActivity.REQUEST_CODE_CHOOSER):
                if (resultCode != RESULT_OK) {
                    Toast.makeText(this, getString(R.string.image_unselected_message), Toast.LENGTH_LONG).show();
                    return;
                }
                Uri result = data.getData();
                selectedImage.setImageURI(result);
                break;
            default:
                break;
        }
    }

}
        

■ 実装の解説

1. onActivityResult

onActivityResultメソッドはstartActivityForResultの結果を受け取ります。onActivityResultメソッドは、リクエストコード, 結果コード, データintentを受け取ることができます。また、必ずOverridesする必要があります。

結果がOKでない(RESULT_OKでない)なら、エラーメッセージを表示します。

if (resultCode != RESULT_OK) {
    Toast.makeText(this, getString(R.string.image_unselected_message), Toast.LENGTH_LONG).show();
    return;
}
        

結果がOKの場合は、intentからuriを取得してimageViewにuriを設定します。

Uri result = data.getData();
selectedImage.setImageURI(result);
        

あらゆる画像に対応するには、取得したUriに応じたContentProviderの処理が必要です。しかし、説明が長くなるのでここでは割愛します。

最後に、AndroidManifest.xmlにREAD_EXTERNAL_STORAGEパーミッションを設定します。

{project_folder}/app/AndroidManifest.xml
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        

ビルド

上記のコードの動作を確認してみましょう。

図2. ギャラリーで取得した画像を表示

ギャラリーで取得した画像が表示されました。

パーミッション実装

アプリをAndroid6.0以降に対応させる場合は、Permissionの実装が必要です。
ギャラリーのアクセスには、READ_EXTERNAL_STORAGEのパーミッションが必要です。

実装します。

{project_folder}/ImageSelectionDemoActivity.java
    /**
     * Called when the '画像を選択する' button is clicked.
     */
    private void checkPermission() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            // READ_EXTERNAL_STORAGE permission has not been granted.
            requestExternalStoragePermission();
        } else {
            startExternalAppSelectableImage();
        }
    }
          

ActivityCompat.checkSelfPermissionメソッドは特定のパーミションが付与されているかどうかをチェックします。

許可されている場合はPackageManager.PERMISSION_GRANTEDが返却され、許可されていない場合はPERMISSION_DENIEDが返却されます。

許可がない場合は、許可を求める処理が必要です。なので、READ_EXTERNAL_STORAGE permissionをリクエストするrequestExternalStoragePermissionメソッドを実装します。

{project_folder}/ImageSelectionDemoActivity.java
    /**
     * Requests the READ_EXTERNAL_STORAGE permission.
     * the permission is requested directly.
     */
    private void requestExternalStoragePermission() {
        // Contact permissions have not been granted yet. Request them directly.
        ActivityCompat.requestPermissions(this, PERMISSION_READ_EXTERNAL_STORAGE, ImageSelectionDemoActivity.REQUEST_READ_EXTERNAL_STORAGE);
    }
          

上記のコードの動作を確認してみましょう。

図3. 権限をリクエスト

権限の許可を求めるダイアログが表示されます。
Permissionのリクエストの結果はonRequestPermissionsResultメソッドで受け取ります。

{project_folder}/ImageSelectionDemoActivity.java
    /**
     * Callback received when a permissions request has been completed.
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        switch (requestCode) {
            case (ImageSelectionDemoActivity.REQUEST_READ_EXTERNAL_STORAGE):
                if (verifyPermissions(grantResults)) {
                    startExternalAppSelectableImage();
                } else {
                    Toast.makeText(this, getString(R.string.permissions_not_granted), Toast.LENGTH_LONG).show();
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                break;
        }
    }

    /**
     * Check that all given permissions have been granted by verifying that each entry in the
     * given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
     */
    public boolean verifyPermissions(int[] grantResults) {
        // At least one result must be checked.
        if (grantResults.length < 1) {
            return false;
        }

        // Verify that each required permission has been granted, otherwise return false.
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }
          

onRequestPermissionsResultメソッドは、引数の配列で権限結果を受け取ります。
「許可しない」を押した場合は、PackageManager.PERMISSION_GRANTED以外が返却されます。

図4. 許可しないをクリック

「許可」を押した場合は、以下のようになります。

図5. 許可をクリック

外部アプリの選択が可能です。

結論

Androidアプリの外部アプリ連携実装は、初期の頃と比べるとかなり複雑になっています。
初めて実装する場合は、苦労すると思いますが、少しづつ必要な知識を身につけてアプリを作ってみてください。

関連記事

タグ検索で調べてみよう

Android7.1