目录

使用系统相机拍摄照片      

使用Camera和SurfaceView自行实现

         对于手机拍照的APP而言,有两种实现方式,一种是借助系统相机来拍摄照片,另一种是通过Camera工具联合表面视图SurfaceView自行规划编码细节。两者有各自的优缺点,可以按需选择。

使用系统相机拍摄照片      

利用系统相机拍摄照片有其优势以及缺点,具体说明如下:

优点:

  1. 开发简单:只需要几行代码就可以启动系统相机应用,无需处理复杂的相机操作。

  2. 稳定性高:系统相机经过长期测试和优化,通常比自定义相机更稳定。

  3. 功能丰富:系统相机通常提供多种拍摄模式、滤镜和设置,用户熟悉且可以直接使用。

  4. 兼容性好:不同设备上的系统相机会处理硬件和驱动的差异,避免了兼容性问题。

缺点:

  1. 定制性差:无法自定义相机界面和功能,只能使用系统提供的界面。

  2. 依赖系统:如果设备上没有相机应用,或者相机应用出现故障,则无法使用。

  3. 返回数据可能受限:返回的图片可能是缩略图或者经过压缩的图片(如果没有指定输出文件)。

        获取系统相机拍摄的照片有两种方式,一个是获取缩略图,另一个是保存拍摄的照片后读取该照片,此时获取的照片为原始图片。示例代码如下,在布局文件中添加两个按钮以及一个文本视图、一个图片视图。



    

        部分Java代码如下,主要看两个活动结果启动器的内容以及方法takeOriginalPhoto的内容,第一个启动器返回的是一个Bitmap类型的数据,可以直接将Bitmap赋给ImageView;第二个启动器返回的是一个布尔类型,表示是否拍照成功,若成功则可以通过在takeOriginalPhoto方法中获取的Uri来打开已保存的图片并将其转化为Bitmap类型赋给ImageView,示例代码中的图片保存在系统相册中,可以在系统相册中找到拍摄的照片。如果只是需要简单的图片那么第一种方式已经足够了,代码量少且简单实现。

public class SnapBySystemCameraActivity extends AppCompatActivity implements View.OnClickListener {
    private final String TAG = "SnapBySystemCamera";
    private Button button_Snap, button_Snap_Origin;
    private TextView textView;
    private ImageView imageView;
    private ActivityResultLauncher activityResultLauncher_Original;     //活动结果启动器(原始图)
    private ActivityResultLauncher activityResultLauncher_Thumbnail;    //活动结果启动器(缩略图)
    private Uri mImageUri;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_snap_by_system_camera);
        initView();
    }
    //初始化页面视图
    private void initView() {
        button_Snap = findViewById(R.id.button_Snap);
        button_Snap_Origin = findViewById(R.id.button_Snap_Origin);
        textView = findViewById(R.id.textView);
        imageView = findViewById(R.id.imageView);
        // 注册一个善后工作的活动结果启动器,准备打开拍照界面(返回缩略图)
        activityResultLauncher_Thumbnail = registerForActivityResult(new ActivityResultContracts.TakePicturePreview(),
                bitmap_Thumbnail -> imageView.setImageBitmap(bitmap_Thumbnail));
        // 注册一个善后工作的活动结果启动器,准备打开拍照界面(返回原始图)
        activityResultLauncher_Original = registerForActivityResult(new ActivityResultContracts.TakePicture(),
                if_success -> {
                    if (if_success) {
                        try (InputStream inputStream = getContentResolver().openInputStream(mImageUri)) {
                            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                            textView.setText("原始图拍照结果如下:");
                            imageView.setImageBitmap(bitmap); // 设置图像视图的位图对象
                        } catch (Exception e) {
                            Log.d(TAG, "onActivityResult: " + e.getMessage());
                        }
                    }
                });
        button_Snap.setOnClickListener(this);
        button_Snap_Origin.setOnClickListener(this);
    }
    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.button_Snap) {
            Log.d(TAG, "onClick: button_Snap");
            activityResultLauncher_Thumbnail.launch(null);
        } else if (view.getId() == R.id.button_Snap_Origin) {
            Log.d(TAG, "onClick: button_Snap_Origin");
            takeOriginalPhoto();
        }
    }
    // 拍照时获取原始图片
    private void takeOriginalPhoto() {
        // Android10开始必须由系统自动分配路径,同时该方式也能自动刷新相册
        ContentValues values = new ContentValues();
        Calendar calendar = Calendar.getInstance();
        String time = String.format("%s%s%s%s", calendar.get(Calendar.HOUR_OF_DAY),
                calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND), calendar.get(Calendar.MILLISECOND));
        // 指定图片文件的名称
        values.put(MediaStore.Images.Media.DISPLAY_NAME, "photo_" + time);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // 类型为图像
        // 通过内容解析器插入一条外部内容的路径信息
        mImageUri = getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        activityResultLauncher_Original.launch(mImageUri);
    }
}

        效果图如下,第一张为刚进入页面,第二张为拍摄获取缩略图,第三张为拍摄获取原始图。可以看到两张照片之间大小是不一致的,第一张若放大到与第二张一样的宽高,它的效果会更加模糊。

使用Camera和SurfaceView自行实现

        利用Camera以及SurfaceView实现拍照的优缺点如下说明:

优点

  1. 高度定制:可以完全自定义相机界面和拍摄流程,满足特定业务需求。

  2. 控制精细:可以控制相机的各种参数(如对焦、曝光、闪光灯等)和拍摄过程。

  3. 无缝体验:可以将相机功能完全集成到应用中,提供一致的用户体验。

缺点

  1. 开发复杂:需要处理相机的打开、预览、参数设置、拍照、释放等整个生命周期,代码量大。

  2. 兼容性挑战:不同设备的相机硬件和驱动可能有差异,需要处理各种兼容性问题。

  3. 稳定性风险:自行管理的相机可能因为操作不当导致崩溃或性能问题。

  4. 功能实现成本高:如变焦、美颜、滤镜等功能都需要自行开发或集成第三方库。

        下面的示例代码是比较简单的,若想要学习更加高深的可以另找其他厉害的博主,作者也才刚学不久。示例代码如下,在布局文件中添加一个按钮、一个SurfaceView以及一个ImageView。



    

        部分Java代码如下,主要看方法initCamera以及按钮监听器中的逻辑处理代码。

public class CameraWithSurfaceViewActivity extends AppCompatActivity {
    private SurfaceView surfaceView;
    private Button button_Snap;
    private ImageView imageView;
    private SurfaceHolder surfaceHolder;
    private Camera camera;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_with_surface_view);
        initView();
    }
    private void initView() {
        button_Snap = findViewById(R.id.button_Snap);
        imageView = findViewById(R.id.imageView);
        surfaceView = findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
                initCamera();
            }
            @Override
            public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {}
            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {}
        });
        button_Snap.setOnClickListener(view -> camera.takePicture(null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] bytes, Camera camera) {
                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                imageView.setImageBitmap(bitmap);
            }
        }));
    }
    private void initCamera() {
        // 初始化 Camera,0 通常代表后置摄像头
        camera = Camera.open(0);
        // 调整摄像头预览角度,例如设置为90度
        camera.setDisplayOrientation(90);
        // 设置SurfaceView不维护自己的缓冲区
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        // 将Camera的预览输出与SurfaceView关联
        try {
            camera.setPreviewDisplay(surfaceHolder);
        } catch (IOException e) {
            Log.d(TAG, "initializeCamera: " + e.getMessage());
        }
        camera.startPreview(); // 开始预览
    }
}

        效果图如下,图片中第一个图片为SurfaceView,第二个为ImageView。