Android CameraX适配15
1.前言:
最近10月初把公司的项目升级到了Android15,但是发现了很多问题,这这其中最恶心的是Android15EdgeToEdge适配 ,因为这个所有界面都要修改,之前的导航栏和状态栏都失效了,今天是讲解CameraX升级15的过程,直接上代码。
2.edge-to-edge全面屏:
在Android 15设备上,若应用程序的targetSDK版本大于等于Android 15,则必须 强制进行全屏展示,同时状态栏和导航栏将透明化处理。对于targetSDK版本小于Android 15的应用,其默认不会启用边到边特性,即用户层的View仍会保持在状态栏和导航栏之间。
此外,在Android 15平台上,先前用于设置系统栏颜色的API,如setNavigationBarColor等,将被弃用。即便使用这些方法进行设置,系统也将默认提供沉浸式体验。
简适配代码如下:
kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
initPermission()
initView()
val window = window
WindowCompat.setDecorFitsSystemWindows(window, false)
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
val stateBars = insets.getInsets(WindowInsetsCompat.Type.statusBars())
v.setPadding(stateBars.left, 0, stateBars.right, 0)
insets
}
}

3.升级后报错:
打开系统拍照界面后返回,就直接崩溃了,崩溃信息如下。

kotlin
Process: com.example.cameraxdemo, PID: 17531
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=102, result=-1, data=null} to activity {com.example.cameraxdemo/com.example.cameraxdemo.activity.CameraActivity}: java.lang.IllegalArgumentException: Mutation of _data is not allowed.
at android.app.ActivityThread.deliverResults(ActivityThread.java:6371)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:6410)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:69)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:60)
at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:179)
at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:114)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:86)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2914)
at android.os.Handler.dispatchMessage(Handler.java:112)
at android.os.Looper.loopOnce(Looper.java:288)
at android.os.Looper.loop(Looper.java:393)
at android.app.ActivityThread.main(ActivityThread.java:9564)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1010)
Caused by: java.lang.IllegalArgumentException: Mutation of _data is not allowed.
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:155)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:589)
at android.content.ContentResolver.insert(ContentResolver.java:2241)
at android.content.ContentResolver.insert(ContentResolver.java:2197)
at com.example.cameraxdemo.utils.FileUtil.getHeadJpgFile(FileUtil.java:1427)
at com.example.cameraxdemo.activity.CameraActivity.workCropFun(CameraActivity.kt:100)
at com.example.cameraxdemo.activity.CameraActivity.onActivityResult(CameraActivity.kt:139)
at android.app.Activity.onActivityResult(Activity.java:7666)
at android.app.Activity.internalDispatchActivityResult(Activity.java:9588)
at android.app.Activity.dispatchActivityResult(Activity.java:9565)
at android.app.ActivityThread.deliverResults(ActivityThread.java:6360)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:6410)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:69)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:60)
at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:179)
at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:114)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:86)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2914)
at android.os.Handler.dispatchMessage(Handler.java:112)
at android.os.Looper.loopOnce(Looper.java:288)
at android.os.Looper.loop(Looper.java:393)
at android.app.ActivityThread.main(ActivityThread.java:9564)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1010)
4.报错的核心代码:

kotlin
public static Object getHeadJpgFile() {
String fileName = System.currentTimeMillis() + FileManager.JPG_SUFFIX;
String headPath = FileManager.getAvatarPath(fileName);
File imgFile;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
imgFile = new File(headPath);
// 通过 MediaStore API 插入file 为了拿到系统裁剪要保存到的uri(因为App没有权限不能访问公共存储空间,需要通过 MediaStore API来操作)
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, imgFile.getAbsolutePath());
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
return CameraApp.Companion.getMInstance().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
imgFile = new File(headPath);
}
return imgFile;
}
5.报错原因分析:
该错误的核心原因是尝试修改 Android 系统中受保护的 _data 字段 。_data 是媒体库(MediaStore)中存储文件路径的字段,Android 从 API 29(Android 10)开始限制直接修改该字段,强制要求使用 MediaStore 的规范 API 进行文件操作,禁止通过 ContentResolver 直接插入或修改 _data 字段,否则会抛出 IllegalArgumentException: Mutation of _data is not allowed 异常。
从报错堆栈看,问题出现在 FileUtil.getHeadJpgFile() 方法中通过 ContentResolver.insert() 操作媒体库时,试图设置 _data 字段的值,违反了系统限制。
6.解决方法:
6.1 直接去掉此属性设置:
kotlin
public static Object getHeadJpgFile() {
String fileName = System.currentTimeMillis() + FileManager.JPG_SUFFIX;
String headPath = FileManager.getAvatarPath(fileName);
File imgFile;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
imgFile = new File(headPath);
// 通过 MediaStore API 插入file 为了拿到系统裁剪要保存到的uri(因为App没有权限不能访问公共存储空间,需要通过 MediaStore API来操作)
ContentValues values = new ContentValues();
// values.put(MediaStore.Images.Media.DATA, imgFile.getAbsolutePath());
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
return CameraApp.Companion.getMInstance().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
imgFile = new File(headPath);
}
return imgFile;
}
6.2 使用MediaStore的其他属性:
RELATIVE_PATH、DISPLAY_NAME等.
kotlin
/**
* 获取jpg图片输出路径
*
* @return 输出路径
*/
public static Object getHeadJpgFile() {
String fileName = System.currentTimeMillis() + FileManager.JPG_SUFFIX;
String headPath = FileManager.getAvatarPath(fileName);
File imgFile;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
imgFile = new File(headPath);
// 通过 MediaStore API 插入file 为了拿到系统裁剪要保存到的uri(因为App没有权限不能访问公共存储空间,需要通过 MediaStore API来操作)
ContentValues values = new ContentValues();
//方式1去掉Media.DATA
//values.put(MediaStore.Images.Media.DATA, imgFile.getAbsolutePath());
//方式2使用MediaStore。RELATIVE_PATH
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/CustomFolder");
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
return CameraApp.Companion.getMInstance().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
imgFile = new File(headPath);
}
return imgFile;
}public static Object getHeadJpgFile() {
String fileName = System.currentTimeMillis() + FileManager.JPG_SUFFIX;
String headPath = FileManager.getAvatarPath(fileName);
File imgFile;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
imgFile = new File(headPath);
// 通过 MediaStore API 插入file 为了拿到系统裁剪要保存到的uri(因为App没有权限不能访问公共存储空间,需要通过 MediaStore API来操作)
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/CustomFolder");
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
return CameraApp.Companion.getMInstance().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
imgFile = new File(headPath);
}
return imgFile;
}
7.运行效果:




8.日志打印:
8.1 使用RELATIVE_PATH方式

8.2 去掉Media.DATA

9.完整测试代码:
kotlin
package com.example.cameraxdemo
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.view.View
import android.view.WindowManager
import android.webkit.MimeTypeMap
import android.widget.Button
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.AspectRatio
import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.core.VideoCapture
import androidx.camera.core.VideoCapture.OnVideoSavedCallback
import androidx.camera.core.VideoCapture.OutputFileOptions
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.net.toFile
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import com.blankj.utilcode.util.LogUtils
import com.example.cameraxdemo.activity.CameraActivity
import com.example.cameraxdemo.utils.Constants
import com.example.cameraxdemo.utils.Constants.Companion.DATE_FORMAT
import com.example.cameraxdemo.utils.Constants.Companion.PHOTO_EXTENSION
import com.example.cameraxdemo.utils.Constants.Companion.REQUIRED_PERMISSIONS
import com.example.cameraxdemo.utils.FileManager
import com.example.cameraxdemo.utils.ToastUtils
import com.example.cameraxdemo.utils.VideoFileUtils.createFile
import com.example.cameraxdemo.utils.VideoFileUtils.getOutputDirectory
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private var imageCamera: ImageCapture? = null
private lateinit var cameraExecutor: ExecutorService
@SuppressLint("RestrictedApi")
private var videoCapture: VideoCapture? = null
private var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA//当前相机
private var preview: Preview? = null//预览对象
private var cameraProvider: ProcessCameraProvider? = null//相机信息
private lateinit var camera: Camera //相机对象
private var isRecordVideo: Boolean = false
private val TAG = "CameraXApp"
private lateinit var outputDirectory: File
private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
private val btnCameraCapture: Button by lazy { findViewById(R.id.btnCameraCapture) }
private val btnVideo: Button by lazy { findViewById(R.id.btnVideo) }
private val btnSwitch: Button by lazy { findViewById(R.id.btnSwitch) }
private val btnOpenCamera: Button by lazy { findViewById(R.id.btnOpenCamera) }
private val viewFinder: PreviewView by lazy { findViewById(R.id.mPreviewView) }
private val rootView : View by lazy {findViewById(R.id.main_root) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
initPermission()
initView()
val window = window
WindowCompat.setDecorFitsSystemWindows(window, false)
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
val stateBars = insets.getInsets(WindowInsetsCompat.Type.statusBars())
v.setPadding(stateBars.left, 0, stateBars.right, 0)
insets
}
}
private fun initView() {
outputDirectory = getOutputDirectory(this)
}
@SuppressLint("RestrictedApi")
private fun initListener() {
btnCameraCapture.setOnClickListener {
takePhoto()
}
btnVideo.setOnClickListener {
if (!isRecordVideo) {
takeVideo()
isRecordVideo = true
btnVideo.text = "停止录像"
} else {
isRecordVideo = false
videoCapture?.stopRecording()//停止录制
//preview?.clear()//清除预览
btnVideo.text = "开始录像"
}
}
btnSwitch.setOnClickListener {
cameraSelector = if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) {
CameraSelector.DEFAULT_FRONT_CAMERA
} else {
CameraSelector.DEFAULT_BACK_CAMERA
}
if (!isRecordVideo) {
startCamera()
}
}
btnOpenCamera.setOnClickListener {
val intent = Intent(this, CameraActivity::class.java)
startActivity(intent)
}
}
private fun initPermission() {
if (checkPermissions()) {
// ImageCapture
startCamera()
} else {
requestPermission()
}
}
private fun requestPermission() {
when {
Build.VERSION.SDK_INT >= 33 -> {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_MEDIA_AUDIO,Manifest.permission.READ_MEDIA_VIDEO,Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO),
Constants.REQUEST_CODE_PERMISSIONS
)
}
else -> {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, Constants.REQUEST_CODE_PERMISSIONS)
}
}
}
/**
* 开始拍照
*/
private fun takePhoto() {
val imageCapture = imageCamera ?: return
val photoFile = createFile(outputDirectory, DATE_FORMAT, PHOTO_EXTENSION)
val metadata = ImageCapture.Metadata().apply {
// Mirror image when using the front camera
isReversedHorizontal = lensFacing == CameraSelector.LENS_FACING_FRONT
}
val outputOptions =
ImageCapture.OutputFileOptions.Builder(photoFile).setMetadata(metadata).build()
imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
LogUtils.e(TAG, "Photo capture failed: ${exc.message}", exc)
ToastUtils.shortToast(" 拍照失败 ${exc.message}")
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
ToastUtils.shortToast(" 拍照成功 $savedUri")
LogUtils.e(TAG, savedUri.path.toString())
val mimeType = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(savedUri.toFile().extension)
MediaScannerConnection.scanFile(
this@MainActivity,
arrayOf(savedUri.toFile().absolutePath),
arrayOf(mimeType)
) { _, uri ->
LogUtils.d(
TAG,
"Image capture scanned into media store: ${uri.path.toString()}"
)
}
}
})
}
/**
* 开始录像
*/
@SuppressLint("RestrictedApi", "ClickableViewAccessibility", "MissingPermission")
private fun takeVideo() {
//开始录像
try {
isRecordVideo = true
val mFileDateFormat = SimpleDateFormat(DATE_FORMAT, Locale.US)
//视频保存路径
val file =
File(FileManager.getCameraVideoPath(), mFileDateFormat.format(Date()) + ".mp4")
val outputOptions = OutputFileOptions.Builder(file)
videoCapture?.startRecording(
outputOptions.build(),
Executors.newSingleThreadExecutor(),
object : OnVideoSavedCallback {
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
isRecordVideo = false
if(BuildConfig.DEBUG){
LogUtils.d(TAG, "===视频保存的地址为=== ${file.absolutePath}")
}
//保存视频成功回调,会在停止录制时被调用
ToastUtils.shortToast(" 录像成功 $file")
}
override fun onError(
videoCaptureError: Int,
message: String,
cause: Throwable?
) {
//保存失败的回调,可能在开始或结束录制时被调用
isRecordVideo = false
if(BuildConfig.DEBUG) {
LogUtils.e(TAG, "onError: $message")
}
ToastUtils.shortToast(" 录像失败 $message")
}
})
} catch (e: Exception) {
e.printStackTrace()
if(BuildConfig.DEBUG) {
LogUtils.e(TAG, "===录像出错===${e.message}")
}
}
}
/**
* 开始相机预览
*/
@SuppressLint("RestrictedApi")
private fun startCamera() {
cameraExecutor = Executors.newSingleThreadExecutor()
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
cameraProvider = cameraProviderFuture.get()//获取相机信息
//预览配置
preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
imageCamera = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
videoCapture = VideoCapture.Builder()//录像用例配置
.setTargetAspectRatio(AspectRatio.RATIO_16_9) //设置高宽比
//.setTargetRotation(viewFinder.display!!.rotation)//设置旋转角度
.build()
try {
cameraProvider?.unbindAll()//先解绑所有用例
camera = cameraProvider?.bindToLifecycle(
this,
cameraSelector,
preview,
imageCamera,
videoCapture
)!!//绑定用例
} catch (e: Exception) {
if(BuildConfig.DEBUG) {
LogUtils.e(TAG, "Use case binding failed", e.message)
}
}
}, ContextCompat.getMainExecutor(this))
initListener()
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
Constants.REQUEST_CODE_PERMISSIONS -> {
var allPermissionsGranted = true
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allPermissionsGranted = false
break
}
}
when {
allPermissionsGranted -> {
// 权限已授予,执行文件读写操作
startCamera()
}
else -> {
// 权限被拒绝,处理权限请求失败的情况
ToastUtils.shortToast("请您打开必要权限")
requestPermission()
}
}
}
}
}
private fun checkPermissions(): Boolean {
when {
Build.VERSION.SDK_INT >= 33 -> {
val permissions = arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_AUDIO,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
)
for (permission in permissions) {
return Environment.isExternalStorageManager()
}
}
else -> {
for (permission in REQUIRED_PERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
}
}
return true
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
}
kotlin
package com.example.cameraxdemo.activity
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.view.View
import android.widget.Button
import android.widget.ImageView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.blankj.utilcode.util.LogUtils
import com.bumptech.glide.Glide
import com.example.cameraxdemo.R
import com.example.cameraxdemo.utils.Constants.Companion.REQUEST_CODE_CAMERA
import com.example.cameraxdemo.utils.Constants.Companion.REQUEST_CODE_CROP
import com.example.cameraxdemo.utils.FileManager
import com.example.cameraxdemo.utils.FileUtil
import java.io.File
/**
*@author: njb
*@date: 2023/8/15 17:20
*@desc:
*/
class CameraActivity :AppCompatActivity(){
private var mUploadImageUri: Uri? = null
private var mUploadImageFile: File? = null
private var photoUri: Uri? = null
private val btnCamera:Button by lazy { findViewById(R.id.btnCamera) }
private val ivAvatar:ImageView by lazy { findViewById(R.id.iv_avatar) }
private val TAG = CameraActivity::class.java.name
private val rootView : View by lazy {findViewById(R.id.camera_root) }
@SuppressLint("RestrictedApi")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_camera)
initView()
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
val stateBars = insets.getInsets(WindowInsetsCompat.Type.statusBars())
v.setPadding(stateBars.left, stateBars.top, stateBars.right, stateBars.bottom)
insets
}
}
private fun initView() {
btnCamera.setOnClickListener {
startSystemCamera()
}
}
/**
* 调起系统相机拍照
*/
private fun startSystemCamera() {
val takeIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val values = ContentValues()
//根据uri查询图片地址
photoUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
LogUtils.d(TAG, "photoUri:" + photoUri?.authority + ",photoUri:" + photoUri?.path)
//放入拍照后的地址
takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
//调起拍照
startActivityForResult(
takeIntent,
REQUEST_CODE_CAMERA
)
}
/**
* 设置用户头像
*/
private fun setAvatar() {
val file: File? = if (mUploadImageUri != null) {
FileManager.getMediaUri2File(mUploadImageUri)
} else {
mUploadImageFile
}
Glide.with(this).load(file).into(ivAvatar)
LogUtils.d(TAG,"filepath"+ file!!.absolutePath)
}
/**
* 系统裁剪方法
*/
private fun workCropFun(imgPathUri: Uri?) {
mUploadImageUri = null
mUploadImageFile = null
if (imgPathUri != null) {
val imageObject: Any = FileUtil.getHeadJpgFile()
if (imageObject is Uri) {
mUploadImageUri = imageObject
}
if (imageObject is File) {
mUploadImageFile = imageObject
}
val intent = Intent("com.android.camera.action.CROP")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
intent.run {
setDataAndType(imgPathUri, "image/*")// 图片资源
putExtra("crop", "true") // 裁剪
putExtra("aspectX", 1) // 宽度比
putExtra("aspectY", 1) // 高度比
putExtra("outputX", 1000) // 裁剪框宽度
putExtra("outputY", 1000) // 裁剪框高度
putExtra("scale", true) // 缩放
putExtra("return-data", false) // true-返回缩略图-data,false-不返回-需要通过Uri
putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()) // 保存的图片格式
putExtra("noFaceDetection", true) // 取消人脸识别
putExtra("quality", 100)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
putExtra(MediaStore.EXTRA_OUTPUT, mUploadImageUri)
} else {
val imgCropUri = Uri.fromFile(mUploadImageFile)
putExtra(MediaStore.EXTRA_OUTPUT, imgCropUri)
}
}
startActivityForResult(
intent, REQUEST_CODE_CROP
)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CODE_CAMERA) {//拍照回调
workCropFun(photoUri)
} else if (requestCode == REQUEST_CODE_CROP) {//裁剪回调
setAvatar()
}
}
}
}
10.总结:
升级Android15后需要适配EdgeToEdge和MediaStore.Images.Media.DATA, 找到报错,分析出原因,解决即可.当然15还有其他适配,这里只是在跑之前的项目遇到的问题顺手讲解一下而已,后面会重点讲解全面屏适配.