ActivityResultContract を使って、外部の画像選択アプリを起動し、ユーザーに画像を選択させます。
Android 標準の画像選択アプリではなく Google Photos アプリを起動して画像選択させたい場合は以下の記事を参照してください。
複数の画像選択に対応したい場合は以下の記事を参照してください。
必要な権限
外部の画像選択アプリによって画像を選択し、その画像を読み取るためには READ_EXTERNAL_STORAGE 権限は不要 です。
画像選択アプリが端末に保存されている画像を読み取る権限を取得しており、ユーザーが選択した画像は Intent 発行元のアプリが読み取れるパスとして返却されるためです。
前提
AndroidX や AndroidX Fragment が導入されている前提です。
実装
class MyFragment: Fragment() {
private val getContentLauncher = registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri ->
if (uri != null) {
// 画像が選択された
// content://com.android.providers.media.documents/...
// のような Content Provider 形式の URI を受け取ります
} else {
// 画像選択がキャンセルされた
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val binding = /* ... */
binding.button.setOnClickListener {
getContentLauncher.launch("image/*")
}
}
// ...
解説
Fragment.registerForActivityResult()
を実行しておくことで、Fragment が Activity に Attach されたときに、Activity の ActivityResultRegistry に GetContent Contract が登録されます。ActivityResultRegistry に登録されると、ActivityContract が onActivityResult() を受け取って処理できるようになります。
Contract を登録すると、任意のタイミングで getContentLauncher.launch()
を実行することで、コンテンツ選択の Intent が発行されます。
GetContent Contract は以下の Intent を発行しています。
Intent(Intent.ACTION_GET_CONTENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType(input) // ここでは input = "image/*"
registerForActivityResult()
は内部で onActivityResult の RequestCode を連番として生成しています。 registerForActivityResult()
を実行する順番は常に同じである必要があります。条件分岐などを使わないように気をつけながら Activity や Fragment のフィールドとして初期化してしまうのが安全です。
実行結果
以下のように画像選択アプリが起動します。Android 標準の画像選択アプリであれば、Google Drive や Google Photos からの選択もできるようになっています。インストールされていれば Dropbox なども表示されます。端末にインストールされている多数のストレージアプリと連携できるため、基本的には GetContent Contract で Android 標準の画像選択アプリを呼び出すことをお勧めします。
Intent Chooser で画像選択とカメラアプリでの写真撮影を選べるようにする
ActivityResultContract で Intent Chooser に画像選択とカメラアプリ起動での写真撮影の両方を表示する方法は以下の記事を参照してください。