兼容Android11以上手机拍照无法保存工具

        近期使用Android拍照功能时发现,一些Android11版本及以上的手机拍照时无法将图片保存到应用程序指定目录中。只会创建文件但是文件的大小为0KB。这个问题会导致应用中一些更换图片的逻辑失效比如用户头像替换、背景图片的替换等。如何解决这个问题给大家提供一个兼容Android 24版本以上的拍照、视频及打开相册的兼容工具类。工具类如下:

class PhotoUtils private constructor() {

    private val mTag: String by lazy {
        TTLog.makeLogTag(PhotoUtils::class.java)
    }

    companion object {
        private const val GALLERY_REQUEST_CODE = 105    // 相册选图标记
        private const val CAMERA_REQUEST_CODE = 106    // 相机拍照标记
        private const val VIDEO_REQUEST_CODE = 107    //相册选择视频

        //拍照图片地址
        private var mTempPhotoPath: String = ""

        //工具列对象
        private var mInstance: PhotoUtils? = null

        //上下文
        private lateinit var mContext: Context

        /**
         * 获取工具列对象方法
         */
        fun getInstance() = mInstance ?: PhotoUtils().also {
            mInstance = it
        }
    }

    /**
     * 打开相册
     * @param activity 所在Activity对象
     */
    fun openPhotoPick(activity: Activity) {
        //设置对象
        mContext = activity
        val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        // 如果限制上传到服务器的图片类型时可以直接写如:"image/jpeg 、 image/png等的类型"
        pickIntent.type = "image/*"
        activity.startActivityForResult(pickIntent, GALLERY_REQUEST_CODE)
    }

    /**
     * 打开视频
     * @param activity 所在Activity对象
     */
    fun openVideoPick(activity: Activity) {
        //设置对象
        mContext = activity
        val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
        pickIntent.type = CONTENT_TYPE
        activity.startActivityForResult(pickIntent, VIDEO_REQUEST_CODE)
    }

    /**
     * 开启照相机
     * @param activity 所在Activity对象
     */
    fun openPhotograph(activity: Activity) {
        val takeIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        //设置对象
        mContext = activity
        //生成图片路径
        mTempPhotoPath = activity.getExternalFilesDir(Environment.DIRECTORY_DCIM)?.absolutePath + File.separator + "${System.currentTimeMillis()}.jpeg"
        //解决Android11无法拍照存储问题
        val imageUri = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
            FileProvider.getUriForFile(activity, "${activity.packageName}.fileprovider", File(mTempPhotoPath))
        } else {
            Uri.fromFile(File(mTempPhotoPath))
        }
        //下面这句指定调用相机拍照后的照片存储的路径
        takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
        //
        activity.startActivityForResult(takeIntent, CAMERA_REQUEST_CODE)
    }

    /**
     * 该方法必须放置在对应Activity的onActivityResult方法中
     * @param requestCode 请求码
     * @param resultCode 响应码
     * @param data 数据传输对象
     * @param method 图片或视频地址回调方法
     */
    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?, method: ((type: MediaTypeEnum, uri: Uri, path: String) -> Unit)? = null) {
        if (resultCode == BaseOtherActivity.RESULT_OK) {
            when (requestCode) {
                CAMERA_REQUEST_CODE -> {// 调用相机拍照
                    val temp = File(mTempPhotoPath)
                    TTLog.e(mTag, "image_temp:${temp.absoluteFile}")
                    method?.let {
                        it(MediaTypeEnum.IMAGE_TYPE, Uri.fromFile(temp), temp.absolutePath)
                    }
                }
                GALLERY_REQUEST_CODE -> {// 直接从相册获取图片地址
                    data?.let { dataIntent ->
                        TTLog.e(mTag, "image_temp:${dataIntent.data}")
                        getFilePath(MediaTypeEnum.IMAGE_TYPE, dataIntent.data
                                ?: Uri.parse(""), method)
                    }
                }
                VIDEO_REQUEST_CODE -> {// 直接从相册获取视频地址
                    data?.let { dataIntent ->
                        TTLog.e(mTag, "video_temp:${dataIntent.data}")
                        getFilePath(MediaTypeEnum.VIDEO_TYPE, dataIntent.data
                                ?: Uri.parse(""), method)
                    }
                }
                else -> {
                    //todo:未知类型
                }
            }
        }
    }

    /**
     * 获得文件sd卡路径
     * @param type 多媒体类型[MediaTypeEnum]
     * @param uri 获取ContentProvider共享数据地址
     * @param method 回到方法
     */
    @SuppressLint("CheckResult")
    private fun getFilePath(type: MediaTypeEnum, uri: Uri, method: ((type: MediaTypeEnum, uri: Uri, path: String) -> Unit)? = null) {
        Single.create<String> {
            val cursor = mContext.contentResolver.query(uri, null, null, null, null)
            if (cursor != null && cursor.count > 0) {
                if (cursor.moveToFirst()) {
                    val data = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA))
                    it.onSuccess(data)
                }
                cursor.close()
            } else {
                it.onError(Throwable("query uri is null"))
            }
        }.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ path ->
                    method?.let {
                        it(type, uri, path ?: "")
                    }
                }, {
                    method?.let {
                        it(type, uri, "")
                    }
                })
    }

    /**
     * 多媒体类型枚举
     */
    enum class MediaTypeEnum {
        IMAGE_TYPE,
        VIDEO_TYPE,
        DEFAULT_TYPE
    }
}

该工具类中定义了三个常量分别是GALLERY_REQUEST_CODED、CAMERA_REQUEST_CODE、VIDEO_REQUEST_CODE依次代表相册选图标记、相册拍照标记及相册选择视频标记。也提供了三个open方法对应打开相应的功能。在onActivityResult方法中获取到的相册中的图片或者视频信息往往都是uri地址(content开头),而我们需要的并不是uri而是具体的图片地址。这样的话就需要通过ContentProvider进行数据库查询,所以在工具类中提供了一个getFilePath()方法对其进行数据库查询获取对应图片或者视频的绝对地址数据。当获取到这个地址就可以进行下一步操作,比如上传、发送文件等。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊猫vs笨笨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值