CameraX图像捕获功能失效问题求助
CameraX拍照无法保存图片问题
我接触Android Studio仅一周,正通过CameraX开发简易相机应用,参考了Android官方CameraX入门教程。应用编译无报错,但点击拍照按钮后无法捕获并保存图片。已在AndroidManifest.xml中配置相机和存储权限,build.gradle.kts也添加了对应依赖,拍照按钮ID为imageCaptureButton。
Logcat错误日志
set:488 set: Unset optional value from type SMPTE2086 IMGMapper com.example.cameraxapp E set:488 set: Unset optional value from type SMPTE2086 IMGMapper com.example.cameraxapp E set:488 set: Unset optional value from type CTA861_3 set
MainActivity.kt代码
package com.example.cameraxapp import android.Manifest import android.content.ContentValues import android.content.pm.PackageManager import android.os.Build import android.os.Bundle import android.provider.MediaStore import androidx.appcompat.app.AppCompatActivity import androidx.camera.core.ImageCapture import androidx.camera.video.Recorder import androidx.camera.video.Recording import androidx.camera.video.VideoCapture import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.example.cameraxapp.databinding.ActivityMainBinding import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.core.Preview import androidx.camera.core.CameraSelector import android.util.Log import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageCaptureException import androidx.camera.core.ImageProxy import androidx.camera.video.FallbackStrategy import androidx.camera.video.MediaStoreOutputOptions import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import androidx.camera.video.VideoRecordEvent import androidx.core.content.PermissionChecker import java.nio.ByteBuffer import java.text.SimpleDateFormat import java.util.Locale typealias LumaListener = (luma: Double) -> Unit class MainActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding private var imageCapture: ImageCapture? = null private var videoCapture: VideoCapture<Recorder>? = null private var recording: Recording? = null private lateinit var cameraExecutor: ExecutorService override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Request camera permissions if (allPermissionsGranted()) { startCamera() } else { requestPermissions() } // Set up the listeners for take photo and video capture buttons viewBinding.imageCaptureButton.setOnClickListener { takePhoto() //Toast.makeText(this, "buttonpressed", Toast.LENGTH_SHORT).show() //Log.d(TAG,"button pressed") } //viewBinding.videoCaptureButton.setOnClickListener { captureVideo() } cameraExecutor = Executors.newSingleThreadExecutor() } private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions()) { permissions -> // Handle Permission granted/rejected var permissionGranted = true permissions.entries.forEach { if (it.key in REQUIRED_PERMISSIONS && it.value == false) permissionGranted = false } if (!permissionGranted) { Toast.makeText(baseContext, "Permission request denied", Toast.LENGTH_SHORT).show() } else { startCamera() } } private fun takePhoto() {// Get a stable reference of the modifiable image capture use case val imageCapture = imageCapture ?: return Toast.makeText(this, "inside takephoto", Toast.LENGTH_SHORT).show() // Create time stamped name and MediaStore entry. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image") } } // Create output options object which contains file + metadata val outputOptions = ImageCapture.OutputFileOptions .Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) .build() // Set up image capture listener, which is triggered after photo has // been taken 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){ val msg = "Photo capture succeeded: ${output.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } } )} private fun captureVideo() {} private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ // Used to bind the lifecycle of cameras to the lifecycle owner val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() // Preview val preview = Preview.Builder() .build() .also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } // Select back camera as a default val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA try { // Unbind use cases before rebinding cameraProvider.unbindAll() // Bind use cases to camera cameraProvider.bindToLifecycle( this, cameraSelector, preview) } catch(exc: Exception) { Log.e(TAG, "Use case binding failed", exc) } }, ContextCompat.getMainExecutor(this))} private fun requestPermissions() { activityResultLauncher.launch(REQUIRED_PERMISSIONS) } private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { ContextCompat.checkSelfPermission( baseContext, it) == PackageManager.PERMISSION_GRANTED } override fun onDestroy() { super.onDestroy() cameraExecutor.shutdown() } companion object { private const val TAG = "CameraXApp" private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private val REQUIRED_PERMISSIONS = mutableListOf ( Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO ).apply { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { add(Manifest.permission.WRITE_EXTERNAL_STORAGE) } }.toTypedArray() } }
应用界面

预期点击「拍照」按钮后,图片会保存到Pictures/CameraX-Image目录,但实际无图片生成,甚至不确定takePhoto函数是否执行。
解决方案
- 核心问题:
startCamera方法中未初始化并绑定ImageCapture用例,导致全局imageCapture变量始终为null,点击按钮时takePhoto函数第一行val imageCapture = imageCapture ?: return直接返回,后续逻辑完全没执行。 - 修复步骤:
- 在
startCamera方法中添加ImageCapture初始化代码:// 初始化ImageCapture用例 imageCapture = ImageCapture.Builder() .build() - 绑定用例时将
imageCapture加入绑定列表:cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture)
- 在
- 额外说明:Logcat中
SMPTE2086相关错误属于设备底层图像格式警告,和拍照保存失败无关,可忽略。
内容的提问来源于stack exchange,提问作者MarCoPolo
相关产品推荐
相关产品推荐

