Jetpack 之 ActivityResultContract的使用,调用相机、相册并裁剪的优雅实现

Jetpack 之 ActivityResultContract的使用,调用相机、相册并裁剪的优雅实现,第1张

Jetpack 之 ActivityResultContract的使用,调用相机、相册并裁剪的优雅实现

自从Google官方发布Jetpack以来,我们Android开发的很多开发习惯都发生了巨大的变化,最近又双叒叕在实现更换头像的功能。发现以前startActivityForResult + onActivityResult 那一套做法又有了新的实现方式。略微找了下相关资料,动手实现了出来,发现代码能够更简洁也更优雅了,感觉收获了一点小惊喜。所以这里给大家分享一下。

以前请求权限以及startActivityForResult,都免不了要实现onActivityResult,代码分离,逻辑不连贯,还要生命一连串code,一个不留神就功能对应不上,浪费时间排查。现在使用registerForActivityResult + ActivityResultContract来实现就更加方便了。

官方已经帮我们封装好了一部分可以直接使用,列在下面就不多做介绍了:

ActivityResultContracts.CaptureVideo, 
ActivityResultContracts.Createdocument, 
ActivityResultContracts.GetContent, 
ActivityResultContracts.GetMultipleContents, 
ActivityResultContracts.OpendocumentTree, 
ActivityResultContracts.Opendocument, 
ActivityResultContracts.OpenMultipledocuments,
ActivityResultContracts.PickContact, ActivityResultContracts.RequestMultiplePermissions, ActivityResultContracts.RequestPermission,
ActivityResultContracts.StartActivityForResult,
ActivityResultContracts.StartIntentSenderForResult,
ActivityResultContracts.TakePicturePreview,
ActivityResultContracts.TakePicture,
ActivityResultContracts.TakeVideo,
ActivityResultContracts.WatchFaceEditorContract

这里主要介绍自定义Contract来实现打开相机拍照、打开相册选择照片,然后进行裁剪是如何实现的。

一、首先,先继承ActivityResultContract实现自定义的Contract,如下:

SelectPhotoContract:

class SelectPhotoContract : ActivityResultContract() {
    companion object {
        private const val TAG = "SelectPhotoContract"
    }

    override fun createIntent(context: Context, input: Unit?): Intent {
        return Intent(Intent.ACTION_PICK).setType("image
class TakePhotoContract : ActivityResultContract() {
    companion object {
        private const val TAG = "TakePhotoContract"
    }

    private var uri: Uri? = null

    override fun createIntent(context: Context, input: Unit?): Intent {
        val mimeType = "image/jpeg"
        val fileName = "IMG_${System.currentTimeMillis()}.jpg"
        uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Android 10 及以上获取图片uri
            val values = contentValuesOf(
                Pair(MediaStore.MediaColumns.DISPLAY_NAME, fileName),
                Pair(MediaStore.MediaColumns.MIME_TYPE, mimeType),
                Pair(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
            )
            context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        } else {
            // Android 9 及以下获取图片uri
            FileProvider.getUriForFile(
                context, "${context.packageName}.provider",
                File(context.externalCacheDir, "/$fileName")
            )
        }
        return Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(MediaStore.EXTRA_OUTPUT, uri)
    }

    override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
        Logger.d(TAG, "Take photo, resultCode: $resultCode, uri: $uri")
        if (resultCode == Activity.RESULT_OK) return uri
        return null
    }
}

CropPhotoContract:

class CropPhotoContract : ActivityResultContract() {
    companion object {
        private const val TAG = "CropPhotoContract"
    }

    private var output: CropOutput? = null

    override fun createIntent(context: Context, input: Uri): Intent {
        // 获取输入图片uri的媒体类型
        val mimeType = context.contentResolver.getType(input)
        // 创建新的图片名称
        val fileName = "IMG_${System.currentTimeMillis()}.${
            MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
        }"
        val outputUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Android 10 及以上获取图片uri
            val values = contentValuesOf(
                Pair(MediaStore.MediaColumns.DISPLAY_NAME, fileName),
                Pair(MediaStore.MediaColumns.MIME_TYPE, mimeType),
                Pair(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
            )
            context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        } else {
            Uri.fromFile(File(context.externalCacheDir!!.absolutePath, fileName))
        }
        output = CropOutput(outputUri!!, fileName)
        return Intent("com.android.camera.action.CROP")
            .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            .setDataAndType(input, "image
    private val takePhotoLauncher = (this as ComponentActivity).registerForActivityResult(TakePhotoContract()) { uri ->
        uri?.let {
            cropPhotoLauncher.launch(it)
        }
    }

    
    private val selectPhotoLauncher = (this as ComponentActivity).registerForActivityResult(SelectPhotoContract()) { uri ->
        uri?.let {
            cropPhotoLauncher.launch(it)
        }
    }

    
    private val cropPhotoLauncher = (this as ComponentActivity).registerForActivityResult(CropPhotoContract()) { output ->
        output?.let {
            Logger.d(TAG, "裁剪完成,开始上传头像到服务器, output: $it")
            // 上传头像
            showLoading(getString(R.string.base_uploading))
            mViewModel.fetchUpload(ContentUriRequestBody(contentResolver, it.uri), it.fileName)
        }
    }

三、调用:

// 打开相机
takePhotoLauncher.launch(null)
// 打开相册
selectPhotoLauncher.launch(null)

OVER

很简单,不是么!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5718062.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存