1.前言
这两天使用startActivityForResult()这个API的时候,突然发现它被废弃了。
通过源代码的注解我发现官方已经给了新的解决方案。
2.原有方案的缺点
我们在使用onActivityResult的时候,往往会有多种不同返回的情况,这种时候我们就需要设定不同的返回码,然后再onActivityResult中进行判断,过多的返回码会降低代码的可读性,还提升了耦合度。
3.ActivityResult API简单使用
首先要导入需要的依赖。
implementation 'androidx.activity:activity:1.3.0'
implementation 'androidx.fragment:fragment:1.3.4'
其次需要创建一个类来集成ActivityResultContract<I,O>,这里的I是intent跳转时携带的数据类型,O是intent返回时的携带的数据类型。还需要实现createIntent和parseResult这两个方法,这两个方法一个是用来创建intent的,一个是用来解析intent返回的数据。
class MyActivityResultContract : ActivityResultContract<String, String>() {
override fun createIntent(context: Context, input: String?): Intent {
return Intent(context,SecondActivity::class.java).apply {
putExtra("name",input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
val data = intent?.getStringExtra("result")
return if (resultCode == Activity.RESULT_OK && data != null) data else null
}
}
创建完成之后我们就可以在Activity中使用它。
private val myActivityResultContract = registerForActivityResult(MyActivityResultContract()){ result ->
Toast.makeText(applicationContext, result, Toast.LENGTH_SHORT).show()
textView.text = result
}
接下来只要在需要跳转的地方调用ActivityResultContract.launch方法即可。写完这一整个步骤,我们可以看到,耦合度虽然降低了,但是整件事情变得越来越麻烦了,还要新建一个类,感觉这样还不如onActivityResult API了。还好官方已经给出了一些定义好的ActivityResulyContract,这些已经能满足我们大部分需求。
4.常用Contract的介绍
Contract | 作用 |
---|---|
StartActivityForResult | 通用的Contract,I-->Intent,O->ActivityResult |
RequestMultiplePermissions | 多权限请求,I-->Array,O-->Map<String,Boolean>,输入的是要请求的权限名,输出权限名和是否成功组成的Map |
TakePicturePreview | 调用MediaStore.ACTION_IMAGE_CAPTURE来拍照,返回值为Bitmap |
TakePicture | 调用MediaStore.ACTION_IMAGE_CAPTURE来拍照,保存到给定Uri地址,返回一个Boolean |
TakeVideo | 调用MediaStore.ACTION_VIDEO_CAPTURE拍摄视频,保存给指定Uri地址,返回一张缩略图 |
PickContact | 从通讯录APP选中联系人,返回一个uri |
GetContent | 传入特定的mime type打开对应的文件管理器 |
CreateDocument | 提示用户创建一个文件,I为文件名 |
CreateMultipleDocuments | 提示用户选择多个文档,I为文件类型 |
OpenDocumentTree | 提示用户选择一个目录,并返回用户选择的Uri |
Demo
package com.zjl.activityresultdemo
import android.Manifest
import android.content.ContentValues
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private val myActivityResultContract =
registerForActivityResult(MyActivityResultContract()) { result ->
Toast.makeText(applicationContext, result, Toast.LENGTH_SHORT).show()
}
private val mPermissionResultContract =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
Toast.makeText(applicationContext, "${it.keys}...${it.values}", Toast.LENGTH_SHORT)
.show()
}
private val mTakePictureContract =
registerForActivityResult(ActivityResultContracts.TakePicture()) {
Toast.makeText(applicationContext, "$it", Toast.LENGTH_SHORT).show()
}
private val mTakePicturePreviewContract =
registerForActivityResult(ActivityResultContracts.TakePicturePreview()) {
iv.setImageBitmap(it)
}
private val mTakeVideoContract =
registerForActivityResult(ActivityResultContracts.TakeVideo()) {
iv.setImageBitmap(it)
}
private val mPicContact =
registerForActivityResult(ActivityResultContracts.PickContact()) {
Log.e("Testtt",it.toString())
}
private val mGetContact =
registerForActivityResult(ActivityResultContracts.GetContent()) {
Log.e("Testtt",it.toString())
}
private val mCreateDoc =
registerForActivityResult(ActivityResultContracts.CreateDocument()) {
}
private val mOpenMultipleDoc =
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) {
}
private val mOpenDocTree =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) {
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_permission.setOnClickListener {
//myActivityResultContract.launch("test")
mPermissionResultContract.launch(
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
)
}
btn_take_pic.setOnClickListener {
contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
ContentValues()
).let {
mTakePictureContract.launch(it)
}
}
btn_take_pic_preview.setOnClickListener {
mTakePicturePreviewContract.launch(null)
}
btn_take_video.setOnClickListener {
contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, ContentValues())
.let {
mTakeVideoContract.launch(it)
}
}
btn_pic_contact.setOnClickListener {
mPicContact.launch(null)
}
btn_get_contact.setOnClickListener {
mGetContact.launch("video/*") //mime type
}
btn_create_doc.setOnClickListener {
mCreateDoc.launch("image/*")
}
btn_open_multi_Doc.setOnClickListener {
mOpenMultipleDoc.launch(arrayOf("image/*","video/*"))
}
btn_open_doc_tree.setOnClickListener {
mOpenDocTree.launch(MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
}
}
}
5.在非Activity/Fragment中接收结果
我们在Activity和Fragment中可以之间接收到返回结果是因为它们的基类都实现了ActivityResultCaller。但有时候我们需要在非Activity非Fragment的组件中接收返回的结果,这时候我们就可以使用ActivityResultRegistry来实现。
Demo:
class SecondActivity : AppCompatActivity() {
private lateinit var observer: MyLifeCycleObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
observer = MyLifeCycleObserver(activityResultRegistry)
lifecycle.addObserver(observer)
btn2.setOnClickListener { observer.selectImage() }
}
}
class MyLifeCycleObserver(private val registry: ActivityResultRegistry) : LifecycleObserver{
private lateinit var getContent: ActivityResultLauncher<String>
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate(owner: LifecycleOwner) {
getContent = registry.register("key",owner,ActivityResultContracts.GetContent()) {
Log.e("TTTEE",it.toString())
}
}
fun selectImage() {
getContent.launch("image/*")
}
}
参考资料: