Android开发实战:基于Camera API构建自定义相机应用教程

引言

在移动互联网时代,相机应用已成为智能手机的标配功能之一。无论是拍照、录像还是实时滤镜,相机应用都为用户提供了丰富的视觉体验。作为一名Android开发者,掌握Camera API的使用是必不可少的一项技能。本文将带你一步步构建一个基于Camera API的自定义相机应用,从基础设置到高级功能,让你全面掌握相机开发的精髓。

一、准备工作

1.1 创建项目

首先,打开Android Studio,创建一个新的Android项目。选择“Empty Activity”模板,命名为“CustomCameraApp”。

1.2 添加权限

AndroidManifest.xml文件中,添加必要的相机权限和存储权限:

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

<uses-feature android:name="android.hardware.camera" android:required="true" />

1.3 请求运行时权限

MainActivity中,请求运行时权限:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
}

二、布局设计

2.1 创建布局文件

res/layout目录下创建activity_main.xml,设计相机预览界面:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/btnCapture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Capture"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"/>
</RelativeLayout>

三、相机预览

3.1 初始化相机

MainActivity中,初始化相机和SurfaceView:

private Camera camera;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    surfaceView = findViewById(R.id.surfaceView);
    surfaceHolder = surfaceView.getHolder();
    surfaceHolder.addCallback(new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            camera = Camera.open();
            try {
                camera.setPreviewDisplay(holder);
                camera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            Camera.Parameters parameters = camera.getParameters();
            parameters.setPreviewSize(width, height);
            camera.setParameters(parameters);
            camera.startPreview();
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            if (camera != null) {
                camera.stopPreview();
                camera.release();
                camera = null;
            }
        }
    });
}

四、拍照功能

4.1 捕获图片

MainActivity中,添加按钮点击事件,实现拍照功能:

Button btnCapture = findViewById(R.id.btnCapture);
btnCapture.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (camera != null) {
            camera.takePicture(null, null, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    saveImageToStorage(data);
                    camera.startPreview();
                }
            });
        }
    }
});

4.2 保存图片

private void saveImageToStorage(byte[] data) {
    File pictureFile = getOutputMediaFile();
    if (pictureFile == null) {
        return;
    }
    try {
        FileOutputStream fos = new FileOutputStream(pictureFile);
        fos.write(data);
        fos.close();
        Toast.makeText(this, "Image saved: " + pictureFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private File getOutputMediaFile() {
    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "CustomCameraApp");
    if (!mediaStorageDir.exists()) {
        if (!mediaStorageDir.mkdirs()) {
            return null;
        }
    }
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    return new File(mediaStorageDir.getPath() + File.separator +
            "IMG_" + timeStamp + ".jpg");
}

五、高级功能

5.1 切换摄像头

在布局中添加切换摄像头的按钮,并在MainActivity中实现切换功能:

<Button
    android:id="@+id/btnSwitchCamera"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Switch Camera"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="20dp"/>
Button btnSwitchCamera = findViewById(R.id.btnSwitchCamera);
btnSwitchCamera.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (camera != null) {
            camera.stopPreview();
            camera.release();
            camera = null;
        }
        if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
            currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
        } else {
            currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
        }
        camera = Camera.open(currentCameraId);
        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});

5.2 添加滤镜

使用OpenCV或其他图像处理库,为相机预览添加滤镜效果。这里以简单的灰度滤镜为例:

private void applyGrayScaleFilter(byte[] data) {
    Mat rgba = new Mat(height, width, CvType.CV_8UC4);
    rgba.put(0, 0, data);
    Mat gray = new Mat(height, width, CvType.CV_8UC1);
    Imgproc.cvtColor(rgba, gray, Imgproc.COLOR_RGBA2GRAY);
    byte[] grayData = new byte[width * height];
    gray.get(0, 0, grayData);
    saveImageToStorage(grayData);
}

onPictureTaken回调中调用applyGrayScaleFilter方法:

camera.takePicture(null, null, new Camera.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        applyGrayScaleFilter(data);
        camera.startPreview();
    }
});

六、总结

七、参考资料

  1. Android官方文档 - Camera API
  2. OpenCV官方文档
  3. Android运行时权限处理

祝你在Android开发的道路上越走越远,创造出更多精彩的应用!