【Kotlin/Android Studio】registerForActivityResultの使い方!launcherとは?
この記事からわかること
- Android Studio/KotlinのregisterForActivityResultの使い方
- 別のActivityを起動させるには?
- Activityの結果を取得する方法
- StartActivityForResultの挙動
- ActivityResultContractの設定値
- RequestPermission/RequestMultiplePermissionsを使用したパーミッションダイアログの結果取得
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Narwhal Feature Drop
- Kotlin:2.1.10
- Material3
- AGP:8.9.2
- Gradle:8.11.1
- Mac M1:Sequoia 15.6.1
Activity Result API
Androidアプリではある画面から別の画面(Activity)を起動させる仕組みとして「Activity Result API」が用意されています。昔はstartActivityForResultとonActivityResultを使用してActivityの起動と結果の受け取りを行なっていましたが、これらは非推奨になり新しく「Activity Result API」が定義されました。
// 呼び出し側
val intent = Intent(this, NextActivity::class.java)
startActivityForResult(intent, REQUEST_CODE)
// 結果を受け取る
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val result = data?.getStringExtra("result")
}
}
「Activity Result API」の1つがregisterForActivityResultです。
registerForActivityResult
abstract @NonNull ActivityResultLauncher<I> <I, O> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback
)
registerForActivityResultは別のアクティビティを起動し結果を取得することができるメソッドです。引数としてActivityResultContract型を受け取りActivityResultLauncher型を返します。
private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// 結果に対する処理
}
※ランチャーとは機能を保持し、あとは簡単な操作のみで起動させるだけの機能のことです。
ActivityResultContract型
公式リファレンス:ActivityResultContract
ActivityResultContractには使用頻度の高いContract(契約)があらかじめ複数定義されています。
| ActivityResultContract | 概要 |
|---|---|
| StartActivityForResult | 任意のActivityを起動して結果を取得 |
| RequestPermission | 単体のパーミッション許可ダイアログを表示して結果を取得 |
| RequestMultiplePermissions | 複数個のパーミッション許可ダイアログを表示して結果を取得 |
| GetContent / GetMultipleContents | ギャラリーなどからURI経由で取得 |
| TakePicture | カメラで写真を撮ってURI経由で取得 |
ActivityResultCallback
registerForActivityResultの2つ目の引数ActivityResultCallback(コールバック)で実際の結果を受け取ることが可能になっています。
Jetpack Composeでの実装:rememberLauncherForActivityResult
Jetpack Composeで実装している場合はrememberLauncherForActivityResultを使用します。基本的な使い方はあまり変わらず引数にActivityResultContractsを指定しManagedActivityResultLauncher型で結果の処理を含んだランチャーを取得することができます。
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// 結果の処理
}
}
別のActivityからデータを受け取る
今回はSecondActivityから入力されたデータをMainActivityで表示させてみます。1アプリ1Activityが基本なので設計上破綻しているかもしれませんが動作確認だけしてみます。
MainActivity側でregisterForActivityResultメソッドを呼び出します。1つ目の引数にはActivityResultContracts.StartActivityForResultを渡します。Activityから返ってくる値はコールバックで受け取れるので変数resultに格納し、resultCodeプロパティから識別コードを、dataプロパティからIntentを参照することができます。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button: Button = findViewById(R.id.done_button)
button.setOnClickListener {
// SecondActivityに送信する用のデータを準備する
val intent = Intent(applicationContext, SecondActivity::class.java)
// SecondActivityを起動
launcher.launch(intent)
}
}
// SecondActivityを起動する機能と結果を処理する機能をもったランチャー(ActivityResultLauncher型)
private val launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// Resultコードをチェック
if (result.resultCode == Activity.RESULT_OK) {
// インテントを取得
val intent = result.data
// 中身を取り出す
val name = intent?.getStringExtra("name")
// 表示
Toast.makeText(this, name, Toast.LENGTH_SHORT)
.show()
}
}
}
Intentの使い方は以下の記事を参考にしてください。
SecondActivity側はsetResultメソッドを使用して結果として返すデータを設定します。1つ目の引数にはActivity.RESULT_OKやActivity.RESULT_CANCELEDなどの識別コードを渡し、2つ目にはIntentを渡します。
public final void setResult(int resultCode, Intent data) {
throw new RuntimeException("Stub!");
}
最後にActivityを終了するfinishメソッドを呼び出して完了です。
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val name: EditText = findViewById(R.id.name_edit)
val backBtn:Button = findViewById(R.id.back_button)
backBtn.setOnClickListener {
// インテントに値をセット
intent.putExtra("name", name.text.toString())
// 結果として返すデータをセット
setResult(Activity.RESULT_OK, intent)
// アクティビティを終了
finish()
}
}
}
これで以下のような機能を実装することができました。
RequestPermission:パーミッションダイアログを表示する
おすすめ記事:【Kotlin/Android Studio】パーミッション(権限)許可のリクエスト方法!requestPermissions
registerForActivityResultの引数にRequestPermissionを渡すことで簡単にパーミッション許可申請ダイアログを表示することができます。ダイアログを出す際は忘れずにマニフェストファイルの追加をしといてください。
<uses-permission android:name="android.permission.CAMERA" />
実装は以下のとおりです。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 許可ダイアログを表示
launcher.launch(Manifest.permission.CAMERA)
}
private val launcher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
// ダイアログの結果で処理を分岐
if (result) {
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT)
.show()
} else {
Toast.makeText(this, "否認されました", Toast.LENGTH_SHORT)
.show()
}
}
}
RequestMultiplePermissions:複数のパーミッションダイアログを表示する
複数のパーミッションダイアログを表示するにはRequestMultiplePermissionsを指定します。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
launcher.launch(
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
}
private val launcher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
it
val camera = it[Manifest.permission.CAMERA] ?: false
val location = it[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false
if (camera && location) {
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT)
.show()
} else {
Toast.makeText(this, "否認されました", Toast.LENGTH_SHORT)
.show()
}
}
}
GetContent:ギャラリーなどからファイルを取得
ギャラリーなどから画像を取得するにはGetContentを使用します。選択した画像を直接取得できるわけではなく取得できるのはURI(Uniform Resource Identifier)になります。URI(=参照)を取得するのでcontentResolverを介して実際のデータを取得する処理を実装する必要があります。
@Composable
fun ImagePickerSample(viewModel: MyViewModel = viewModel()) {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent()
) { uri: Uri? ->
uri?.let {
val source = ImageDecoder.createSource(context.contentResolver, it)
val bitmap = ImageDecoder.decodeBitmap(source)
// ViewModelなどに渡す
viewModel.onImageSelected(bitmap)
}
}
// ボタンでギャラリーを開く
Button(onClick = { launcher.launch("image/*") }) {
Text("画像を選択")
}
}
また選択できるファイル形式はMIMEタイプで指定することが可能です。
- "image/*" → 画像全般
- "video/*" → 動画
- "application/pdf" → PDF
- "*/*" → すべて
GetMultipleContents:ギャラリーなどからファイルを複数取得
複数取得したい場合はGetMultipleContentsを使用することができます。
val multiLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetMultipleContents()
) { uris: List<Uri> ->
uris.forEach { uri ->
// URIごとに処理
}
}
multiLauncher.launch("image/*")
エミュレーターのギャラリーに画像を追加する
エミュレーターでギャラリーから画像を選択する動作確認を行いたい場合はエミューレータのギャラリーに画像を入れ込む必要があります。他に方法があるかもしれないですが以下の方法でギャラリーに画像を追加することができました。
- エミュレーター起動
- エミュレーターに入れ込みたい画像をドラッグ&ドロップ
- エミュレーター内の「Files」アプリを起動(ダウンロードに先ほどの画像があるはず)
- 画像を選択し左下の「Share」をクリック
- これでギャラリーに画像が追加されました
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。






