在 Android 中调用摄像头和相册是常见的功能需求,以下是详细的实现步骤和 Kotlin 示例代码,涵盖权限请求、拍照、选择图片、处理返回结果等关键环节。
1. 添加权限
在 AndroidManifest.xml
中添加以下权限:
xml
<!-- 摄像头权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 存储权限(读取相册) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 针对 Android 13+ 的媒体权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
注意:从 Android 10(API 29)开始,作用域存储(Scoped Storage)限制了直接访问外部存储的权限,需使用
MediaStore
API。
2. 动态权限请求
在 Activity
或 Fragment
中请求运行时权限:
kotlin
private val REQUEST_PERMISSION_CODE = 100
// 检查并请求权限
private fun checkPermissions() {
val permissions = mutableListOf<String>()
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.CAMERA)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.READ_MEDIA_IMAGES)
}
} else {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
if (permissions.isNotEmpty()) {
ActivityCompat.requestPermissions(this, permissions.toTypedArray(), REQUEST_PERMISSION_CODE)
} else {
// 权限已授予,执行操作
}
}
// 处理权限结果
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSION_CODE) {
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
// 权限已授予
} else {
Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show()
}
}
}
3. 调用摄像头拍照
步骤 1:创建临时文件
kotlin
private var photoFile: File? = null
private val REQUEST_TAKE_PHOTO = 101
private fun createImageFile(): File {
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${System.currentTimeMillis()}_",
".jpg",
storageDir
).apply {
photoFile = this
}
}
步骤 2:启动摄像头 Intent
kotlin
private fun takePhoto() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
photoFile = createImageFile()
photoFile?.let { file ->
val photoURI: Uri = FileProvider.getUriForFile(
this,
"${packageName}.fileprovider", // 需配置 FileProvider
file
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO)
}
}
}
步骤 3:配置 FileProvider
在 AndroidManifest.xml
中添加:
xml
<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
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="my_images" path="Pictures/" />
</paths>
4. 调用相册选择图片
kotlin
private val REQUEST_PICK_PHOTO = 102
private fun pickPhotoFromGallery() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "image/*"
startActivityForResult(intent, REQUEST_PICK_PHOTO)
}
5. 处理返回结果
在 onActivityResult
中处理拍照或相册返回的图片:
kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
REQUEST_TAKE_PHOTO -> {
// 从临时文件读取拍照结果
photoFile?.let { file ->
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
imageView.setImageBitmap(bitmap)
}
}
REQUEST_PICK_PHOTO -> {
// 从相册 URI 获取图片
data?.data?.let { uri ->
val inputStream = contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
imageView.setImageBitmap(bitmap)
}
}
}
}
}
6. 完整示例代码
kotlin
class MainActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
private var photoFile: File? = null
private val REQUEST_TAKE_PHOTO = 101
private val REQUEST_PICK_PHOTO = 102
private val REQUEST_PERMISSION_CODE = 100
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
imageView = findViewById(R.id.imageView)
findViewById<Button>(R.id.btnCamera).setOnClickListener {
checkPermissions()
takePhoto()
}
findViewById<Button>(R.id.btnGallery).setOnClickListener {
checkPermissions()
pickPhotoFromGallery()
}
}
// 动态权限检查和请求(见步骤 2)
// 拍照相关方法(见步骤 3)
// 相册选择方法(见步骤 4)
// 处理返回结果(见步骤 5)
}
关键注意事项
- FileProvider 配置:必须正确配置
FileProvider
,否则在 Android 7.0+ 会抛出FileUriExposedException
。 - 作用域存储:从 Android 10 开始,直接访问外部存储受限,需使用
MediaStore
API。 - 大图处理:加载大图时建议使用
Glide
或Picasso
等库,避免内存溢出(OOM)。 - 兼容性:针对不同 Android 版本调整权限请求逻辑(如 Android 13+ 的
READ_MEDIA_IMAGES
)。
通过上述代码,可以实现在 Android 应用中调用摄像头拍照和从相册选择图片的功能。