Android onActivityResult的替代方法—registerForActivityResult

一、前言

今天新建项目引入 implementation "androidx.fragment:fragment-ktx:1.3.0"包后,发现startActivityForResult()、onActivityResult()、requestPermissions()、onRequestPermissionsResult()方法被标记为过时,取而代之的是新方法registerForActivityResult()

二、基本用法

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

跳转到BActivity后,调用setResult()方法传递数据,这部分和以前一样

注意:
所以上面代码可能需要如下编写方式,为了方便,下面的案例统一使用上面的方式。

    private lateinit var activityResultLauncher: ActivityResultLauncher

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            val data = it.data
            val resultCode = it.resultCode
        }
        
        val textView = findViewById(R.id.textView)
        textView.setOnClickListener {
            activityResultLauncher.launch(Intent(this, BActivity::class.java))
        }
    }

三、调用联系人列表

        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))
                        Log.e(TAG, "联系人姓名:$name")
                        if (cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) == "1") {
                            //该联系人名下存在手机号,查询方法自行实现
                        }
                    }
                }
            }
        }.launch(null)

四、获取敏感权限

4.1 获取单个权限

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

4.2 获取多个权限

        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){it->
            //通过的权限
            val grantedList = it.filterValues { it }.mapNotNull { it.key }
            
            //未通过的权限
            val deniedList = (it - grantedList).map { it.key }
            
            //拒绝并且点了“不再询问”权限
            val alwaysDeniedList = deniedList.filterNot {
                    ActivityCompat.shouldShowRequestPermissionRationale(
                        activity,
                        it
                    )
                }
        }.launch(
            arrayOf(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE
            )
        )

五、调用文件选择器

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

        registerForActivityResult(ActivityResultContracts.GetContent()){
 
        }.launch("text/plain")

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

        registerForActivityResult(ActivityResultContracts.OpenDocument()){
        
        }.launch(arrayOf("image/*","text/plain"))

常用的文件mimetype对照表

扩展名类 型/子类型
.jpg、.jpeg image/jpeg
.png image/png
.webp image/webp
.bmp image/bmp
.gif image/gif
.xls application/vnd.ms-excel
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.doc application/msword
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
.pdf application/pdf
.pps application/vnd.ms-powerpoint
.ppt application/vnd.ms-powerpoint
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
.apk application/vnd.android.package-archive
.js application/x-javascript
.jar application/java-archive
.bin、.class、.exe、.rar application/octet-stream
.tar application/x-tar
.tgz application/x-compressed
.zip application/x-zip-compressed
.z application/x-compress
.html、.htm text/html
.txt、.c、.cpp、.h、.java、.conf、.log、.prop、.rc、.sh、.xml text/plain
.wav audio/x-wav
.wma audio/x-ms-wma
.wmv audio/x-ms-wmv
.m3u audio/x-mpegurl
.m4a、.m4b、.m4p audio/mp4a-latm
.mp2、.mp3 audio/x-mpeg
.mpga audio/mpeg
.ogg audio/ogg
.3gp video/3gpp
.asf video/x-ms-asf
.avi video/x-msvideo
.m4u video/vnd.mpegurl
.m4v video/x-m4v
.mov video/quicktime
.mp4、.mpg4 video/mp4
.mpe、.mpeg、.mpg video/mpeg

最全的:http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types

六、调用相机

        //需要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)

或者使用下面的方式,直接返回Bitmap图片

        registerForActivityResult(ActivityResultContracts.TakePicturePreview()){
            Glide.with(this).load(it).into(binding.imageView)
        }.launch(null)

七、自定义ActivityResultContract

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

class CropImageContent : ActivityResultContract() {
    var outUri: Uri? = null

    //构建意图
    override fun createIntent(context: Context, input: CropImageResult): Intent {
        //把CropImageResult转换成裁剪图片的意图
        val intent = Intent("com.android.camera.action.CROP")
        val imageName = "${input.imageName}.jpg"
        outUri =  Uri.fromFile(getClipImage(context, imageName))

        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.putExtra("noFaceDetection", true) //去除默认的人脸识别,否则和剪裁匡重叠
        intent.setDataAndType(input.uri, "image/*")
        intent.putExtra("crop", "true") // crop=true 有这句才能出来最后的裁剪页面.
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()) // 返回格式
        intent.putExtra("return-data", false)
        intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri)

        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 (resultCode == -1) {
            if (outUri != null)
                return outUri!!
            else
                return null
        } else { //取消裁剪
            return null
        }

    }

    /**
     * 获取裁剪之后的图片文件
     */
    private fun getClipImage(context: Context, clipImageName: String): File {
        val file = File(externalCacheDir!!.absolutePath+"图片名称.jpg")
        return file
    }

}

/**
 * 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,
    val imageName: String = "temp_crop_image"
)

使用:

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

你可能感兴趣的:(Android onActivityResult的替代方法—registerForActivityResult)