要在 Android 13 上实现图片选择、显示并添加裁剪功能,我们可以使用第三方裁剪库如 UCrop。以下是完整实现方案:
1. 添加依赖
在 build.gradle (Module) 中添加:
dependencies {
// 基础依赖
implementation 'androidx.activity:activity-ktx:1.7.0'
implementation 'androidx.fragment:fragment-ktx:1.5.7'
implementation 'com.github.bumptech.glide:glide:4.14.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
// 图片裁剪库
implementation 'com.github.yalantis:ucrop:2.2.8'
// 可选,用于处理Android 13+照片选择器
implementation 'androidx.activity:activity-ktx:1.7.0'
}
ImagePickerWithCropActivity.kt
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.bumptech.glide.Glide
import com.bumptech.glide.signature.ObjectKey
import com.yalantis.ucrop.UCrop
import smartconnection.com.smartconnect.R
import java.io.File
class ImagePickerWithCropActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
private lateinit var selectButton: Button
// 临时文件用于存储裁剪后的图片
private val cropFile by lazy {
File(externalCacheDir, "cropped_image.jpg")
}
// 图片选择启动器
private val pickImageLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { uri ->
// 启动裁剪
startCrop(uri)
}
}
}
// 裁剪结果启动器
private val cropImageLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.let { intent ->
UCrop.getOutput(intent)?.let { uri ->
// 显示裁剪后的图片
displayImage(uri)
}
}
} else if (result.resultCode == UCrop.RESULT_ERROR) {
result.data?.let {
val cropError = UCrop.getError(it)
Toast.makeText(this, "裁剪错误: ${cropError?.message}", Toast.LENGTH_SHORT).show()
}
}
}
// 权限请求启动器
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
openImagePicker()
} else {
Toast.makeText(
this,
"需要权限才能访问图片",
Toast.LENGTH_SHORT
).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_image_picker_crop)
imageView = findViewById(R.id.imageView)
selectButton = findViewById(R.id.selectButton)
selectButton.setOnClickListener {
checkPermissionAndPickImage()
}
}
private fun checkPermissionAndPickImage() {
// Android 13+使用READ_MEDIA_IMAGES,低版本使用READ_EXTERNAL_STORAGE
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
when {
ContextCompat.checkSelfPermission(
this,
permission
) == PackageManager.PERMISSION_GRANTED -> {
openImagePicker()
}
shouldShowRequestPermissionRationale(permission) -> {
Toast.makeText(
this,
"需要权限才能访问您的图片",
Toast.LENGTH_SHORT
).show()
requestPermissionLauncher.launch(permission)
}
else -> {
requestPermissionLauncher.launch(permission)
}
}
}
private fun openImagePicker() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
pickImageLauncher.launch(intent)
}
private fun startCrop(sourceUri: Uri) {
val destinationUri = Uri.fromFile(cropFile)
UCrop.of(sourceUri, destinationUri)
.withAspectRatio(1f, 1f) // 1:1比例
.withMaxResultSize(1000, 1000) // 最大尺寸
.withOptions(getCropOptions()) // 自定义选项
.start(this, cropImageLauncher)
}
private fun getCropOptions(): UCrop.Options {
val options = UCrop.Options()
options.setHideBottomControls(true) // 显示底部控制栏
options.setFreeStyleCropEnabled(true) // 允许自由裁剪
options.setStatusBarColor(ContextCompat.getColor(this, R.color.white))
options.setToolbarColor(ContextCompat.getColor(this, R.color.white))
options.setToolbarTitle("图片裁剪")
return options
}
private fun displayImage(uri: Uri) {
Glide.with(this)
.load(uri)
.signature(ObjectKey(System.currentTimeMillis().toString())) // 使用时间戳作为签名
.centerCrop()
.into(imageView)
}
}
activity_image_picker_crop.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">
<ImageView
android:id="@+id/imageView"
android:layout_width="300dp"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:background="#f0f0f0"
android:contentDescription="Cropped image" />
<Button
android:id="@+id/selectButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="选择并裁剪图片" />
</LinearLayout>
AndroidManifest.xml配置:
<!--相册-->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- 对于 Android 13 以下版本的回退权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
在 AndroidManifest.xml 中添加 UCrop 所需的配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="your.package.name">
<application>
<!-- 其他你的Activity声明 -->
<!-- UCrop 所需的Activity声明 -->
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
<!-- 其他配置 -->
</application>
</manifest>