You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中实现相机/相册选图并直接裁剪功能

Android 图片选择+裁剪实现方案(Kotlin+XML)

一、原生实现(无第三方依赖,稳定可靠)

1. 弹出相机/相册选择菜单

在按钮点击事件中弹出选项对话框:

btnSelectImage.setOnClickListener {
    val options = arrayOf("拍照", "从相册选择")
    AlertDialog.Builder(this)
        .setTitle("选择图片来源")
        .setItems(options) { _, which ->
            when (which) {
                0 -> openCamera()
                1 -> openGallery()
            }
        }
        .show()
}

2. 调用相机拍照

需提前申请相机、存储权限,通过FileProvider处理文件Uri避免权限问题:

private lateinit var photoUri: Uri
private val REQUEST_CAMERA = 1001

private fun openCamera() {
    // 权限检查
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
        ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_CAMERA)
        return
    }
    // 创建临时文件存储拍照结果
    val photoFile = File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "temp_photo.jpg")
    photoUri = FileProvider.getUriForFile(this, "${packageName}.fileprovider", photoFile)
    // 启动相机Intent
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
        putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
        addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    }
    startActivityForResult(intent, REQUEST_CAMERA)
}

在AndroidManifest.xml注册FileProvider:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

在res/xml目录创建file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="photos" path="Pictures" />
</paths>

3. 调用相册选择图片

private val REQUEST_GALLERY = 1002

private fun openGallery() {
    val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply {
        type = "image/*"
    }
    startActivityForResult(intent, REQUEST_GALLERY)
}

4. 图片裁剪

调用系统裁剪功能,支持比例锁定和尺寸设置:

private val REQUEST_CROP = 1003

private fun cropImage(uri: Uri) {
    val cropIntent = Intent("com.android.camera.action.CROP").apply {
        setDataAndType(uri, "image/*")
        putExtra("crop", "true")
        // 设置1:1裁剪比例
        putExtra("aspectX", 1)
        putExtra("aspectY", 1)
        // 设置输出图片尺寸
        putExtra("outputX", 300)
        putExtra("outputY", 300)
        putExtra("return-data", true)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    startActivityForResult(cropIntent, REQUEST_CROP)
}

5. 处理返回结果

用registerForActivityResult替代过时的onActivityResult:

// 相册选择结果
private val galleryResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == RESULT_OK) {
        result.data?.data?.let { cropImage(it) }
    }
}

// 相机拍照结果
private val cameraResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == RESULT_OK) {
        cropImage(photoUri)
    }
}

// 裁剪结果
private val cropResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == RESULT_OK) {
        val bitmap = result.data?.extras?.getParcelable<Bitmap>("data")
        // 将裁剪后的图片显示到ImageView
        imageView.setImageBitmap(bitmap)
    }
}

// 注意:openCamera和openGallery中需改用launch启动Intent
// 例如openGallery()中替换为:galleryResult.launch(intent)

二、推荐第三方库(文档完善+兼容性好)

UCrop

目前最主流的图片裁剪库,支持自定义裁剪比例、圆形裁剪、样式定制,Kotlin兼容良好:

  1. 添加依赖:
implementation 'com.github.yalantis:ucrop:2.2.8'
  1. 调用裁剪:
val destinationUri = Uri.fromFile(File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "cropped_photo.jpg"))
UCrop.of(sourceUri, destinationUri)
    .withAspectRatio(1f, 1f)
    .withMaxResultSize(500, 500)
    .start(this)
  1. 处理裁剪结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == RESULT_OK && requestCode == UCrop.REQUEST_CROP) {
        val croppedUri = UCrop.getOutput(data!!)
        // 使用裁剪后的图片
    } else if (resultCode == UCrop.RESULT_ERROR) {
        val error = UCrop.getError(data!!)
        // 处理错误
    }
}

PictureSelector

一站式图片选择+裁剪+压缩库,内置相机相册选择逻辑,无需自己处理权限和Uri:
添加依赖后,可通过简单配置实现选择+裁剪的完整流程,文档详细易上手。

三、常见问题排查

  • 权限问题:Android 13+需额外申请READ_MEDIA_IMAGES权限,动态权限申请要处理用户拒绝的情况。
  • FileProvider配置错误:检查authorities是否与代码中一致,file_paths.xml的路径是否正确指向存储目录。
  • 系统裁剪不兼容:部分机型系统裁剪Intent参数有差异,可直接改用UCrop替代系统裁剪。

内容的提问来源于stack exchange,提问作者propeace

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 06:22:42