ActivityResultContract是什么
很简单的一句话,ActivityResultContract
是用来在大部分场景中对startActivityForResult
和onActivityResult
进行替代的官方api
。
ActivityResultContract
提供了一种类型安全的获取返回值的方式,比如拍照的api会返回泛型指定的bitmap。这避免了我们手动处理onActivityResult回调导致的各种问题。
当然了我认为ActivityResultContract
最好的地方就是省心,尤其对于系统预置的集中ActivityResultContract
,只需要两步模板代码即可实现功能。
自定义一个ActivityResultContract
要实现自定义行为需要先自定义一个ActivityResultContract
类,定义如下:
- 集成ActivityResultContract类
ActivityResultContract
类中有两个泛型,第一个泛型是I,第二个泛型是O,I表示输入也就是我们启动activity需要putExtra的内容,O表述输入即onActivityResult返回的数据
ActivityResultContract
有两个方法
createIntent
表示创建启动activity
的Intent
,其中方法的第二个参数可用于传给待启动activity
的参数parseResult
表示对返回数据的解析,方法的返回值就是registerForActivityResult
中回调的数据
class CustomResultContracts : ActivityResultContract<Int, String>() { override fun createIntent(context: Context, input: Int?): Intent { return Intent(context, DestinishActivity::class.java).putExtra("input",input) } override fun parseResult(resultCode: Int, intent: Intent?): String { return intent?.getStringExtra("data") ?: "未返回数据" } } 复制代码
- 注册监听
private val customContract = registerForActivityResult(CustomResultContracts()){ getData(14).content="自定义Contracts返回数据:$it" getData(14).notifyDataSetChange() } 复制代码
- 启动activity
customContract.launch(1) 复制代码
官方提供的预置ActivityResultContract
StartActivityForResult启动activity返回结果
本例调用方法后会启动一个activity,新的activity点击返回数据将数据返回到列表中展示
- 代码
注册代码
//注册结果监听 registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == 3) { showResult(it.data?.getStringExtra("data")) } } 复制代码
启动代码
//启动activity,参数传intent startForResult.launch(Intent(this@MainActivity, DestinishActivity::class.java)) 复制代码
- 效果
TakePicturePreview跳转拍照页面
跳转到拍照页面,会返回一个Bitmap,拍摄的图片不会被持久化到磁盘中
- 代码
registerForActivityResult(ActivityResultContracts.TakePicturePreview()) { getData(1).let { it.notifyDataSetChange() } } 复制代码
模拟器录制不变,暂无效果图
TakePicture拍摄预览图片
TakePicture
方法会跳转到系统相机拍摄一张照片,返回boolean值,图片会被存储到我们指定的目录中
- 代码
private val takePreviewPic = registerForActivityResult(ActivityResultContracts.TakePicture()) { logEE("搞预览图片成功") } 复制代码
CaptureVideo拍摄视频
拍摄代码,需要说明的是拍摄视频完成后会需要等待较长时间,等待手机处理完视频的存储
private val captureVideo = registerForActivityResult(ActivityResultContracts.CaptureVideo()) { logEE("拍摄视频成功:$it") } 复制代码
模拟器录制不变,暂无效果图
RequestPermission请求权限
非常简洁的方式实现权限申请
- 申请权限代码
cameraPermission.launch(Manifest.permission.CAMERA) 复制代码
- 注册申请权限监听
private val cameraPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { getData(3).apply { content = "请求相机权限结果$it" notifyDataSetChange() } } 复制代码
- 效果
RequestMultiplePermissions请求多个权限
- 调用申请多个权限代码
mutlePermission.launch( arrayOf( Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE ) ) 复制代码
- 注册申请多个权限的代码
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { var result = "" it.forEach { gaint -> result += "获取${gaint.key} 权限 ${if (gaint.value) "成功" else "失败"}" } getData(4).content = result getData(4).notifyDataSetChange() } 复制代码
- 效果
PickContact获取联系人
- 请求打开联系人选择页面
pickContact.launch(null)//参数传空 复制代码
- 监听获取联系人结果
private val pickContact = registerForActivityResult(ActivityResultContracts.PickContact()) { logEE(it.toString()) getData(8).apply { content = it.toString() notifyDataSetChange() } } 复制代码
- 实现效果
GetContent打开文件浏览器
实现使用文件浏览器选择图片功能
- 打开文件浏览器
getContent.launch("image/*") 复制代码
- 处理返回结果
private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { logEE(it.toString()) } 复制代码
- 实现效果
因为模拟器没有图片可选,所以没有内容展示
2021/08/06 更新新内容如下
Fragment中使用ActivityForResultContract
Fragment
中的使用和activity中是一样的,直接放参考代码吧
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.findViewById<Button>(R.id.btn).setOnClickListener { request.launch(Intent(activity, DestinishActivity::class.java))//启动新activity } } /** * 注册结果监听 */ val request = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { logEE(it.data?.getStringExtra("data")!!) } 复制代码
最终效果和activity是一致的
两个Activity中不同Fragment如何通信
2021/12/11 更新新内容如下需要引入如下库
implementation 'androidx.fragment:fragment-ktx:1.3.0' 复制代码
我们建立如下图中的四个类
Comm1Activity中展示ActivityCommFragment1,Comm2Activity中展示ActivityCommFragment2
我们要做这样一个事情,在ActivityCommFragment1
中打开 Comm2Activity
并且把数据传到ActivityCommFragment2
中,同时在ActivityCommFragment2
中返回数据在 ActivityCommFragment1
中收到返回的数据。
代码如下:
- 在ActivityCommFragment1中编写接收返回数据代码
private val launcher:ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result -> if (result.resultCode == Activity.RESULT_OK){ binding.tvSend.text = result.data?.getIntExtra("nums",-1).toString() } } 复制代码
- ActivityCommFragment1中启动Comm2Activity
launcher.launch(Intent(requireContext(),Comm2Activity::class.java)) 复制代码
- 在ActivityCommFragment2中返回数据的方法
binding.btnReturndata.setOnClickListener { requireActivity().setResult(Activity.RESULT_OK,Intent().apply { putExtra("nums",123) }) requireActivity().finish() } 复制代码