热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android仿支付宝人脸识别UI

前言前一段时间做人脸识别,UI设计师也是惯用“cv”大法,做了一个仿支付宝的UI,这里就总结一下自定义仿支付宝人脸识别界面UI,人脸识

前言

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

需求

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

Android 仿支付宝人脸识别UI
image
Android 仿支付宝人脸识别UI
image

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

Android 仿支付宝人脸识别UI
image

实现

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

1、attrs.xml配置自定义属性

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

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 = 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 = 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

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(); } }

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

Android 仿支付宝人脸识别UI
image

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

总结

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

声明:

转载请注明出处

作者:戎码虫
链接:https://www.jianshu.com/p/625266dfab1d


推荐阅读
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
author-avatar
莫不静了_660
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有