Android 最简单的自定义证件照Mask之一

做者: Jooyer, 时间: 2019.01.08

天星

Github地址,欢迎点赞,forkphp

如今部分APP都有一个身份认证,通常须要身份证正面,反面,在度娘那也有不少,我发现他们在属性配置上略少了一些,因此特地写了一个!,四周线条颜色,间距,粗细都有属性设置!java

本次代码也是比较简单,就一个系统的 SurfaceView 和 一个自定义的View, 我就直接贴源码,若是还有不清楚的,能够留言或者看githubandroid

接下来咱们依次讲解:

  1. CameraSufaceView
  2. CameraMaskView
  3. 属性及默认值

首先,看看 CameraSufaceView,必要的注释都有

package cn.molue.jooyer.masker;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Environment;
import android.support.constraint.ConstraintLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;

/** * @Date 2018/12/29 * @Desc 相机预览界面 */
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {

    private static final String TAG = "CameraSurfaceView";

    private SurfaceHolder holder;
    private Camera mCamera;
    private Rect mCenterMarkRect;
    private int mScreenWidth;
    private int mScreenHeight;

    private File mFile;
    private OnPathChangedListener onPathChangedListener;


    public void setOnPathChangedListener(OnPathChangedListener onPathChangedListener) {
        this.onPathChangedListener = onPathChangedListener;
    }

    public CameraSurfaceView(Context context) {
        this(context, null);
    }

    public CameraSurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        getScreenMetrix(context);
        initView();
    }

    //拿到手机屏幕大小
    private void getScreenMetrix(Context context) {
        mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
        mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;

    }

    private void initView() {
        //得到surfaceHolder引用
        holder = getHolder();
        // 屏幕常亮
        holder.setKeepScreenOn(true);
        //为SurfaceView的句柄添加一个回调函数
        holder.addCallback(this);

// holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置类型
    }

    // 在surface建立后当即被调用。在开发自定义相机时,能够经过重载这个函数调用camera.open()、camera.setPreviewDisplay(),
    // 来实现获取相机资源、链接camera和surface等操做
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i(TAG, "surfaceCreated");
        if (mCamera == null) {
            mCamera = Camera.open();
            try {
                // 设置用于显示拍照影像的SurfaceHolder对象
                mCamera.setPreviewDisplay(holder);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    // 能够经过重载这个函数调用camera.startPreview来开启相机预览,使得camera预览帧数据能够传递给surface,从而实时显示相机预览图像
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        setCameraParams(mScreenWidth, mScreenHeight);
        mCamera.startPreview();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();//中止预览
        mCamera.release();//释放相机资源
        mCamera = null;
    }

    @Override
    public void onAutoFocus(boolean success, Camera Camera) {
        
    }


    private void setCameraParams(int width, int height) {
        Log.i(TAG, "------setCameraParams width=" + width + " height=" + height);
        Camera.Parameters parameters = mCamera.getParameters();
        // 获取摄像头支持的PictureSize列表
        List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
        /**从列表中选取合适的分辨率*/
        Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
        if (null == picSize) {
            Log.i(TAG, "null == picSize");
            picSize = parameters.getPictureSize();
        }

        // 根据选出的PictureSize从新设置SurfaceView大小
        float w = picSize.width;
        float h = picSize.height;
        Log.i(TAG, "----------picSize.width=" + picSize.width + " --- picSize.height=" + picSize.height);
        // 设置捕获图片尺寸
        parameters.setPictureSize(picSize.width, picSize.height);
        this.setLayoutParams(new ConstraintLayout.LayoutParams((int) (height * (h / w)), height));

        // 获取摄像头支持的PreviewSize列表
        List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
        Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
        if (null != preSize) {
            Log.i(TAG, "----------preSize.width=" + preSize.width + " --- preSize.height=" + preSize.height);
            // 设置预览图片尺寸
            parameters.setPreviewSize(preSize.width, preSize.height);
        }

        parameters.setJpegQuality(100); // 设置照片质量
        if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式
        }

        mCamera.cancelAutoFocus();//自动对焦。
        mCamera.setParameters(parameters);

        // 计算摄像头方向
        Camera.CameraInfo info =
                new Camera.CameraInfo();
        Camera.getCameraInfo(0, info);
        int rotation = ((Activity) getContext()).getWindowManager().getDefaultDisplay()
                .getRotation();
        int degrees = 0;

        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        // 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示
        mCamera.setDisplayOrientation(result);

    }


    /** * 从列表中选取合适的分辨率 * 默认w:h = 4:3 * <p>注意:这里的w对应屏幕的height * h对应屏幕的width<p/> */
    private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) {
        Log.i(TAG, "screenRatio=" + screenRatio);
        Camera.Size result = null;
        for (Camera.Size size : pictureSizeList) {
            float currentRatio = ((float) size.width) / size.height;
            if (currentRatio - screenRatio == 0) {
                result = size;
                break;
            }
        }

        if (null == result) {
            for (Camera.Size size : pictureSizeList) {
                float curRatio = ((float) size.width) / size.height;
                if (curRatio == 4f / 3) {// 默认w:h = 4:3
                    result = size;
                    break;
                }
            }
        }

        return result;
    }


    // 拍照瞬间调用 (添加这个才会有咔嚓声)
    private Camera.ShutterCallback shutter = new Camera.ShutterCallback() {
        @Override
        public void onShutter() {
        }
    };

    // 得到没有压缩过的图片数据
    private Camera.PictureCallback raw = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera Camera) {
        }
    };

    //建立jpeg图片回调数据对象
    private Camera.PictureCallback jpeg = new Camera.PictureCallback() {

        private Bitmap bitmap;

        @Override
        public void onPictureTaken(byte[] data, Camera Camera) {

            BufferedOutputStream bos = null;
            Bitmap bm = null;
            if (data != null) {

            }

            try {
                // 得到图片
                bm = BitmapFactory.decodeByteArray(data, 0, data.length);

                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

// 图片存储前旋转
                    Matrix m = new Matrix();
                    int height = bm.getHeight();
                    int width = bm.getWidth();
                    m.setRotate(90);
                    //旋转后的图片
                    bitmap = Bitmap.createBitmap(bm, 0, 0, width, height, m, true);
                    
                    String photo = "IMMQY/IMG_" + String.valueOf(new Date().getTime() + ".jpg");
                    mFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath(), photo);
                    if (!mFile.getParentFile().exists()) {
                        mFile.getParentFile().mkdirs();
                    }
                    bos = new BufferedOutputStream(new FileOutputStream(mFile));
                    if (null == mCenterMarkRect) {
                        throw new NullPointerException("mCenterMarkRect is not null");
                    }
                    Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap,
                            mScreenWidth, mScreenHeight, true);
                    // 截取
                    bm = Bitmap.createBitmap(sizeBitmap,
                            mCenterMarkRect.left, mCenterMarkRect.top,
                            mCenterMarkRect.right - mCenterMarkRect.left,
                            mCenterMarkRect.bottom - mCenterMarkRect.top);

                    bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);//将图片压缩到流中
                } else {
                    Toast.makeText(getContext(), "没有检测到内存卡", Toast.LENGTH_SHORT).show();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.flush();//输出
                    bos.close();//关闭
                    bm.recycle();// 回收bitmap空间
                    mCamera.stopPreview();// 关闭预览
                    mCamera.startPreview();// 开启预览
                    if (onPathChangedListener != null) {
                        onPathChangedListener.onValueChange(mFile.getPath());
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    };

    /** * 拍照 */
    public void takePicture() {
        //设置参数,并拍照
        setCameraParams(mScreenWidth, mScreenHeight);
        // 当调用camera.takePiture方法后,camera关闭了预览,这时须要调用startPreview()来从新开启预览
        mCamera.takePicture(shutter, null, jpeg);
    }


    public void setAutoFocus() {
        mCamera.autoFocus(this);
    }

    public void setCenterMarkRect(Rect centerMarkRect) {
        this.mCenterMarkRect = centerMarkRect;
    }

    public interface OnPathChangedListener {
        void onValueChange(String path);
    }

}

复制代码

而后咱们来瞧瞧 CameraMaskView ,其实这里就是将四周挡住,留下中间位置做为拍照的具体内容

package cn.molue.jooyer.masker;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;


/** * @Date 2018/12/29 * @Time 11:37 */
public class CameraMaskView extends View {
    // 默认若是方框布局中时距离上面尺寸
    private static final int MASK_MARGIN = 100;
    // 默认线宽
    private static final int LINE_WIDTH = 5;
    // 默认线长
    private static final int LINE_LENGTH = 40;
    // 默认线外边距
    private static final int LINE_MARGIN = 10;
    // 默认线内边距
    private static final int LINE_PADDING = 5;
    // 默认提示文本
    private static final String TEXT = "请将身份证置于此框内";

    // 控件的宽高
    private int viewWidth;
    private int viewHeight;

    // 中间透明区域(方框)宽高
    public int rectWidth;
    public int rectHeight;
    // 提示文本
    private String mTipText = "";
    // 文本颜色
    private int mTipTextColor = 0;
    // 文本大小
    private int mTipTextSize = 0;
    // 文本距离底部 遮罩框 间隔
    private int mTipMargin = 0;

    // 中间透明区域(方框)坐标
    private Rect mCenterMarkRect = new Rect();
    // 遮罩颜色
    private int mMaskColor = 0;
    // 遮罩是居中,仍是居上
    private int mMaskPosition = 0;

    private int mMaskMargin = 0;

    // 四周线条长度
    private int mLineLength;
    // 四周线条宽度(厚度)
    private int mLineWidth;

    private int mLineColor = 0;

    private int mLinePadding = 0;

    private int mLineMargin = 0;

    // 文字画笔
    private Paint maskPaint;
    // 线条画笔
    private Paint linePaint;
    // 文字画笔
    private Paint wordPaint;
    // 计算提示文本的位置的
    private Rect rect;
    // 文本基线
    private int baseline;

    public CameraMaskView(Context context, AttributeSet attrs) {
        super(context, attrs);
        parse(context, attrs);
        init(context);
    }

    private void parse(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CameraMaskView);

        mMaskMargin = (int) array.getDimension(R.styleable.CameraMaskView_cmv_mask_margin, MASK_MARGIN);
        mMaskPosition = array.getInt(R.styleable.CameraMaskView_cmv_position_flag, 0);

        mMaskColor = array.getColor(R.styleable.CameraMaskView_cmv_mask_color, 0xa0000000);
        mTipText = array.getString(R.styleable.CameraMaskView_cmv_tip_text);
        if (null == mTipText || mTipText.isEmpty()) {
            mTipText = TEXT;
        }
        mTipTextColor = array.getColor(R.styleable.CameraMaskView_cmv_tip_color, 0xFFFFFFFF);
        mTipTextSize = (int) array.getDimension(R.styleable.CameraMaskView_cmv_tip_size, sp2px(context, 14));
        mTipMargin = (int) array.getDimension(R.styleable.CameraMaskView_cmv_tip_margin, dp2px(context, 20));
        mLineColor = array.getColor(R.styleable.CameraMaskView_cmv_line_color, 0xFF85D28A);
        mLineWidth = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_width, dp2px(context, LINE_WIDTH));
        mLineLength = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_length, dp2px(context, LINE_LENGTH));
        mLinePadding = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_padding, dp2px(context, LINE_PADDING));
        mLineMargin = (int) array.getDimension(R.styleable.CameraMaskView_cmv_line_margin, dp2px(context, LINE_MARGIN));

        array.recycle();
    }

    private void init(Context context) {
        viewWidth = context.getResources().getDisplayMetrics().widthPixels;
        viewHeight = context.getResources().getDisplayMetrics().heightPixels;
        rectWidth = viewWidth - dp2px(context, 2 * mLineMargin);
        // 身份证 长度85.6mm,宽度54mm
        rectHeight = (int) (rectWidth * 54 / 85.6);

        if (1 == mMaskPosition) {
            mCenterMarkRect.left = (viewWidth - rectWidth) / 2;
            mCenterMarkRect.top = mMaskMargin;
            mCenterMarkRect.right = mCenterMarkRect.left + rectWidth;
            mCenterMarkRect.bottom = mMaskMargin + rectHeight;
        } else {
            mCenterMarkRect.left = (viewWidth - rectWidth) / 2;
            mCenterMarkRect.top = (viewHeight - rectHeight) / 2;
            mCenterMarkRect.right = mCenterMarkRect.left + rectWidth;
            mCenterMarkRect.bottom = mCenterMarkRect.top + rectHeight;
        }

        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setColor(mLineColor);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeWidth(mLineWidth);// 设置线宽
        linePaint.setAlpha(255);

        maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        maskPaint.setColor(mMaskColor);

        wordPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        wordPaint.setColor(mTipTextColor);
        wordPaint.setTextSize(mTipTextSize);

        rect = new Rect(mCenterMarkRect.left, mCenterMarkRect.top - mTipTextSize - mTipMargin,
                mCenterMarkRect.right, mCenterMarkRect.top - mTipMargin);
        Paint.FontMetricsInt fontMetrics = wordPaint.getFontMetricsInt();
        baseline = rect.top + (rect.bottom - rect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
        wordPaint.setTextAlign(Paint.Align.CENTER);
    }


    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (1 == mMaskPosition) {
            // 顶部蒙层
            canvas.drawRect(0, 0, viewWidth, mMaskMargin, maskPaint);
            // 左侧
            canvas.drawRect(0, mMaskMargin, (viewWidth - rectWidth) / 2, mMaskMargin + rectHeight, maskPaint);
            // 右侧
            canvas.drawRect(viewWidth - (viewWidth - rectWidth) / 2, mMaskMargin, viewWidth, mMaskMargin + rectHeight, maskPaint);
            // 底部蒙层
            canvas.drawRect(0, mMaskMargin + rectHeight, viewWidth, viewHeight, maskPaint);
        } else {
            // 顶部蒙层
            canvas.drawRect(0, 0, viewWidth, viewHeight / 2 - rectHeight / 2, maskPaint);
            // 左侧
            canvas.drawRect(0, viewHeight / 2 - rectHeight / 2, (viewWidth - rectWidth) / 2, viewHeight / 2 + rectHeight / 2, maskPaint);
            // 右侧
            canvas.drawRect(viewWidth - (viewWidth - rectWidth) / 2, viewHeight / 2 - rectHeight / 2, viewWidth, viewHeight / 2 + rectHeight / 2, maskPaint);
            // 底部蒙层
            canvas.drawRect(0, viewHeight / 2 + rectHeight / 2, viewWidth, viewHeight, maskPaint);
        }

        // 绘制文字
        canvas.drawText(mTipText, rect.centerX(), baseline, wordPaint);

        // 绘制四个角
        canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding) - mLineWidth / 2.0F, mCenterMarkRect.top - dp2px(getContext(), mLinePadding),
                mCenterMarkRect.left + mLineLength, mCenterMarkRect.top - dp2px(getContext(), mLinePadding), linePaint);
        canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.top - dp2px(getContext(), mLinePadding),
                mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.top + mLineLength, linePaint);

        canvas.drawLine(mCenterMarkRect.right - mLineLength, mCenterMarkRect.top - dp2px(getContext(), mLinePadding),
                mCenterMarkRect.right + dp2px(getContext(), mLinePadding) + mLineWidth / 2.0F, mCenterMarkRect.top - dp2px(getContext(), mLinePadding), linePaint);
        canvas.drawLine(mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.top - dp2px(getContext(), mLinePadding),
                mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.top + mLineLength, linePaint);

        canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom - mLineLength,
                mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding) + mLineWidth / 2.0F, linePaint);
        canvas.drawLine(mCenterMarkRect.left - dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding),
                mCenterMarkRect.left + mLineLength, mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding), linePaint);

        canvas.drawLine(mCenterMarkRect.right - mLineLength, mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding),
                mCenterMarkRect.right + dp2px(getContext(), mLinePadding) + mLineWidth / 2.0F, mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding), linePaint);
        canvas.drawLine(mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom - mLineLength,
                mCenterMarkRect.right + dp2px(getContext(), mLinePadding), mCenterMarkRect.bottom + dp2px(getContext(), mLinePadding), linePaint);
    }

    public Rect getCenterMarkRect() {
        return mCenterMarkRect;
    }


    private int dp2px(Context context, float dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
    }


    private int sp2px(Context context, int dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dpValue, context.getResources().getDisplayMetrics());
    }

}
复制代码

来看看自定义的属性(xml文件)

里面就是一大堆自定义的属性,具体含义往下看git

<?xml version="1.0" encoding="utf-8"?>
<resources>

 <declare-styleable name="CameraMaskView">
        <!-- 蒙层颜色 -->
        <attr name="cmv_mask_color" format="color|reference" />

        <!-- 提示文本 -->
        <attr name="cmv_tip_text" format="string|reference" />
        <!-- 文字颜色 -->
        <attr name="cmv_tip_color" format="color|reference" />
        <!-- 文字大小 -->
        <attr name="cmv_tip_size" format="integer|dimension" />
        <!-- 文字底部距离方框的间隔 -->
        <attr name="cmv_tip_margin" format="integer|dimension" />

        <!-- 边线颜色 -->
        <attr name="cmv_line_color" format="color|reference" />
        <!-- 边线宽度(厚度) -->
        <attr name="cmv_line_width" format="integer|dimension" />
        <!-- 边线长度 -->
        <attr name="cmv_line_length" format="integer|dimension" />
        <!-- 边线与内部透明方框的间隔 -->
        <attr name="cmv_line_padding" format="integer|dimension" />
        <!-- 边线与外面手机屏幕边框的间隔 -->
        <attr name="cmv_line_margin" format="integer|dimension"/>

        <!-- 默认方框是剧中的 -->
        <attr name="cmv_position_flag">
            <enum name="center" value="0"/>
            <enum name="margin" value="1"/>
        </attr>

        <!-- 只有当设置了 cmv_position_flag = margin 才有效 -->
        <attr name="cmv_mask_margin" format="integer|dimension"/>

    </declare-styleable>

</resources>
复制代码

属性虽然有些多,可是大都都见名知意.github

最后来看看在 Activity 布局中的用法

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CameraActivity">

    <cn.molue.jooyer.masker.CameraSurfaceView android:id="@+id/camera_surface_view" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/>

    <cn.molue.jooyer.masker.CameraMaskView android:id="@+id/camera_rect_view" android:layout_width="0dp" android:layout_height="0dp" app:cmv_line_color="#85D28A" app:cmv_line_length="40dp" app:cmv_line_margin="10dp" app:cmv_line_padding="2dp" app:cmv_line_width="2dp" app:cmv_mask_color="#FF76635A" app:cmv_mask_margin="80dp" app:cmv_position_flag="margin" app:cmv_tip_color="#FFFFFF" app:cmv_tip_margin="40dp" app:cmv_tip_size="15sp" app:cmv_tip_text="请将身份证头像面置于此框内" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />


    <View android:id="@+id/view_background" android:layout_width="0dp" android:layout_height="123dp" android:background="#FF2F2A28" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" />

    <TextView android:id="@+id/text_view_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="33dp" android:layout_marginEnd="50dp" android:padding="18dp" android:gravity="center_vertical" android:text="取消" android:textColor="#FFF" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toLeftOf="@id/image_view_take" />

    <ImageView android:id="@+id/image_view_take" android:layout_width="60dp" android:layout_height="60dp" android:background="@drawable/camera_take_background" android:layout_marginBottom="30dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" />


</android.support.constraint.ConstraintLayout>
复制代码

这个就不解释了,最后看看 Activity中的操做canvas

package cn.molue.jooyer.masker;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

/** * @Author Jooyer * @Date 2018/01/08 * @Time 14.27 */
public class CameraActivity extends AppCompatActivity {

    private CameraSurfaceView cameraSurfaceView;
    private CameraMaskView cameraRectView;
    private TextView tvCancel;
    private ImageView ivTake;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        findView();
        initListener();
    }

    private void findView() {
        tvCancel = findViewById(R.id.text_view_cancel);
        ivTake = findViewById(R.id.image_view_take);
        cameraSurfaceView = findViewById(R.id.camera_surface_view);
        cameraRectView = findViewById(R.id.camera_rect_view);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        // 设置中间预览大小
        cameraSurfaceView.setCenterMarkRect(cameraRectView.getCenterMarkRect());
    }


    private void initListener() {

        tvCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        ivTake.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                cameraSurfaceView.takePicture();
            }
        });

        cameraSurfaceView.setOnPathChangedListener(new CameraSurfaceView.OnPathChangedListener() {
            @Override
            public void onValueChange(String path) {
                Toast.makeText(CameraActivity.this, path, Toast.LENGTH_SHORT).show();
            }
        });

    }
}
复制代码

记得在使用时申请相机和存储权限

喜欢记得点赞,收藏,转发哈!

参考地址: blog.csdn.net/github_3784… ,感谢大佬指引!

相关文章
相关标签/搜索