Android Camera拍照获取Bitmap完整实现
本文提供一个完整的Camera拍照并返回Bitmap的实现方案,包含权限处理、相机预览和照片捕获流程。
1、基本准备
在AndroidManifest.xml中添加权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<!-- 如果需要保存文件 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />2、Activity/Fragment实现代码
关键点说明:使用了最新的CameraX API,比传统Camera API更简单易用。完整的权限处理流程,包括运行时权限检查和申请
class CameraActivity : AppCompatActivity() {
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
private lateinit var imageCapture: ImageCapture
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
// 检查相机权限
if (!hasPermissions(this, REQUIRED_PERMISSIONS)) {
requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
} else {
startCamera()
}
btn_take_photo.setOnClickListener {
takePhoto()
}
}
companion object {
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
private const val REQUEST_CODE_PERMISSIONS = 1001
fun hasPermissions(context: Context, permissions: Array<String>) =
permissions.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
}
private fun startCamera() {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// 创建预览用例
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(preview_view.surfaceProvider)
}
// 创建图像捕捉用例
imageCapture = ImageCapture.Builder()
.setTargetRotation(windowManager.defaultDisplay.rotation)
.build()
try {
// 解绑之前的所有绑定
cameraProvider.unbindAll()
// 将用例绑定到生命周期
cameraProvider.bindToLifecycle(
this,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageCapture
)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun takePhoto() {
// 创建临时文件存储照片
val photoFile = File(
externalMediaDirs.first(),
"${System.currentTimeMillis()}.jpg"
)
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
// 从文件中读取Bitmap
val bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
runOnUiThread {
// 在这里处理bitmap,例如显示在ImageView中
imageView.setImageBitmap(bitmap)
// 也可以直接使用内存中的bitmap对象进行其他操作
processCapturedBitmap(bitmap)
}
}
})
}
private fun processCapturedBitmap(bitmap: Bitmap) {
// 这里可以对bitmap进行处理
Toast.makeText(this, "成功获取${bitmap.width}x${bitmap.height}的照片",
Toast.LENGTH_SHORT).show()
}
@SuppressLint("MissingSuperCall")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted(grantResults)) {
startCamera()
} else {
Toast.makeText(this, "未授予所需权限", Toast.LENGTH_SHORT).show()
finish()
}
}
}
private fun allPermissionsGranted(grantResults: IntArray) =
grantResults.isNotEmpty() &&
grantResults.all { it == PackageManager.PERMISSION_GRANTED }
}两种Bitmap获取方式:
直接从保存的文件解码
可以直接转换ImageProxy为Bitmap(如需内存中处理)
3、XML布局文件 (activity_camera.xml)
旋转处理:根据设备方向自动调整照片方向
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 相机预览视图 -->
<androidx.camera.view.PreviewView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 拍照按钮 -->
<Button
android:id="@+id/btn_take_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:text="拍摄照片" />
<!-- 显示结果的ImageView -->
<ImageView
android:id="@+id/imageView"
android:layout_width="300dp"
android:layout_height="400dp"
android:layout_gravity="top|end"
android:visibility="gone"/>
</FrameLayout>如果要直接在内存中处理而不保存文件,可以使用以下替代方案:
imageCapture.takePicture(ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val bitmap = image.toBitmap()
// 处理bitmap...
image.close()
}
})注意:记得在应用的build.gradle中添加CameraX依赖:
def camerax_version = "1.3.0-alpha04"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:$camerax_version"
