言いたいことはそれだけか

KotlinとかAndroidとかが好きです。調べたことをメモします。٩( 'ω' )و

Intentを使って複数枚画像を取得するときのメモ

Intentを使ってPhotoとかいい感じの(=ユーザが選択した)アプリから画像を選択したいときに、1枚なのか複数枚なのかで色々違うのでメモ。

複数枚画像を選択する場合

選択させるアプリを起動するとき

ポイントは Intent.EXTRA_ALLOW_MULTIPLE のextraをtrueに設定すること。
色々ググったときにactionが Intent.ACTION_GET_CONTENT でいけると書いていたけど、これだと複数画像選択できず一枚選択した時点で元のアプリに戻ってしまう挙動になった。(OS 7.1.2/Nexus 6P実機環境)
下記コードのように Intent.ACTION_PICK だと動く。

val intent = Intent()
intent.type = "image/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.action = Intent.ACTION_PICK
this.startActivityForResult(Intent.createChooser(intent, "Choose Photo"),
                                        CHOOSE_PHOTO_REQUEST_CODE)

選択した画像の情報を取り出すとき

Activity#onActivityResult() で受け取るintentの clipDatauriの情報が入っているのでこれを使う。 下記のサンプルコードは選択した画像のuriの情報を取り出してlistViewに表示するコードの一部。 getItemAt() で各要素にアクセスできる。

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == CHOOSE_PHOTO_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            val itemCount = data?.clipData?.itemCount ?: 0
            val uriList = mutableListOf<Uri>()
            for (i in 0..itemCount - 1) {
                val uri = data?.clipData?.getItemAt(i)?.uri
                uri?.let { uriList.add(it) }
            }
            this.adapter.data = uriList
            this.adapter.notifyDataSetChanged()
        }
    }

余談だけど、 clipData が保持しているuriの情報を持っているArrayListがpublicではない、かつ直接アクセスするためのAPIが公開されていないのでこんな汚い感じでmutableListだったりfor文だったりを使わないといけない感じになった(´;ω;`)

↓ClipDataのコード
Cross Reference: /frameworks/base/core/java/android/content/ClipData.java

153 public class ClipData implements Parcelable {

167     final ArrayList<Item> mItems; // publicじゃない

819     /**
820      * Return a single item inside of the clip data.  The index can range
821      * from 0 to {@link #getItemCount()}-1.
822      */
823     public Item getItemAt(int index) {
824         return mItems.get(index); // 指定したindexの要素しか取れない
825     }

もしimmutableなmItemsがgetできたり別のiterableな何かが取得できるAPIが生えてたら、こんな感じでもっと綺麗にかけるのに…

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == CHOOSE_PHOTO_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            // もしmItemsに外部からアクセスできたらこんな感じで1行で済ませられそう
            this.adapter.data = data?.clipData?.items.map { it.uri } ?: emptyList()
            this.adapter.notifyDataSetChanged()
        }
    }

拡張関数とか自分で書いてもいいけど、そんなに利用頻度高くなさそう、ということで諦めてしまいそう。

1枚だけ画像を選択する場合

選択させるアプリを起動するとき

この場合のactionは Intent.ACTION_GET_CONTENT でいけた。

            val intent = Intent()
            intent.type = "image/*"
            intent.action = Intent.ACTION_GET_CONTENT
            this.startActivityForResult(Intent.createChooser(intent, "Choose Photo"),
                                        CHOOSE_PHOTO_REQUEST_CODE)

選択した画像の情報を取り出すとき

取り出すときは複数枚の場合と同じく、Activity#onActivityResult() で受け取るintentの clipDatauriの情報が入っているので同様に扱うことができる。