1. 老的实现方式
日常开发中,实现页面传值,通常通过startActivityForResult和onActivityResult配合,通过判断requestCode处理不同的业务场景
startActivityForResult(intent, requestCode)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK && requestCode == 100) {
// 处理页面返回的参数
}
}
2. 新Activity Result API
private val startForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
//页面返回值
val data = result.data
}
}
// 打开页面
startForResult.launch(Intent(this, ActivityB::class.java))
上面的实现是不是简单明了,直接注册回调实现业务逻辑,有多个传值的业务就定义多个回调协定(ActivityResultContract),可以很好的单独处理,不用在onActivityResult里面通过requestCode判断,非常方便的实现业务解耦。
3.如何使用
3.1 添加依赖,Activity Result API是在Androidx Activity和Fragment中引入的
implementation "androidx.activity:activity-ktx:1.2.2"
implementation "androidx.fragment:fragment-ktx:1.3.2"
3.2 注册协定,获取ActivityResultLauncher
private val selectData =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
}
}
3.3 构造Intent传递参数,启动页面
btnA.setOnClickListener {
val intent = Intent(this, ActivityB::class.java)
selectData.launch(intent)
}
4. 其它的协定
上面使用的ActivityResultContracts.StartActivityForResult是一个通用的协定,可以原封不动的通过Intent启动页面,ActivityResult接收返回值,系统还定义了好多通用的协定方便使用,下面一一简单介绍
- StartActivityForResult 这是一个通用协定,它可接受任何
Intent
作为输入内容并返回ActivityResult
,让您能够在回调中提取resultCode
和Intent
- RequestMultiplePermissions 用于请求一组权限
- RequestPermission 用于请求一个权限
- TakePicturePreview 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回
Bitmap
,可以继承它重写createIntent方法传递额外的参数 - TakePicture 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回
Uri
- TakeVideo 调用MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频,返回
Uri
- PickContact 从通讯录APP获取联系人
- GetContent 通过Intent.ACTION_GET_CONTENT选择一条内容,默认添加了Intent.CATEGORY_OPENABLE接收流内容,返回
Uri
- GetMultipleContents 同上,选择多条内容,需要api 18以上
- OpenDocument 选择一个文档
- OpenMultipleDocuments 选择一个、多个文档
- OpenDocumentTree 打开文档树选择文档
- CreateDocument 提示用户选择一个路径,创建新文档
大部分系统间的交互都已经定义好了,我们可以直接使用,比如GetContent,如下:
private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
// Handle the Intent
}
// 选择图片
getContent.launch("image/*")
// 选择视频
getContent.launch("video/*")
4.自定义协定
除了上面的预定义协定,我们还可以根据实际的业务定义自己的协定,当然实现也很简单,只需要继承ActivityResultContract<I, O>
抽象类即可,ActivityResultContract类有两个抽象方法,
//创建Intent,在Activity#startActivityForResult中使用I input是定义的范型
public abstract Intent createIntent(Context context, I input)
//解析Activity#onActivityResult中的返回结果,返回范型O
public abstract O parseResult(int resultCode, Intent intent)
下面实现一个例子
// 定义协定
class MyActivityResultContract : ActivityResultContract<Int, String>() {
override fun createIntent(context: Context, input: Int?): Intent {
return Intent(context, ActivityB::class.java).apply {
putExtra("projectId", input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
if (intent == null || !intent.hasExtra("projectName")) {
return null
}
return intent.getStringExtra("projectName").orEmpty()
}
}
//注册协定
private val getProject = registerForActivityResult(MyActivityResultContract()) {
//it 是返回值
}
//启动页面
getProject.launch(23)
5.非Activity、Fragment页面接收协定结果
一般的onActivityResult只能在Activity类处理返回结果,处理业务逻辑,增加了代码的耦合性,同时有很多场景希望在自己的类里面接收结果,处理逻辑,比如在Adapter里面响应点击事件,处理返回结果,下面介绍如何在业务类实现打开新页面,处理返回结果。
页面的启动管理是通过ActivityResultRegistry实现的,Activity里面已经定义好了ActivityResultRegistry方便我们使用,在自己的类里面只需要传递过来就可以处理跳转逻辑了。
class MyLifecycleObserver(private val registry : ActivityResultRegistry)
: DefaultLifecycleObserver {
lateinit var getContent : ActivityResultLauncher<String>
override fun onCreate(owner: LifecycleOwner) {
getContent = registry.register("key", owner, GetContent()) { uri ->
// Handle the returned Uri
}
}
fun selectImage() {
getContent.launch("image/*")
}
}
class MyFragment : Fragment() {
lateinit var observer : MyLifecycleObserver
override fun onCreate(savedInstanceState: Bundle?) {
// ...
observer = MyLifecycleObserver(requireActivity().activityResultRegistry)
lifecycle.addObserver(observer)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val selectButton = view.findViewById<Button>(R.id.select_button)
selectButton.setOnClickListener {
// Open the activity to select an image
observer.selectImage()
}
}
}
使用 ActivityResultRegistry
API 时,强烈建议您使用可接受 LifecycleOwner
作为参数的 API,因为 LifecycleOwner
会在 Lifecycle
被销毁时自动移除已注册的启动器。不过,如果 LifecycleOwner
不存在,每个 ActivityResultLauncher
类都允许您手动调用 unregister()
作为替代。