registerForActivityResult()

registerForActivityResult()是startActivityForResult()的替代,简化了数据回调的写法

最基本最简单最常用的写法

        //java写法
        registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                Intent data = result.getData();
                int resultCode = result.getResultCode();
            }
        }).launch(new Intent(context,BActivity.class));

        //kotlin写法
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()
        ) { 
            val data = it.data
            val resultCode = it.resultCode
        }.launch(Intent(context,BActivity::class.java))


       //launch()方法,输入Intent,ActivityResultCallback:获取返回的数据,
       //ActivityResultContracts.StartActivityForResult 是官方提供用来处理回调数据的ActivityResultContract类
       //跳转到BActivity后,调用setResult()方法传递数据,这部分和以前一样

使用registerForActivityResult方法,可以简化许多相关操作,举几个例子

调用联系人列表,获取联系人

        registerForActivityResult(ActivityResultContracts.PickContact()){
            if(it != null){
                val cursor = contentResolver.query(it, null, null, null, null)
                cursor?.run {
                    if(cursor.moveToFirst()){
                        val name =
                            cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                        LogUtils.e("联系人姓名:$name")
                        if(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) == "1"){
                            //该联系人名下存在手机号,查询方法自行实现
                        }
                    }

                }
            }
        }.launch(null)

调用相机拍照

        //需要WRITE_EXTERNAL_STORAGE权限
        val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val values = ContentValues()
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, "图片名称.jpg")
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
            contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        }else{
            FileProvider.getUriForFile(this,BuildConfig.authorities,File(externalCacheDir!!.absolutePath+"图片名称.jpg"))
        }    
        registerForActivityResult(ActivityResultContracts.TakePicture()){
            if(it)
                Glide.with(this).load(uri).into(binding.imageView)
        }.launch(uri)


        //或者可以用更简单的方法
        registerForActivityResult(ActivityResultContracts.TakePicturePreview()){
            Glide.with(this).load(it).into(binding.imageView)
        }.launch(null)

调用文件选择器选择文件

调用文件选择器,获取指定类型的文件,可在launch()方法里使用mimetype指定调用文件类型,这里以调用文本文档为例

        registerForActivityResult(ActivityResultContracts.GetContent()){

        }.launch("text/plain")

如果需要选择多种文件类型,可以使用OpenDocument

        registerForActivityResult(ActivityResultContracts.OpenDocument()){
            Glide.with(this).load(it).into(binding.imageView)
        }.launch(arrayOf("image/*","text/plain"))

获取敏感权限

使用registerForActivityResult获取权限,也会变得非常简单

获取一个权限

            registerForActivityResult(ActivityResultContracts.RequestPermission()){
               if(it){
                   //用户同意了该权限
               }else{
                   //用户拒绝了该权限
               }

            }.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)

获取多个权限

        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){it->
            //通过的权限
            val grantedList = it.filterValues { it }.mapNotNull { it.key }
            //是否所有权限都通过
            val allGranted = grantedList.size == it.size
            val list = (it - grantedList).map { it.key }
            //未通过的权限
            val deniedList = list.filter { ActivityCompat.shouldShowRequestPermissionRationale(this, it) }
            //拒绝并且点了“不再询问”权限
            val alwaysDeniedList = list - deniedList
        }.launch(arrayOf("权限1","权限2","权限3"))

 

自定义ActivityResultContract<I,O>

ActivityResultContract<I,O> 官方提供的,通过输入类型I构建意图并将回调数据转换成输入类型O的契约类,官方总共给我们提供了14个ActivityResultContract实现类(截自本文发布时间),通过ActivityResultContracts构建,可以非常轻松地调用文件,联系人,敏感权限等等,另外,我们也可以实现自己的ActivityResultContract来简化意图操作,这里写一个裁剪图片的ActivityResultContract为例

import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.webkit.MimeTypeMap
import androidx.activity.result.contract.ActivityResultContract
import java.io.File

class CropImage: ActivityResultContract<CropImageResult, Uri>(){
    var outUri:Uri? = null
    //构建意图
    override fun createIntent(context: Context, input: CropImageResult): Intent {
        //把CropImageResult转换成裁剪图片的意图
        val intent = Intent("com.android.camera.action.CROP")
        val mimeType = context.contentResolver.getType(input.uri)
        val imageName:String = "${System.currentTimeMillis()}.${MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)}"
        outUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            val values = ContentValues()
            values.put(MediaStore.MediaColumns.DISPLAY_NAME,imageName)
            values.put(MediaStore.MediaColumns.MIME_TYPE,mimeType)
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
            context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values)
        }else{
            Uri.fromFile(File(context.externalCacheDir!!.absolutePath, imageName))
        }
        context.grantUriPermission(context.getPackageName(),outUri, Intent.FLAG_GRANT_READ_URI_PERMISSION )
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.putExtra("noFaceDetection", true) //去除默认的人脸识别,否则和剪裁匡重叠
        intent.setDataAndType(input.uri, mimeType)
        intent.putExtra("crop", "true") // crop=true 有这句才能出来最后的裁剪页面.
        intent.putExtra("output", outUri)
        intent.putExtra("outputFormat", "JPEG") // 返回格式
        intent.putExtra("return-data", true)


        if (input.outputX != 0 && input.outputY != 0) {
            intent.putExtra("outputX", input.outputX)
            intent.putExtra("outputY", input.outputY)
        }
        if(input.aspectX != 0 && input.aspectY != 0){
            if(input.aspectY == input.aspectX && Build.MANUFACTURER == "HUAWEI"){
                intent.putExtra("aspectX", 9999)
                intent.putExtra("aspectY", 9998)
            }else{
                intent.putExtra("aspectX", input.aspectX)
                intent.putExtra("aspectY", input.aspectY)
            }
        }

        return intent
    }

    //接收意图并处理数据
    override fun parseResult(resultCode: Int, intent: Intent?): Uri {
        if(outUri != null)
            return outUri!!
        else
            return Uri.parse("")
    }

}

/**
 * uri:需要裁剪的图片
 * aspect:长宽比例
 * output:图片输出长宽
 * uri 要裁剪的图片
 * aspect 剪裁比例
 * output 输入图片长宽
 */
class CropImageResult(val uri: Uri,
                      val aspectX:Int = 0,
                      val aspectY:Int = 0,
                      @androidx.annotation.IntRange(from = 0 ,to = 1080)
                      val outputX:Int = 0,
                      @androidx.annotation.IntRange(from = 0 ,to = 1080)
                      val outputY:Int = 0)

使用:

    //裁剪图片
    private fun crop(uri: Uri) {
        registerForActivityResult(CropImage()){
            Glide.with(this).load(it.toFile).into(binding.ivImage)
        }.launch(CropImageResult(uri,1,1))
    }

之前写这篇文章时,registerForActivityResult()方法的使用还没做限制,新版本的ActivityResultLauncher必需在activity的onCreate()方法或fragment的onCreate()、onAttach()里先注册,然后在需要调用的地方调用launch方法,以裁剪图片为例,在activity方法里需要这样写:


class Activity1: ComponentActivity(){
    val cropImage:ActivityResultLauncher<CropRequest> =         
    registerForActivityResult(CropImage()) {
            //获取到裁剪的图片
        }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Button(this).setOnClickListener { 
            cropImage.launch(…)
        }
        
    }
}

基类封装

registerForActivityResult回调顺序遵循的是FILO规则,用双端列队保存回调监听即可

 

猜你喜欢

转载自blog.csdn.net/jingzz1/article/details/107338872
今日推荐