Android Studio(Java)实现APP内前置摄像头自拍功能需求
Android 应用内实现前置摄像头自拍(无需跳转系统相机)
这个功能的官方叫法是自定义相机预览与拍摄,核心是直接调用系统相机硬件,在应用内的预览控件中显示画面并完成拍摄。以下是基于Java的完整实现步骤:
一、权限申请
首先要配置相机相关权限,Android 6.0及以上需要动态申请:
- 在
AndroidManifest.xml中添加静态权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.front" android:required="true" />
- 动态申请权限的代码:
private static final int REQUEST_CAMERA_PERMISSION = 100; private void requestCameraPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); } else { initCameraPreview(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CAMERA_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { initCameraPreview(); } else { Toast.makeText(this, "需要相机权限才能使用自拍功能", Toast.LENGTH_SHORT).show(); } } }
二、布局添加预览与拍摄控件
使用TextureView实现灵活的相机预览,搭配拍摄按钮:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextureView android:id="@+id/camera_preview" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/btn_capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="30dp" android:text="拍照" /> </RelativeLayout>
三、核心相机逻辑实现
完整的Activity代码,包含前置摄像头初始化、预览、拍照及照片处理:
public class SelfieCameraActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener { private Camera camera; private TextureView textureView; private Button captureBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_selfie_camera); textureView = findViewById(R.id.camera_preview); captureBtn = findViewById(R.id.btn_capture); captureBtn.setOnClickListener(v -> capturePhoto()); requestCameraPermission(); } private void initCameraPreview() { textureView.setSurfaceTextureListener(this); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // 打开前置摄像头 camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); try { Camera.Parameters parameters = camera.getParameters(); // 适配预览尺寸,避免变形 List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes(); Camera.Size optimalSize = getOptimalPreviewSize(previewSizes, width, height); parameters.setPreviewSize(optimalSize.width, optimalSize.height); camera.setParameters(parameters); // 绑定预览到TextureView camera.setPreviewTexture(surface); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); releaseCamera(); } } // 计算适配控件的最佳预览尺寸 private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) h / w; Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; for (Camera.Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } private void capturePhoto() { if (camera != null) { camera.takePicture(null, null, (data, camera) -> { // 解析照片数据 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); // 前置摄像头拍摄的照片是镜像,翻转后恢复正常视角 bitmap = flipBitmapHorizontally(bitmap); // 这里可添加保存照片、设置为头像的逻辑 // saveBitmapToLocal(bitmap); // updateUserAvatar(bitmap); // 重启预览,支持再次拍摄 camera.startPreview(); }); } } // 水平翻转Bitmap private Bitmap flipBitmapHorizontally(Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.setScale(-1, 1); return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { releaseCamera(); return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) {} // 释放相机资源 private void releaseCamera() { if (camera != null) { camera.stopPreview(); camera.release(); camera = null; } } @Override protected void onDestroy() { super.onDestroy(); releaseCamera(); } }
四、额外注意事项
- 上述代码基于旧版
CameraAPI,若适配Android 5.0+(API 21+),推荐使用功能更完善的camera2API,但其代码复杂度更高。 - 可在清单文件中锁定该Activity为竖屏,避免旋转导致预览变形:
<activity android:name=".SelfieCameraActivity" android:screenOrientation="portrait" />
内容的提问来源于stack exchange,提问作者Rocky Malobago
相关产品推荐
相关产品推荐

