美文网首页androidAndroid
Android onActivityResult的替代方法—re

Android onActivityResult的替代方法—re

作者: hao_developer | 来源:发表于2021-12-14 09:26 被阅读0次

    一、前言

    今天新建项目引入 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()方法传递数据,这部分和以前一样

    注意:\color{red}{ActivityResultLauncher必需在activity的onCreate()方法或fragment的onCreate()、onAttach()里先注册,然后在需要调用的地方调用launch方法。}\
    所以上面代码可能需要如下编写方式,为了方便,下面的案例统一使用上面的方式。

        private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
    
        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<TextView>(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<I,O>

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

    class CropImageContent : 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 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的替代方法—re

        本文链接:https://www.haomeiwen.com/subject/jinjfrtx.html