前言
前一段时间做人脸识别,UI设计师也是惯用“cv”大法,做了一个仿支付宝的UI,这里就总结一下自定义仿支付宝人脸识别界面UI,人脸识别产品为公司产品不便于贴出来,这里就用前置摄像头画面代替。
需求
界面1首先我们看看支付宝人脸识别UI界面
界面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版本在更新一下。。。
网友评论