8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Android: ActivityResultContract を使って画像選択アプリで画像を複数選択する (EXTRA_ALLOW_MULTIPLE)

Last updated at Posted at 2021-08-24

ActivityResultContract を使って外部の画像選択アプリを起動し、ユーザーに複数の画像を選択させます。

単一の画像選択で構わない場合は以下の記事も参照してください。

Android 標準の画像選択アプリではなく、Google Photos を起動して複数画像選択をしたい場合は以下の記事を参照してください。

必要な権限

外部の画像選択アプリによって画像を選択し、その画像を読み取るためには READ_EXTERNAL_STORAGE 権限は不要 です。

画像選択アプリが端末に保存されている画像を読み取る権限を取得しており、ユーザーが選択した画像は Intent 発行元のアプリが読み取れるパスとして返却されるためです。

前提

AndroidX や AndroidX Fragment が導入されている前提です。

実装

複数画像選択に対応した GetContentsContract を実装します。

class GetContentsContract : ActivityResultContract<String, List<String>?>() {
    override fun createIntent(context: Context, input: String): Intent {
        return Intent(Intent.ACTION_GET_CONTENT)
            .addCategory(Intent.CATEGORY_OPENABLE)
            .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
            .setType(input)
    }

    override fun parseResult(resultCode: Int, intent: Intent?): List<String>? {
        return if (resultCode == Activity.RESULT_OK) {
            intent?.clipData?.let { clipData ->
                (0 until clipData.itemCount).mapNotNull {
                    clipData.getItemAt(it).uri?.toString()
                }
            } ?: intent?.data?.toString()?.let { listOf(it) }
        } else null
    }
}

GetContentsContract を使用して複数の画像選択を実行します。

class MyFragment: Fragment() {
    private val getContentsLauncher = registerForActivityResult(
        GetContentsContract()
    ) { uris ->
        if (uris != null) {
            // 画像が選択された
            // content://com.android.providers.media.documents/...
            // のような Content Provider 形式の URI を受け取ります
        } else {
            // 画像選択がキャンセルされた
        }
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = /* ... */
        binding.button.setOnClickListener {
            getContentsLauncher.launch("image/*")
        }
    }
// ...

解説

ACTION_GET_CONTENT は複数の画像選択に対応しています。

ACTION_GET_CONTENT に加えて、EXTRA_ALLOW_MULTIPLE に true を指定すると複数選択となります。

複数選択の結果は Intent.clipData に格納されています。 ClipData.getItemAt() で ClipData.itemCount まで取り出せます。 ClipData.Item の uri に選択したファイルのパスが含まれています。

画像選択アプリでユーザーが一つの画像を選択した場合は clipData ではなく intent.data にファイルパスが格納されていることがあります。intent.clipData から値を取得できなければ単一ファイル選択とみなし、 inetnet.data からファイルパス取得するようにしています。

実行結果

以下のように Android 標準の画像選択アプリが起動します。

画像選択アプリの見た目や動作は OS のバージョンや端末の種類によります。

以下の標準アプリでは複数画像選択できるようにしていても、画像をタップすると単一のファイルを選択して確定してしまうようです。

image.png

ユーザーが画像を長押しすると、以下のように複数選択 UI となります。

image.png

標準の画像選択アプリは、左側のドロワーメニューから Google Photos アプリも呼び出すことができます。

Google Photos アプリの選択画面は、この画面を開いた状態ですでに複数画像選択表示となっています。

image.png

注意事項

EXTRA_ALLOW_MULTIPLE による複数画像選択 Intent は簡単に実装できますが、以下の制限があります。

複数画像選択 Intent は便利ですがあまり融通が効かず、実際に活躍する場面は少ないかもしれません。

選択できるファイル数の上限を指定できない

Android 標準の画像選択アプリでも Google Photos でも、選択できる画像の上限数を指定することはできません。ユーザーが選択した分がすべて Intent に返されます。

画像選択 Intent の呼び出し側のアプリで、Intent の結果からファイル数を削るのか、無限のファイル選択に対応できるようにするのかを検討し、対処できるようにしておく必要があります。

すでに選択済みのファイル一覧を渡して、選択済みの状態から開始することはできない

Android 標準の画像選択アプリでも Google Photos でも、画像選択を開始するときに任意の画像を選択済みのような表示にすることはできません。Intent を発行するたびにすべて未選択の状態から開始します。特定の画像を非表示とするような機能もありません。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?