Android 仿支付宝人脸识别UI

作者: 戎码虫 | 来源:发表于2019-04-03 00:49 被阅读0次

    前言

    前一段时间做人脸识别,UI设计师也是惯用“cv”大法,做了一个仿支付宝的UI,这里就总结一下自定义仿支付宝人脸识别界面UI,人脸识别产品为公司产品不便于贴出来,这里就用前置摄像头画面代替。

    需求

    首先我们看看支付宝人脸识别UI界面

    界面1
    界面2

    需求分析:
    根据人脸的情况中间提示语不断变化,外边的进度条也会根据人脸情况动态变化,根据支付宝界面实现效果。


    实现效果

    实现

    这里用两种方式实现一种继承SurfaceView自定义,另一种是继承View自定义,第一种方式将预览视频流一起绘制,也可以作为一个蒙版覆盖上面。

    1、attrs.xml配置自定义属性
    <resources>
        <!--人脸识别界面-->
        <declare-styleable name="FaceView">
            <!--文本-->
            <attr name="tip_text" format="string"/>
            <!--颜色-->
            <attr name="tip_text_color" format="color"/>
            <!--文字大小-->
            <attr name="tip_text_size" format="dimension"/>
    
        </declare-styleable>
    </resources>
    

    这里只将提示框文字的大小、颜色、内容暴露出来,其他控件颜色可自行修改

    2、控件代码实现
    SurfaceView实现
    public class FaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
        private final String TAG = "FaceView";
        private SurfaceHolder mSurfaceHolder;
        /**
         * 是否可以开始绘制了
         */
        private boolean mStart = false;
        /**
         * 默认中间圆的半径从0开始
         */
        private float currentRadius = 0;
        /**
         * 控件的宽度(默认)
         */
        private int mViewWidth = 400;
        /**
         * 控件高度
         */
        private int mViewHeight = 400;
        /**
         * 中心圆屏幕边距
         */
        private int margin;
        /**
         * 圆圈画笔
         */
        private Paint mPaint;
        /**
         * 提示文本
         */
        private String mTipText;
        /**
         * 提示文本颜色
         */
        private int mTipTextColor;
        /**
         * 提示文本颜色
         */
        private int mTipTextSize;
        /**
         * 内圆半径
         */
        private int mRadius;
        /**
         * 背景弧宽度
         */
        private float mBgArcWidth;
    
        /**
         * 圆心点坐标
         */
        private Point mCenterPoint = new Point();
        /**
         * 圆弧边界
         */
        private RectF mBgRectF = new RectF();
    
        /**
         * 开始角度
         */
        private int mStartAngle = 105;
    
        /**
         * 结束角度
         */
        private int mEndAngle = 330;
    
        /**
         * 圆弧背景画笔
         */
        private Paint mBgArcPaint;
        /**
         * 提示语画笔
         */
        private Paint mTextPaint;
    
        /**
         * 圆弧画笔
         */
        private Paint mArcPaint;
        /**
         * 渐变器
         */
        private SweepGradient mSweepGradient;
    
        /**
         * 是否开始
         */
        private boolean isRunning = true;
    
        /**
         * 是否后退
         */
        private boolean isBack = false;
        /**
         * 绘制速度
         */
        private int speed = 5;
    
        /**
         * 设置默认转动角度0
         */
        float currentAngle = 0;
    
        public FaceView(Context context) {
            this(context, null);
        }
    
        public FaceView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public FaceView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            //获取xml里面的属性值
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FaceView);
            mTipText = array.getString(R.styleable.FaceView_tip_text);
            mTipTextColor = array.getColor(R.styleable.FaceView_tip_text_color, Color.WHITE);
            mTipTextSize = array.getDimensionPixelSize(R.styleable.FaceView_tip_text_size, ScreenUtils.sp2px(context, 12));
            array.recycle();
            Log.d(TAG, "FaceView构造");
            initHolder(context);
        }
    
        /**
         * 初始化控件View
         */
        private void initHolder(Context context) {
            //获得SurfaceHolder对象
            mSurfaceHolder = getHolder();
            //设置透明背景
            mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
            //添加回调
            mSurfaceHolder.addCallback(this);
            //显示顶层
            setZOrderOnTop(true);
            //防止遮住控件
            setZOrderMediaOverlay(true);
            //屏蔽界面焦点
            setFocusable(true);
            //保持屏幕长亮
            setKeepScreenOn(true);
    
            //初始化值
            margin = ScreenUtils.dp2px(context, 60);
            mBgArcWidth = ScreenUtils.dp2px(context, 5);
    
            //初始化画笔
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(getResources().getColor(R.color.colorAccent));
            mPaint.setStyle(Paint.Style.FILL);
    
            //绘制文字画笔
            mTextPaint = new Paint();
            mTextPaint.setStyle(Paint.Style.FILL);
            mTextPaint.setStrokeWidth(8);
            mTextPaint.setColor(mTipTextColor);
            mTextPaint.setTextSize(mTipTextSize);
            mTextPaint.setTextAlign(Paint.Align.CENTER);
    
            // 圆弧背景
            mBgArcPaint = new Paint();
            mBgArcPaint.setAntiAlias(true);
            mBgArcPaint.setColor(getResources().getColor(R.color.circleBg));
            mBgArcPaint.setStyle(Paint.Style.STROKE);
            mBgArcPaint.setStrokeWidth(mBgArcWidth);
            mBgArcPaint.setStrokeCap(Paint.Cap.ROUND);
    
            // 圆弧
            mArcPaint = new Paint();
            mArcPaint.setAntiAlias(true);
            mArcPaint.setStyle(Paint.Style.STROKE);
            mArcPaint.setStrokeWidth(mBgArcWidth);
            mArcPaint.setStrokeCap(Paint.Cap.ROUND);
    
            //开启线程检测
            new Thread(this).start();
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder surfaceHolder) {
            mStart = true;
            Log.d(TAG, "surfaceCreated()");
        }
    
    
        @Override
        public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
            Log.d(TAG, "surfaceChanged()");
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
            mStart = false;
            Log.d(TAG, "surfaceDestroyed()");
        }
    
        @SuppressLint("DrawAllocation")
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //测量view的宽度
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            if (widthMode == MeasureSpec.EXACTLY) {
                mViewWidth = MeasureSpec.getSize(widthMeasureSpec);
            }
    
            //测量view的高度
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            if (heightMode == MeasureSpec.EXACTLY) {
                mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
            }
    
            setMeasuredDimension(mViewWidth, mViewHeight);
            Log.d(TAG, "onMeasure  mViewWidth : " + mViewWidth + "  mViewHeight : " + mViewHeight);
    
            //获取圆的相关参数
            mCenterPoint.x = mViewWidth / 2;
            mCenterPoint.y = mViewHeight / 2;
    
            //外环圆的半径
            mRadius = mCenterPoint.x - margin;
    
            //绘制背景圆弧的边界
            mBgRectF.left = mCenterPoint.x - mRadius - mBgArcWidth / 2;
            mBgRectF.top = mCenterPoint.y - mRadius - mBgArcWidth / 2;
            mBgRectF.right = mCenterPoint.x + mRadius + mBgArcWidth / 2;
            mBgRectF.bottom = mCenterPoint.y + mRadius + mBgArcWidth / 2;
    
            //进度条颜色 -mStartAngle将位置便宜到原处
            mSweepGradient = new SweepGradient(mCenterPoint.x - mStartAngle, mCenterPoint.y - mStartAngle, getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark));
        }
    
        @Override
        public void run() {
            //循环绘制画面内容
            while (true) {
                if (mStart) {
                    drawView();
                }
            }
        }
    
        private void drawView() {
            Canvas canvas = null;
            try {
                //获得canvas对象
                canvas = mSurfaceHolder.lockCanvas();
                //清除画布上面里面的内容
                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                //绘制画布内容
                drawContent(canvas);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (canvas != null) {
                    //释放canvas锁,并且显示视图
                    mSurfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    
        /**
         * 跟新提示信息
         *
         * @param title
         */
        public void updateTipsInfo(String title) {
            mTipText = title;
        }
    
        private void drawContent(Canvas canvas) {
            //防止save()和restore()方法代码之后对Canvas执行的操作,继续对后续的绘制会产生影响
            canvas.save();
            //先画提示语
            drawHintText(canvas);
            //绘制正方形的框内类似人脸识别
    //        drawFaceRectTest(canvas);
            //绘制人脸识别部分
            drawFaceCircle(canvas);
            //画外边进度条
            drawRoundProgress(canvas);
            canvas.restore();
        }
    
        private void drawFaceCircle(Canvas canvas) {
            // 圆形,放大效果
            currentRadius += 20;
            if (currentRadius > mRadius)
                currentRadius = mRadius;
    
            //设置画板样式
            Path path = new Path();
            //以(400,200)为圆心,半径为100绘制圆 指创建顺时针方向的矩形路径
            path.addCircle(mCenterPoint.x, mCenterPoint.y, currentRadius, Path.Direction.CW);
            // 是A形状中不同于B的部分显示出来
            canvas.clipPath(path, Region.Op.DIFFERENCE);
            // 半透明背景效果
            canvas.clipRect(0, 0, mViewWidth, mViewHeight);
            //绘制背景颜色
            canvas.drawColor(getResources().getColor(R.color.viewBgWhite));
        }
    
    
        /**
         * 绘制人脸识别界面进度条
         *
         * @param canvas canvas
         */
        private void drawRoundProgress(Canvas canvas) {
            // 逆时针旋转105度
            canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y);
            // 设置圆环背景
            canvas.drawArc(mBgRectF, 0, mEndAngle, false, mBgArcPaint);
            //判断是否正在运行
            if (isRunning) {
                if (isBack) {
                    currentAngle -= speed;
                    if (currentAngle <= 0)
                        currentAngle = 0;
                } else {
                    currentAngle += speed;
                    if (currentAngle >= mEndAngle)
                        currentAngle = mEndAngle;
                }
            }
            // 设置渐变颜色
            mArcPaint.setShader(mSweepGradient);
            canvas.drawArc(mBgRectF, 0, currentAngle, false, mArcPaint);
        }
    
        /**
         * 从头位置开始动画
         */
        public void resetPositionStart() {
            currentAngle = 0;
            isBack = false;
        }
    
        /**
         * 动画直接完成
         */
        public void finnishAnimator() {
            currentAngle = mEndAngle;
            isBack = false;
        }
    
        /**
         * 停止动画
         */
        public void pauseAnimator() {
            isRunning = false;
        }
    
        /**
         * 开始动画
         */
        public void startAnimator() {
            isRunning = true;
        }
    
        /**
         * 动画回退
         */
        public void backAnimator() {
            isRunning = true;
            isBack = true;
        }
    
        /**
         * 动画前进
         */
        public void forwardAnimator() {
            isRunning = true;
            isBack = false;
        }
    
        /**
         * 绘制人脸识别提示
         *
         * @param canvas canvas
         */
        private void drawHintText(Canvas canvas) {
            //圆视图宽度 (屏幕减去两边距离)
            int cameraWidth = mViewWidth - 2 * margin;
            //x轴起点(文字背景起点)
            int x = margin;
            //宽度(提示框背景宽度)
            int width = cameraWidth;
            //y轴起点
            int y = (int) (mCenterPoint.y - mRadius);
            //提示框背景高度
            int height = cameraWidth / 4;
            Rect rect = new Rect(x, y, x + width, y + height);
            canvas.drawRect(rect, mPaint);
    
            //计算baseline
            Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
            float distance = (fontMetrics.bottom - fontMetrics.top) / 4;
            float baseline = rect.centerY() + distance;
            canvas.drawText(mTipText, rect.centerX(), baseline, mTextPaint);
        }
    
        /**
         * 绘制人脸识别矩形区域
         *
         * @param canvas canvas
         */
        private void drawFaceRectTest(Canvas canvas) {
            int cameraWidth = mViewWidth - 2 * margin;
            int x = margin + cameraWidth / 6;
            int width = cameraWidth * 2 / 3;
            int y = mCenterPoint.x + (width / 2);
            int height = width;
            Rect rect = new Rect(x, y, x + width, y + height);
            mPaint.setColor(Color.GREEN);
            mPaint.setStyle(Paint.Style.STROKE);
            canvas.drawRect(rect, mPaint);
        }
    }
    

    当获取canvas 实例后,可以将视频流一样画置SurfaceView上面

    View实现

    步骤:
    1、自定义View的属性
    2、在自定义View的构造方法中获取View属性值
    3、重写onMeasure(int,int)方法
    4、重写onDraw(Canvas canvas)方法

    public class FaceView2 extends View implements Runnable {
        private final String TAG = "FaceView";
        /**
         * 是否可以开始绘制了
         */
        private boolean mStart = false;
        /**
         * 默认中间圆的半径从0开始
         */
        private float currentRadius = 0;
        /**
         * 控件的宽度(默认)
         */
        private int mViewWidth = 400;
        /**
         * 控件高度
         */
        private int mViewHeight = 400;
        /**
         * 中心圆屏幕边距
         */
        private int margin;
        /**
         * 圆圈画笔
         */
        private Paint mPaint;
        /**
         * 提示文本
         */
        private String mTipText;
        /**
         * 提示文本颜色
         */
        private int mTipTextColor;
        /**
         * 提示文本颜色
         */
        private int mTipTextSize;
        /**
         * 内圆半径
         */
        private int mRadius;
        /**
         * 背景弧宽度
         */
        private float mBgArcWidth;
    
        /**
         * 圆心点坐标
         */
        private Point mCenterPoint = new Point();
        /**
         * 圆弧边界
         */
        private RectF mBgRectF = new RectF();
    
        /**
         * 开始角度
         */
        private int mStartAngle = 105;
    
        /**
         * 结束角度
         */
        private int mEndAngle = 330;
    
        /**
         * 设置默认转动角度0
         */
        float currentAngle = 0;
    
        /**
         * 圆弧背景画笔
         */
        private Paint mBgArcPaint;
        /**
         * 提示语画笔
         */
        private Paint mTextPaint;
    
        /**
         * 圆弧画笔
         */
        private Paint mArcPaint;
        /**
         * 渐变器
         */
        private SweepGradient mSweepGradient;
    
        /**
         * 是否开始
         */
        private boolean isRunning = true;
    
        /**
         * 是否后退
         */
        private boolean isBack = false;
        /**
         * 绘制速度
         */
        private int speed = 5;
    
    
        public FaceView2(Context context) {
            this(context, null);
        }
    
        public FaceView2(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public FaceView2(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            //获取xml里面的属性值
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FaceView);
            mTipText = array.getString(R.styleable.FaceView_tip_text);
            mTipTextColor = array.getColor(R.styleable.FaceView_tip_text_color, Color.WHITE);
            mTipTextSize = array.getDimensionPixelSize(R.styleable.FaceView_tip_text_size, ScreenUtils.sp2px(context, 12));
            array.recycle();
    
            Log.d(TAG, "FaceView构造");
            initPaint(context);
        }
    
        /**
         * 初始化控件View
         */
        private void initPaint(Context context) {
            //获取界面焦点
            setFocusable(true);
            //保持屏幕长亮
            setKeepScreenOn(true);
    
            //初始化值
            margin = ScreenUtils.dp2px(context, 60);
            mBgArcWidth = ScreenUtils.dp2px(context, 5);
    
            //初始化画笔
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(getResources().getColor(R.color.colorAccent));
            mPaint.setStyle(Paint.Style.FILL);
    
            //绘制文字画笔
            mTextPaint = new Paint();
            mTextPaint.setStyle(Paint.Style.FILL);
            mTextPaint.setStrokeWidth(8);
            mTextPaint.setColor(mTipTextColor);
            mTextPaint.setTextSize(mTipTextSize);
            mTextPaint.setTextAlign(Paint.Align.CENTER);
    
            // 圆弧背景
            mBgArcPaint = new Paint();
            mBgArcPaint.setAntiAlias(true);
            mBgArcPaint.setColor(getResources().getColor(R.color.circleBg));
            mBgArcPaint.setStyle(Paint.Style.STROKE);
            mBgArcPaint.setStrokeWidth(mBgArcWidth);
            mBgArcPaint.setStrokeCap(Paint.Cap.ROUND);
    
            // 圆弧
            mArcPaint = new Paint();
            mArcPaint.setAntiAlias(true);
            mArcPaint.setStyle(Paint.Style.STROKE);
            mArcPaint.setStrokeWidth(mBgArcWidth);
            mArcPaint.setStrokeCap(Paint.Cap.ROUND);
    
            //开启线程检测
            new Thread(this).start();
        }
    
        @SuppressLint("DrawAllocation")
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //测量view的宽度
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            if (widthMode == MeasureSpec.EXACTLY) {
                mViewWidth = MeasureSpec.getSize(widthMeasureSpec);
            }
    
            //测量view的高度
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            if (heightMode == MeasureSpec.EXACTLY) {
                mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
            }
    
            setMeasuredDimension(mViewWidth, mViewHeight);
            Log.d(TAG, "onMeasure  mViewWidth : " + mViewWidth + "  mViewHeight : " + mViewHeight);
    
            //获取圆的相关参数
            mCenterPoint.x = mViewWidth / 2;
            mCenterPoint.y = mViewHeight / 2;
    
            //外环圆的半径
            mRadius = mCenterPoint.x - margin;
    
            //绘制背景圆弧的边界
            mBgRectF.left = mCenterPoint.x - mRadius - mBgArcWidth / 2;
            mBgRectF.top = mCenterPoint.y - mRadius - mBgArcWidth / 2;
            mBgRectF.right = mCenterPoint.x + mRadius + mBgArcWidth / 2;
            mBgRectF.bottom = mCenterPoint.y + mRadius + mBgArcWidth / 2;
    
            //进度条颜色 -mStartAngle/2将位置到原处
            mSweepGradient = new SweepGradient(mCenterPoint.x - mStartAngle / 2,
                    mCenterPoint.y - mStartAngle / 2, getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark));
        }
    
        @Override
        protected void onVisibilityChanged(View changedView, int visibility) {
            super.onVisibilityChanged(changedView, visibility);
            mStart = (visibility == VISIBLE);
        }
    
        @Override
        public void run() {
            //循环绘制画面内容
            while (true) {
                if (mStart) {
                    try {
                        changeValue();
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 动态检测改变值
         */
        private void changeValue() {
            // 内圆形,放大效果
            currentRadius += 20;
            if (currentRadius > mRadius)
                currentRadius = mRadius;
    
            //外部圈的动画效果
            if (isRunning) {
                if (isBack) {
                    currentAngle -= speed;
                    if (currentAngle <= 0)
                        currentAngle = 0;
                } else {
                    currentAngle += speed;
                    if (currentAngle >= mEndAngle)
                        currentAngle = mEndAngle;
                }
            }
            //重绘view
            invalidate();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //绘制画布内容
            drawContent(canvas);
        }
    
        /**
         * 跟新提示信息
         *
         * @param title
         */
        public void updateTipsInfo(String title) {
            mTipText = title;
        }
    
        private void drawContent(Canvas canvas) {
            //防止save()和restore()方法代码之后对Canvas执行的操作,继续对后续的绘制会产生影响
            canvas.save();
            //先画提示语
            drawHintText(canvas);
            //绘制正方形的框内类似人脸识别
    //        drawFaceRectTest(canvas);
            //绘制人脸识别部分
            drawFaceCircle(canvas);
            //画外边进度条
            drawRoundProgress(canvas);
            canvas.restore();
        }
    
        private void drawFaceCircle(Canvas canvas) {
            //设置画板样式
            Path path = new Path();
            //以(400,200)为圆心,半径为100绘制圆 指创建顺时针方向的矩形路径
            path.addCircle(mCenterPoint.x, mCenterPoint.y, currentRadius, Path.Direction.CW);
            // 是A形状中不同于B的部分显示出来
            canvas.clipPath(path, Region.Op.DIFFERENCE);
            // 半透明背景效果
            canvas.clipRect(0, 0, mViewWidth, mViewHeight);
            //绘制背景颜色
            canvas.drawColor(getResources().getColor(R.color.viewBgWhite));
        }
    
    
        /**
         * 绘制人脸识别界面进度条
         *
         * @param canvas canvas
         */
        private void drawRoundProgress(Canvas canvas) {
            // 逆时针旋转105度
            canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y);
            // 设置圆环背景
            canvas.drawArc(mBgRectF, 0, mEndAngle, false, mBgArcPaint);
            // 设置渐变颜色
            mArcPaint.setShader(mSweepGradient);
            canvas.drawArc(mBgRectF, 0, currentAngle, false, mArcPaint);
        }
    
        /**
         * 从头位置开始动画
         */
        public void resetPositionStart() {
            currentAngle = 0;
            isBack = false;
        }
    
        /**
         * 动画直接完成
         */
        public void finnishAnimator() {
            currentAngle = mEndAngle;
            isBack = false;
        }
    
        /**
         * 停止动画
         */
        public void pauseAnimator() {
            isRunning = false;
        }
    
        /**
         * 开始动画
         */
        public void startAnimator() {
            isRunning = true;
        }
    
        /**
         * 动画回退
         */
        public void backAnimator() {
            isRunning = true;
            isBack = true;
        }
    
        /**
         * 动画前进
         */
        public void forwardAnimator() {
            isRunning = true;
            isBack = false;
        }
    
        /**
         * 绘制人脸识别提示
         *
         * @param canvas canvas
         */
        private void drawHintText(Canvas canvas) {
            //圆视图宽度 (屏幕减去两边距离)
            int cameraWidth = mViewWidth - 2 * margin;
            //x轴起点(文字背景起点)
            int x = margin;
            //宽度(提示框背景宽度)
            int width = cameraWidth;
            //y轴起点
            int y = (int) (mCenterPoint.y - mRadius);
            //提示框背景高度
            int height = cameraWidth / 4;
            Rect rect = new Rect(x, y, x + width, y + height);
            canvas.drawRect(rect, mPaint);
    
            //计算baseline
            Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
            float distance = (fontMetrics.bottom - fontMetrics.top) / 4;
            float baseline = rect.centerY() + distance;
            canvas.drawText(mTipText, rect.centerX(), baseline, mTextPaint);
        }
    
        /**
         * 绘制人脸识别矩形区域
         *
         * @param canvas canvas
         */
        private void drawFaceRectTest(Canvas canvas) {
            int cameraWidth = mViewWidth - 2 * margin;
            int x = margin + cameraWidth / 6;
            int width = cameraWidth * 2 / 3;
            int y = mCenterPoint.x + (width / 2);
            int height = width;
            Rect rect = new Rect(x, y, x + width, y + height);
            mPaint.setColor(Color.GREEN);
            mPaint.setStyle(Paint.Style.STROKE);
            canvas.drawRect(rect, mPaint);
        }
    }
    

    这里值得注意的是这几个方法,使用的时候可以根据实际调用这几个动画。

     /**
         * 从头位置开始动画
         */
        public void resetPositionStart() {
            currentAngle = 0;
            isBack = false;
        }
    
        /**
         * 动画直接完成
         */
        public void finnishAnimator() {
            currentAngle = mEndAngle;
            isBack = false;
        }
    
        /**
         * 停止动画
         */
        public void pauseAnimator() {
            isRunning = false;
        }
    
        /**
         * 开始动画
         */
        public void startAnimator() {
            isRunning = true;
        }
    
        /**
         * 动画回退
         */
        public void backAnimator() {
            isRunning = true;
            isBack = true;
        }
    
        /**
         * 动画前进
         */
        public void forwardAnimator() {
            isRunning = true;
            isBack = false;
        }
    
    3、使用布局UI
    <?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:id="@+id/cl_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".PreviewActivity">
    
        <!--相机预览显示-->
        <com.demo.facerecognition.view.FaceView
            android:id="@+id/fv_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:tip_text="请闭眼"
            app:tip_text_size="20sp"/>
    
        <!--关闭按钮-->
        <android.support.v7.widget.AppCompatImageView
            android:id="@+id/iv_close"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignParentLeft="true"
            android:padding="5dp"
            android:src="@drawable/iv_svg_close"/>
    
        <!--界面提示信息-->
        <TextView
            android:id="@+id/tv_tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/iv_close"
            android:layout_marginTop="10dp"
            android:text="@string/face_tip"
            android:textColor="@color/textColor"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_constraintLeft_toRightOf="parent"
            app:layout_constraintRight_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/iv_close"/>
    </android.support.constraint.ConstraintLayout>
    
    4、Activity使用
    public class PreviewActivity extends AppCompatActivity {
        private final String TAG = "PreviewActivity";
        private Camera camera;
        private boolean isPreview = false;
    
        static final String[] PERMISSION = new String[]{
                //获取照相机权限
                Manifest.permission.CAMERA,
        };
    
        /**
         * 设置Android6.0的权限申请
         */
        private void setPermissions() {
            if (ContextCompat.checkSelfPermission(PreviewActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                //Android 6.0申请权限
                ActivityCompat.requestPermissions(this, PERMISSION, 1);
            } else {
                Log.i(TAG, "权限申请ok");
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_preview);
            //初始化布局
            ConstraintLayout constraintLayout = findViewById(R.id.cl_root);
            final FaceView faceView = findViewById(R.id.fv_title);
            ImageView imageView = findViewById(R.id.iv_close);
            //申请手机的权限
            setPermissions();
    
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int i = (int) (1 + Math.random() * 4);
                    switch (i) {
                        case 1:
                            faceView.resetPositionStart();
                            faceView.updateTipsInfo("没有检测人脸");
                            break;
                        case 2:
                            faceView.backAnimator();
                            faceView.updateTipsInfo("请露正脸");
                            break;
                        case 3:
                            faceView.pauseAnimator();
                            faceView.updateTipsInfo(" 眨眨眼");
                            break;
                        case 4:
                            faceView.startAnimator();
                            faceView.updateTipsInfo("离近一点");
                            break;
                        default:
                            break;
                    }
                }
            });
    
            //添加布局
            SurfaceView mSurfaceView = new SurfaceView(this);
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT);
            params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
            mSurfaceView.setLayoutParams(params);
            constraintLayout.addView(mSurfaceView, 0);
            //得到getHolder实例
            SurfaceHolder mSurfaceHolder = mSurfaceView.getHolder();
            mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
            // 添加 Surface 的 callback 接口
            mSurfaceHolder.addCallback(mSurfaceCallback);
        }
    
        private SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder surfaceHolder) {
                try {
                    //打开硬件摄像头,这里导包得时候一定要注意是android.hardware.Camera
                    // Camera,open() 默认返回的后置摄像头信息
                    //设置角度,此处 CameraId 我默认 为 1 (前置)
                    if (Camera.getNumberOfCameras() > 1) {
                        camera = Camera.open(1);
                    } else {
                        camera = Camera.open(0);
                    }
                    //设置相机角度
                    camera.setDisplayOrientation(90);
                    //通过SurfaceView显示取景画面
                    camera.setPreviewDisplay(surfaceHolder);
                    //开始预览
                    camera.startPreview();
                    //设置是否预览参数为真
                    isPreview = true;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
            }
    
            @Override
            public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
                if (camera != null) {
                    if (isPreview) {//正在预览
                        try {
                            camera.stopPreview();
                            camera.release();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
    
        @Override
        protected void onDestroy() {
            if (camera != null) {
                if (isPreview) {//正在预览
                    try {
                        camera.stopPreview();
                        camera.release();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            super.onDestroy();
        }
    }
    

    视频流用的前置摄像头,这里的提示和动画也是随机,项目中可以根据真实情况展示。


    各个view的位置

    【注意】自定义view应该注意前后绘制顺序和叠加模式

    总结

    项目中用到就简单抽了一下,有遇到类似需求可以参考一下,最近在学Flutter,学的差不多了写个Flutter版本在更新一下。。。

    相关文章

      网友评论

        本文标题:Android 仿支付宝人脸识别UI

        本文链接:https://www.haomeiwen.com/subject/rujobqtx.html