Android 自定义Camera(二)

1,212

通过上一篇 Android 自定义Camera(一) 的介绍,我们已经可以完成一个可拍照的基础demo, 当然其中也有很多要注意的细节问题,比如预览方向,设置预览比例和返回图片的比例等等问题,详情可以看上一篇博客,这次我会丰富相机的功能加入 闪光灯模式,正方形拍摄,延迟拍摄和前后摄像头切换功能,接下来就一个一个开始吧。(下面会有完整源码地址

闪光灯模式

相机的闪光灯模式有三种, 自动,关闭, 打开, 代码也 比较好理解,在拍照之前切换调用就可以,个别地方加了注释,这里我贴上代码: 1 关闭情况:

/**
     * 关闭闪光灯
     *
     * @param mCamera
     */
    public void turnLightOff(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        //获取手机支持的模式
        List<String> flashModes = parameters.getSupportedFlashModes();
        String flashMode = parameters.getFlashMode();
        // Check if camera flash exists
        if (flashModes == null) {
            return;
        }
        if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
            // Turn off the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }

2 自动模式:

/**
     * 自动模式闪光灯
     *
     * @param mCamera
     */
    public void turnLightAuto(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        // Check if camera flash exists
        if (flashModes == null) {
            // Use the screen as a flashlight (next best thing)
            return;
        }
        String flashMode = parameters.getFlashMode();
        if (!Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }

3 关闭模式

/**
     * 打开闪关灯
     *
     * @param mCamera
     */
    public void turnLightOn(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        // Check if camera flash exists
        if (flashModes == null) {
            // Use the screen as a flashlight (next best thing)
            return;
        }
        String flashMode = parameters.getFlashMode();
        if (!Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }

正方形拍摄

这里我要说一下我怎么做的,目前我认为 由于我们取景框的比例必须满足手机支持的尺寸,还有surfaceView和预览尺寸的比例也要一样,这样其实我们并不能确定取景框的大小具体是多少,所以我实现正方形拍摄用的是 遮盖取景框 的方式, 拍摄的时候将取景框遮住让其成正方形, 在返回图片的时候再将其裁剪成正方形即可, 目前在正式项目中我也在这样用,因为很多手机都直接支持正方形尺寸, 这里我用了俩个属性动画来遮盖取景框, 代码如下, 这里主要是计算一下要遮住的高度

/**
     * 正方形拍摄
     */
    public void camera_square_0() {
        camera_square.setImageResource(R.drawable.btn_camera_size1_n);

        //属性动画
        ValueAnimator anim = ValueAnimator.ofInt(0, animHeight);
        anim.setDuration(300);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int currentValue = Integer.parseInt(animation.getAnimatedValue().toString());
                RelativeLayout.LayoutParams Params = new RelativeLayout.LayoutParams(screenWidth, currentValue);
                Params.setMargins(0, SystemUtils.dp2px(context, 44), 0, 0);
                homeCustom_cover_top_view.setLayoutParams(Params);

                RelativeLayout.LayoutParams bottomParams = new RelativeLayout.LayoutParams(screenWidth, currentValue);
                bottomParams.setMargins(0, screenHeight - menuPopviewHeight - currentValue, 0, 0);
                homeCustom_cover_bottom_view.setLayoutParams(bottomParams);
            }

        });
        anim.start();

        homeCustom_cover_top_view.bringToFront();
        home_custom_top_relative.bringToFront();
        homeCustom_cover_bottom_view.bringToFront();
        index++;
    }

    /**
     * 长方形方形拍摄
     */
    public void camera_square_1() {
        camera_square.setImageResource(R.drawable.btn_camera_size2_n);

        ValueAnimator anim = ValueAnimator.ofInt(animHeight, 0);
        anim.setDuration(300);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int currentValue = Integer.parseInt(animation.getAnimatedValue().toString());
                RelativeLayout.LayoutParams Params = new RelativeLayout.LayoutParams(screenWidth, currentValue);
                Params.setMargins(0, SystemUtils.dp2px(context, 44), 0, 0);
                homeCustom_cover_top_view.setLayoutParams(Params);

                RelativeLayout.LayoutParams bottomParams = new RelativeLayout.LayoutParams(screenWidth, currentValue);
                bottomParams.setMargins(0, screenHeight - menuPopviewHeight - currentValue, 0, 0);
                homeCustom_cover_bottom_view.setLayoutParams(bottomParams);
            }
        });
        anim.start();
        index = 0;
    }

延迟拍摄

先说一下延迟拍摄的原理, 其实实现方式也不止一种的,这里我使用了线程加handler的方式,核心代码如下,先是记录了一个延迟时间,然后递减这个时间每隔1s, 最后它等于0的时候执行拍摄代码, 效果我就不贴啦,可以下载代码运行看, 就是一个大大的数字在屏幕上读秒

new Thread(new Runnable() {
                            @Override
                            public void run() {
                                while (delay_time > 0) {
                                    //按秒数倒计时
                                    try {
                                        Thread.sleep(1000);
                                    } catch (InterruptedException e) {
                                        mHandler.sendEmptyMessage(AppConstant.WHAT.ERROR);
                                        return;
                                    }
                                    delay_time--;
                                    mHandler.sendEmptyMessage(AppConstant.WHAT.SUCCESS);
                                }
                            }
                        }).start();

切换前置后置摄像头

几乎所有的相机程序都需要这个功能,必须要能前置和后置啊,那这个其实实现起来也比较的简单,当相机正在预览过程中进行前后置切换的时候一定要先释放相机资源, 然后再开启预览, 切换摄像头无非就是切换cameraId, 这里先看下代码:

 public void  switchCamera() {
        releaseCamera();
        //这个方法getNumberOfCameras()是从API Level 9 引入的, 取得摄像头个数 一般的正常手机只有一个摄像头, 这里我也先按照一个摄像头处理了
        mCameraId = (mCameraId + 1) % mCamera.getNumberOfCameras();
        mCamera = getCamera(mCameraId);
        if (mHolder != null) {
            startPreview(mCamera, mHolder);
        }
    }

加水印

顺便也说一下加水印的功能吧, 加水印功能上实现起来不难,难得是要画好看的水印哈哈, 其实加水印也是在拍照返回的bitmap上加入的, 其实就是用Canvas类画上去,核心代码如下, 假设拍照返回的图为bitmap:

Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(tempbitmap, left, top, paint);

代码大体就是这样,通过canvas.drawBitmap方法画上去的, 这个当然就随意的画了, 记录预览时水印的位置,然后画上去妥妥的。

OK, 这篇博客就到这里了, 接下来我会研究Camera2, 听说增加了好玩的功能。

下面是源码地址:

github.com/jinguangyue… Star一下不亏.