Android如何写一个拍照页面

引言

在Android中,拍照功能被广泛应用于各种应用程序中,如社交媒体、相机应用、扫描应用等。本文将介绍如何在Android应用程序中编写一个功能完善的拍照页面。

准备工作

在开始编写代码之前,我们需要确保项目中已经添加了相机权限。在AndroidManifest.xml文件中添加以下权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

同时,还需要在build.gradle文件中添加以下依赖:

implementation 'androidx.camera:camera-core:1.0.0'
implementation 'androidx.camera:camera-camera2:1.0.0'
implementation 'androidx.camera:camera-lifecycle:1.0.0'
implementation 'androidx.camera:camera-view:1.0.0'

创建拍照页面布局

首先,我们需要创建一个拍照页面的布局文件。在res/layout目录下创建一个名为activity_camera.xml的文件,并添加以下代码:

<androidx.camera.view.PreviewView
    android:id="@+id/viewFinder"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<Button
    android:id="@+id/captureButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:text="拍照" />

在这个布局中,我们使用了PreviewView来显示相机预览界面,并添加了一个Button来触发拍照操作。

初始化相机

接下来,我们需要在拍照页面的Activity中初始化相机。在Activity的onCreate方法中添加以下代码:

private lateinit var cameraProvider: ProcessCameraProvider

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera)
    
    // 获取相机Provider
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    cameraProviderFuture.addListener(Runnable {
        cameraProvider = cameraProviderFuture.get()
        bindCameraUseCases()
    }, ContextCompat.getMainExecutor(this))
}

在这段代码中,我们使用ProcessCameraProvider.getInstance(this)来获取相机Provider,并在监听器中异步获取的到的相机Provider对象。

配置相机用例

在初始化相机后,我们需要配置相机用例,以便进行预览和拍照操作。添加以下代码:

private fun bindCameraUseCases() {
    val viewFinder = findViewById<PreviewView>(R.id.viewFinder)
    val preview = Preview.Builder().build().also {
        it.setSurfaceProvider(viewFinder.surfaceProvider)
    }

    val imageCapture = ImageCapture.Builder().build()
    findViewById<Button>(R.id.captureButton).setOnClickListener {
        val file = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
        val outputOptions = ImageCapture.OutputFileOptions.Builder(file).build()
        imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageSavedCallback {
                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    val savedUri = Uri.fromFile(file)
                    val msg = "图片已保存至: $savedUri"
                    Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
                }
                override fun onError(exception: ImageCaptureException) {
                    val msg = "拍照失败: ${exception.message}"
                    Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
                }
            }
        )
    }

    cameraProvider.unbindAll()
    cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageCapture)
}

在这段代码中,我们首先通过findViewById获取到PreviewView对象,并创建了一个Preview用例,并将其与PreviewView关联起来。

然后,我们创建了一个ImageCapture用例,并在拍照按钮的点击事件中调用了takePicture方法来触发拍照操作。在拍照完成后,我们可以通过onImageSaved回调中的outputFileResults参数获取到保存的图片文件,并显示一个Toast提示用户图片保存的路径。

最后,我们使用cameraProvider.unbindAll()来解绑之前绑定的用例,并通过cameraProvider.bindToLifecycle将Preview、ImageCapture用例与相机生命周期绑定。

请求相机权限

为了保证应用程序能够正常访问相机,我们还需要在Activity中请求相机