<meta charset="utf-8">
前言
前一段時(shí)間做人臉識(shí)別,UI設(shè)計(jì)師也是慣用“cv”大法,做了一個(gè)仿支付寶的UI景描,這里就總結(jié)一下自定義仿支付寶人臉識(shí)別界面UI万哪,人臉識(shí)別產(chǎn)品為公司產(chǎn)品不便于貼出來(lái),這里就用前置攝像頭畫(huà)面代替脐恩。
需求
首先我們看看支付寶人臉識(shí)別UI界面
需求分析:
根據(jù)人臉的情況中間提示語(yǔ)不斷變化镐侯,外邊的進(jìn)度條也會(huì)根據(jù)人臉情況動(dòng)態(tài)變化,根據(jù)支付寶界面實(shí)現(xiàn)效果驶冒。
實(shí)現(xiàn)
這里用兩種方式實(shí)現(xiàn)一種繼承SurfaceView自定義苟翻,另一種是繼承View自定義,第一種方式將預(yù)覽視頻流一起繪制骗污,也可以作為一個(gè)蒙版覆蓋上面崇猫。
1、attrs.xml配置自定義屬性
<resources>
<!--人臉識(shí)別界面-->
<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>
這里只將提示框文字的大小需忿、顏色诅炉、內(nèi)容暴露出來(lái)蜡歹,其他控件顏色可自行修改
2、控件代碼實(shí)現(xiàn)
SurfaceView實(shí)現(xiàn)
public class FaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private final String TAG = "FaceView";
private SurfaceHolder mSurfaceHolder;
/**
* 是否可以開(kāi)始繪制了
*/
private boolean mStart = false;
/**
* 默認(rèn)中間圓的半徑從0開(kāi)始
*/
private float currentRadius = 0;
/**
* 控件的寬度(默認(rèn))
*/
private int mViewWidth = 400;
/**
* 控件高度
*/
private int mViewHeight = 400;
/**
* 中心圓屏幕邊距
*/
private int margin;
/**
* 圓圈畫(huà)筆
*/
private Paint mPaint;
/**
* 提示文本
*/
private String mTipText;
/**
* 提示文本顏色
*/
private int mTipTextColor;
/**
* 提示文本顏色
*/
private int mTipTextSize;
/**
* 內(nèi)圓半徑
*/
private int mRadius;
/**
* 背景弧寬度
*/
private float mBgArcWidth;
/**
* 圓心點(diǎn)坐標(biāo)
*/
private Point mCenterPoint = new Point();
/**
* 圓弧邊界
*/
private RectF mBgRectF = new RectF();
/**
* 開(kāi)始角度
*/
private int mStartAngle = 105;
/**
* 結(jié)束角度
*/
private int mEndAngle = 330;
/**
* 圓弧背景畫(huà)筆
*/
private Paint mBgArcPaint;
/**
* 提示語(yǔ)畫(huà)筆
*/
private Paint mTextPaint;
/**
* 圓弧畫(huà)筆
*/
private Paint mArcPaint;
/**
* 漸變器
*/
private SweepGradient mSweepGradient;
/**
* 是否開(kāi)始
*/
private boolean isRunning = true;
/**
* 是否后退
*/
private boolean isBack = false;
/**
* 繪制速度
*/
private int speed = 5;
/**
* 設(shè)置默認(rèn)轉(zhuǎn)動(dòng)角度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構(gòu)造");
initHolder(context);
}
/**
* 初始化控件View
*/
private void initHolder(Context context) {
//獲得SurfaceHolder對(duì)象
mSurfaceHolder = getHolder();
//設(shè)置透明背景
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
//添加回調(diào)
mSurfaceHolder.addCallback(this);
//顯示頂層
setZOrderOnTop(true);
//防止遮住控件
setZOrderMediaOverlay(true);
//屏蔽界面焦點(diǎn)
setFocusable(true);
//保持屏幕長(zhǎng)亮
setKeepScreenOn(true);
//初始化值
margin = ScreenUtils.dp2px(context, 60);
mBgArcWidth = ScreenUtils.dp2px(context, 5);
//初始化畫(huà)筆
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(getResources().getColor(R.color.colorAccent));
mPaint.setStyle(Paint.Style.FILL);
//繪制文字畫(huà)筆
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);
//開(kāi)啟線程檢測(cè)
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) {
//測(cè)量view的寬度
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mViewWidth = MeasureSpec.getSize(widthMeasureSpec);
}
//測(cè)量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);
//獲取圓的相關(guān)參數(shù)
mCenterPoint.x = mViewWidth / 2;
mCenterPoint.y = mViewHeight / 2;
//外環(huán)圓的半徑
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;
//進(jìn)度條顏色 -mStartAngle將位置便宜到原處
mSweepGradient = new SweepGradient(mCenterPoint.x - mStartAngle, mCenterPoint.y - mStartAngle, getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark));
}
@Override
public void run() {
//循環(huán)繪制畫(huà)面內(nèi)容
while (true) {
if (mStart) {
drawView();
}
}
}
private void drawView() {
Canvas canvas = null;
try {
//獲得canvas對(duì)象
canvas = mSurfaceHolder.lockCanvas();
//清除畫(huà)布上面里面的內(nèi)容
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//繪制畫(huà)布內(nèi)容
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()方法代碼之后對(duì)Canvas執(zhí)行的操作月而,繼續(xù)對(duì)后續(xù)的繪制會(huì)產(chǎn)生影響
canvas.save();
//先畫(huà)提示語(yǔ)
drawHintText(canvas);
//繪制正方形的框內(nèi)類(lèi)似人臉識(shí)別
// drawFaceRectTest(canvas);
//繪制人臉識(shí)別部分
drawFaceCircle(canvas);
//畫(huà)外邊進(jìn)度條
drawRoundProgress(canvas);
canvas.restore();
}
private void drawFaceCircle(Canvas canvas) {
// 圓形,放大效果
currentRadius += 20;
if (currentRadius > mRadius)
currentRadius = mRadius;
//設(shè)置畫(huà)板樣式
Path path = new Path();
//以(400,200)為圓心议纯,半徑為100繪制圓 指創(chuàng)建順時(shí)針?lè)较虻木匦温窂? path.addCircle(mCenterPoint.x, mCenterPoint.y, currentRadius, Path.Direction.CW);
// 是A形狀中不同于B的部分顯示出來(lái)
canvas.clipPath(path, Region.Op.DIFFERENCE);
// 半透明背景效果
canvas.clipRect(0, 0, mViewWidth, mViewHeight);
//繪制背景顏色
canvas.drawColor(getResources().getColor(R.color.viewBgWhite));
}
/**
* 繪制人臉識(shí)別界面進(jìn)度條
*
* @param canvas canvas
*/
private void drawRoundProgress(Canvas canvas) {
// 逆時(shí)針旋轉(zhuǎn)105度
canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y);
// 設(shè)置圓環(huán)背景
canvas.drawArc(mBgRectF, 0, mEndAngle, false, mBgArcPaint);
//判斷是否正在運(yùn)行
if (isRunning) {
if (isBack) {
currentAngle -= speed;
if (currentAngle <= 0)
currentAngle = 0;
} else {
currentAngle += speed;
if (currentAngle >= mEndAngle)
currentAngle = mEndAngle;
}
}
// 設(shè)置漸變顏色
mArcPaint.setShader(mSweepGradient);
canvas.drawArc(mBgRectF, 0, currentAngle, false, mArcPaint);
}
/**
* 從頭位置開(kāi)始動(dòng)畫(huà)
*/
public void resetPositionStart() {
currentAngle = 0;
isBack = false;
}
/**
* 動(dòng)畫(huà)直接完成
*/
public void finnishAnimator() {
currentAngle = mEndAngle;
isBack = false;
}
/**
* 停止動(dòng)畫(huà)
*/
public void pauseAnimator() {
isRunning = false;
}
/**
* 開(kāi)始動(dòng)畫(huà)
*/
public void startAnimator() {
isRunning = true;
}
/**
* 動(dòng)畫(huà)回退
*/
public void backAnimator() {
isRunning = true;
isBack = true;
}
/**
* 動(dòng)畫(huà)前進(jìn)
*/
public void forwardAnimator() {
isRunning = true;
isBack = false;
}
/**
* 繪制人臉識(shí)別提示
*
* @param canvas canvas
*/
private void drawHintText(Canvas canvas) {
//圓視圖寬度 (屏幕減去兩邊距離)
int cameraWidth = mViewWidth - 2 * margin;
//x軸起點(diǎn)(文字背景起點(diǎn))
int x = margin;
//寬度(提示框背景寬度)
int width = cameraWidth;
//y軸起點(diǎn)
int y = (int) (mCenterPoint.y - mRadius);
//提示框背景高度
int height = cameraWidth / 4;
Rect rect = new Rect(x, y, x + width, y + height);
canvas.drawRect(rect, mPaint);
//計(jì)算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);
}
/**
* 繪制人臉識(shí)別矩形區(qū)域
*
* @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);
}
}
當(dāng)獲取canvas 實(shí)例后父款,可以將視頻流一樣畫(huà)置SurfaceView上面
View實(shí)現(xiàn)
步驟:
1、自定義View的屬性
2瞻凤、在自定義View的構(gòu)造方法中獲取View屬性值
3铛漓、重寫(xiě)onMeasure(int,int)方法
4、重寫(xiě)onDraw(Canvas canvas)方法
public class FaceView2 extends View implements Runnable {
private final String TAG = "FaceView";
/**
* 是否可以開(kāi)始繪制了
*/
private boolean mStart = false;
/**
* 默認(rèn)中間圓的半徑從0開(kāi)始
*/
private float currentRadius = 0;
/**
* 控件的寬度(默認(rèn))
*/
private int mViewWidth = 400;
/**
* 控件高度
*/
private int mViewHeight = 400;
/**
* 中心圓屏幕邊距
*/
private int margin;
/**
* 圓圈畫(huà)筆
*/
private Paint mPaint;
/**
* 提示文本
*/
private String mTipText;
/**
* 提示文本顏色
*/
private int mTipTextColor;
/**
* 提示文本顏色
*/
private int mTipTextSize;
/**
* 內(nèi)圓半徑
*/
private int mRadius;
/**
* 背景弧寬度
*/
private float mBgArcWidth;
/**
* 圓心點(diǎn)坐標(biāo)
*/
private Point mCenterPoint = new Point();
/**
* 圓弧邊界
*/
private RectF mBgRectF = new RectF();
/**
* 開(kāi)始角度
*/
private int mStartAngle = 105;
/**
* 結(jié)束角度
*/
private int mEndAngle = 330;
/**
* 設(shè)置默認(rèn)轉(zhuǎn)動(dòng)角度0
*/
float currentAngle = 0;
/**
* 圓弧背景畫(huà)筆
*/
private Paint mBgArcPaint;
/**
* 提示語(yǔ)畫(huà)筆
*/
private Paint mTextPaint;
/**
* 圓弧畫(huà)筆
*/
private Paint mArcPaint;
/**
* 漸變器
*/
private SweepGradient mSweepGradient;
/**
* 是否開(kāi)始
*/
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構(gòu)造");
initPaint(context);
}
/**
* 初始化控件View
*/
private void initPaint(Context context) {
//獲取界面焦點(diǎn)
setFocusable(true);
//保持屏幕長(zhǎng)亮
setKeepScreenOn(true);
//初始化值
margin = ScreenUtils.dp2px(context, 60);
mBgArcWidth = ScreenUtils.dp2px(context, 5);
//初始化畫(huà)筆
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(getResources().getColor(R.color.colorAccent));
mPaint.setStyle(Paint.Style.FILL);
//繪制文字畫(huà)筆
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);
//開(kāi)啟線程檢測(cè)
new Thread(this).start();
}
@SuppressLint("DrawAllocation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//測(cè)量view的寬度
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mViewWidth = MeasureSpec.getSize(widthMeasureSpec);
}
//測(cè)量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);
//獲取圓的相關(guān)參數(shù)
mCenterPoint.x = mViewWidth / 2;
mCenterPoint.y = mViewHeight / 2;
//外環(huán)圓的半徑
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;
//進(jìn)度條顏色 -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() {
//循環(huán)繪制畫(huà)面內(nèi)容
while (true) {
if (mStart) {
try {
changeValue();
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 動(dòng)態(tài)檢測(cè)改變值
*/
private void changeValue() {
// 內(nèi)圓形鲫构,放大效果
currentRadius += 20;
if (currentRadius > mRadius)
currentRadius = mRadius;
//外部圈的動(dòng)畫(huà)效果
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);
//繪制畫(huà)布內(nèi)容
drawContent(canvas);
}
/**
* 跟新提示信息
*
* @param title
*/
public void updateTipsInfo(String title) {
mTipText = title;
}
private void drawContent(Canvas canvas) {
//防止save()和restore()方法代碼之后對(duì)Canvas執(zhí)行的操作浓恶,繼續(xù)對(duì)后續(xù)的繪制會(huì)產(chǎn)生影響
canvas.save();
//先畫(huà)提示語(yǔ)
drawHintText(canvas);
//繪制正方形的框內(nèi)類(lèi)似人臉識(shí)別
// drawFaceRectTest(canvas);
//繪制人臉識(shí)別部分
drawFaceCircle(canvas);
//畫(huà)外邊進(jìn)度條
drawRoundProgress(canvas);
canvas.restore();
}
private void drawFaceCircle(Canvas canvas) {
//設(shè)置畫(huà)板樣式
Path path = new Path();
//以(400,200)為圓心,半徑為100繪制圓 指創(chuàng)建順時(shí)針?lè)较虻木匦温窂? path.addCircle(mCenterPoint.x, mCenterPoint.y, currentRadius, Path.Direction.CW);
// 是A形狀中不同于B的部分顯示出來(lái)
canvas.clipPath(path, Region.Op.DIFFERENCE);
// 半透明背景效果
canvas.clipRect(0, 0, mViewWidth, mViewHeight);
//繪制背景顏色
canvas.drawColor(getResources().getColor(R.color.viewBgWhite));
}
/**
* 繪制人臉識(shí)別界面進(jìn)度條
*
* @param canvas canvas
*/
private void drawRoundProgress(Canvas canvas) {
// 逆時(shí)針旋轉(zhuǎn)105度
canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y);
// 設(shè)置圓環(huán)背景
canvas.drawArc(mBgRectF, 0, mEndAngle, false, mBgArcPaint);
// 設(shè)置漸變顏色
mArcPaint.setShader(mSweepGradient);
canvas.drawArc(mBgRectF, 0, currentAngle, false, mArcPaint);
}
/**
* 從頭位置開(kāi)始動(dòng)畫(huà)
*/
public void resetPositionStart() {
currentAngle = 0;
isBack = false;
}
/**
* 動(dòng)畫(huà)直接完成
*/
public void finnishAnimator() {
currentAngle = mEndAngle;
isBack = false;
}
/**
* 停止動(dòng)畫(huà)
*/
public void pauseAnimator() {
isRunning = false;
}
/**
* 開(kāi)始動(dòng)畫(huà)
*/
public void startAnimator() {
isRunning = true;
}
/**
* 動(dòng)畫(huà)回退
*/
public void backAnimator() {
isRunning = true;
isBack = true;
}
/**
* 動(dòng)畫(huà)前進(jìn)
*/
public void forwardAnimator() {
isRunning = true;
isBack = false;
}
/**
* 繪制人臉識(shí)別提示
*
* @param canvas canvas
*/
private void drawHintText(Canvas canvas) {
//圓視圖寬度 (屏幕減去兩邊距離)
int cameraWidth = mViewWidth - 2 * margin;
//x軸起點(diǎn)(文字背景起點(diǎn))
int x = margin;
//寬度(提示框背景寬度)
int width = cameraWidth;
//y軸起點(diǎn)
int y = (int) (mCenterPoint.y - mRadius);
//提示框背景高度
int height = cameraWidth / 4;
Rect rect = new Rect(x, y, x + width, y + height);
canvas.drawRect(rect, mPaint);
//計(jì)算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);
}
/**
* 繪制人臉識(shí)別矩形區(qū)域
*
* @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);
}
}
這里值得注意的是這幾個(gè)方法结笨,使用的時(shí)候可以根據(jù)實(shí)際調(diào)用這幾個(gè)動(dòng)畫(huà)包晰。
/**
* 從頭位置開(kāi)始動(dòng)畫(huà)
*/
public void resetPositionStart() {
currentAngle = 0;
isBack = false;
}
/**
* 動(dòng)畫(huà)直接完成
*/
public void finnishAnimator() {
currentAngle = mEndAngle;
isBack = false;
}
/**
* 停止動(dòng)畫(huà)
*/
public void pauseAnimator() {
isRunning = false;
}
/**
* 開(kāi)始動(dòng)畫(huà)
*/
public void startAnimator() {
isRunning = true;
}
/**
* 動(dòng)畫(huà)回退
*/
public void backAnimator() {
isRunning = true;
isBack = true;
}
/**
* 動(dòng)畫(huà)前進(jìn)
*/
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">
<!--相機(jī)預(yù)覽顯示-->
<com.demo.facerecognition.view.FaceView
android:id="@+id/fv_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:tip_text="請(qǐng)閉眼"
app:tip_text_size="20sp"/>
<!--關(guān)閉按鈕-->
<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[]{
//獲取照相機(jī)權(quán)限
Manifest.permission.CAMERA,
};
/**
* 設(shè)置Android6.0的權(quán)限申請(qǐng)
*/
private void setPermissions() {
if (ContextCompat.checkSelfPermission(PreviewActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//Android 6.0申請(qǐng)權(quán)限
ActivityCompat.requestPermissions(this, PERMISSION, 1);
} else {
Log.i(TAG, "權(quán)限申請(qǐng)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);
//申請(qǐng)手機(jī)的權(quán)限
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("沒(méi)有檢測(cè)人臉");
break;
case 2:
faceView.backAnimator();
faceView.updateTipsInfo("請(qǐng)露正臉");
break;
case 3:
faceView.pauseAnimator();
faceView.updateTipsInfo(" 眨眨眼");
break;
case 4:
faceView.startAnimator();
faceView.updateTipsInfo("離近一點(diǎn)");
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實(shí)例
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 {
//打開(kāi)硬件攝像頭伐憾,這里導(dǎo)包得時(shí)候一定要注意是android.hardware.Camera
// Camera,open() 默認(rèn)返回的后置攝像頭信息
//設(shè)置角度,此處 CameraId 我默認(rèn) 為 1 (前置)
if (Camera.getNumberOfCameras() > 1) {
camera = Camera.open(1);
} else {
camera = Camera.open(0);
}
//設(shè)置相機(jī)角度
camera.setDisplayOrientation(90);
//通過(guò)SurfaceView顯示取景畫(huà)面
camera.setPreviewDisplay(surfaceHolder);
//開(kāi)始預(yù)覽
camera.startPreview();
//設(shè)置是否預(yù)覽參數(shù)為真
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) {//正在預(yù)覽
try {
camera.stopPreview();
camera.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
};
@Override
protected void onDestroy() {
if (camera != null) {
if (isPreview) {//正在預(yù)覽
try {
camera.stopPreview();
camera.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
super.onDestroy();
}
}
視頻流用的前置攝像頭赫模,這里的提示和動(dòng)畫(huà)也是隨機(jī)树肃,項(xiàng)目中可以根據(jù)真實(shí)情況展示。
【注意】自定義view應(yīng)該注意前后繪制順序和疊加模式
總結(jié)
項(xiàng)目中用到就簡(jiǎn)單抽了一下瀑罗,有遇到類(lèi)似需求可以參考一下胸嘴,最近在學(xué)Flutter,學(xué)的差不多了寫(xiě)個(gè)Flutter版本在更新一下斩祭。劣像。。
聲明:
轉(zhuǎn)載請(qǐng)注明出處
作者:戎碼蟲(chóng)
鏈接:http://www.reibang.com/p/625266dfab1d