Android:绘制自定义View人脸识别框

一.绘制矩形框实现

项目开发需要自定义View实现一个人脸框,代码实现很平常,一些细节记录一下,方便以后查阅。

代码实现:

1.1 自定义人脸识别框:

FaceView.java

package com.android.example.ui.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.android.example.R;

import java.util.List;

public class FaceView extends View {

    //人脸识别框数组
    private List mRectList;
    //画笔
    private Paint mPaint;

    public FaceView(Context context) {
        super(context);
        init();
    }

    /* 这个构造函数不能缺失,否则会编译报错
     * View走的构造函数也是这个,第一个构造函数反而没走
     */
    public FaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public FaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint();
        //设置抗锯齿
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        mPaint.setColor(Color.RED);
    }

    public void setFaceRects(List mRectList) {
        this.mRectList = mRectList;
        //请求更新,View会自动执行onDraw进行绘制
        invalidate();
        //同样是请求更新,该方法表示在主线程发起绘制请求
        //postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mRectList != null) {
            for (Rect rect : mRectList) {
                canvas.drawRect(rect, mPaint);
            }
        }
    }
}

1.2 布局文件中添加FaceView:

注意:

这里我把FaceView的layout_width、layout_height都设置成了"match_parent"

这里面有个容易踩到
本次开发时,xml中FaceView控件往上一直到第一层父布局宽、高的设置都是:
layout_width="match_parent"
layout_height="wrap_content"

导致View的width、height无法确定,所以onDraw()一直不被执行
后来才发现是这里的问题
将控件FaceView控件宽高设置为固定大小就可以了

也就是说如果自定义View的宽高无法被确定,OnDraw()是不会执行的
这时候就需要排查设置宽高,或者修改布局配置使其值能够被确定。

1.3 代码调用:

List rectlist = new ArrayList();
Rect rect1 = new Rect(300, 300, 500, 500);
rectlist.add(rect1);

FaceView faceRectsView = findViewById(R.id.face_rects_view);
faceRectsView.setFaceRects(rectlist);

1.4 实现效果

Android:绘制自定义View人脸识别框_第1张图片

一个完整的自定义人脸识别框FaceView到此就实现了

顺带手在onDraw里可以做出其他不同的效果

二.绘制线段实现

使用canvas.drawLine()函数绘制线段,使用实现一个只显示四个角的人脸识别框

2.1 代码:

@Override
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);

	if (mRectList != null) {
		for (Rect rect : mRectList) {
			/*左上角竖线*/
			canvas.drawLine(rect.left, rect.top, rect.left, rect.top + 20, mPaint);
			/*左上角横线*/
			canvas.drawLine(rect.left, rect.top, rect.left + 20, rect.top, mPaint);
			/*右上角竖线*/
			canvas.drawLine(rect.right, rect.top, rect.right - 20, rect.top, mPaint);
			/*右上角横线*/
			canvas.drawLine(rect.right, rect.top, rect.right, rect.top + 20, mPaint);
			/*左下角竖线*/
			canvas.drawLine(rect.left, rect.bottom, rect.left, rect.bottom - 20, mPaint);
			/*左下角横线*/
			canvas.drawLine(rect.left, rect.bottom, rect.left + 20, rect.bottom, mPaint);
			/*右下角竖线*/
			canvas.drawLine(rect.right, rect.bottom, rect.right, rect.bottom - 20, mPaint);
			/*右下角横线*/
			canvas.drawLine(rect.right, rect.bottom, rect.right - 20, rect.bottom, mPaint);
		}
	}
}

2.2 效果

Android:绘制自定义View人脸识别框_第2张图片

还有其他有趣的效果:圆形、椭圆等,就不一一列举了。

三.绘制Bitmap实现

3.1 实现过程

上一节讲述的都是用画布Cavas、画笔Paint自带的绘制线段、矩形等Api实现人脸框

这一节讲怎么使用UI设计的图片绘制人脸框,其实也就是onDraw()中绘制Bitmap

人脸框图片facerect.png:

Android:绘制自定义View人脸识别框_第3张图片

onDraw()绘制Bitmap代码:

package com.android.example.ui.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.android.example.R;

import java.util.List;

public class FaceView extends View {

    private List mRectList;
    private Paint mPaint;
    private Bitmap mFaceRectBitmap;

    public FaceView(Context context) {
        super(context);
        init();
    }

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

    public FaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint();
        //不需要再配置画笔
        /*mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        mPaint.setColor(Color.RED);*/

        /*图片解析成Bitmap
         *Android不允许直接修改res里面的图片,所以要用copy方法*/
        mFaceRectBitmap = BitmapFactory.decodeResource(getResources(),
                R.mipmap.perception_facerect).copy(Bitmap.Config.ARGB_8888, true);
    }

    public void setFaceRects(List mRectList) {
        this.mRectList = mRectList;
        invalidate();
    }

    //不需要再绘制时调用,销毁资源
    public void clear() {
        mRectList = null;
        mFaceRectBitmap.recycle();
        mFaceRectBitmap = null;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mRectList != null && mFaceRectBitmap != null) {
            for (Rect rect : mRectList) {
                //drawBitmap()有多个重载,接下来会详细讨论
                canvas.drawBitmap(mFaceRectBitmap, rect.left, rect.top, mPaint);
            }
        }
    }
}

注: 画笔Paint只需要有一个实例对象就行,不需要再进行配置

3.2 实现效果

Android:绘制自定义View人脸识别框_第4张图片

 

四.canvas.drawBitmap()

上一节代码注释中提到了,canvas.drawBitmap()这个函数有多个重载

下面就来详细研究一下Canvas里关于绘制Bitmap的方法:

  1. drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
  2. drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
  3. drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
  4. drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)

方法1方法2只有dst参数类型 Rect 和 RectF 的区别
这两个方法要实现的功能都是把Bitmap的src区域绘制到画布dst区域内
如果dst无法完全覆盖src,而src和dst比例不对称,就会出现拉伸或缩放

方法3是把(left, top)作为Bitmap的左上角进行绘制,不拉伸也不压缩,如果超出了画布就不绘制,佛系绘制。

方法4是使用矩阵matrix绘制Bitmap,绘制前先设置matrix的属性
该方法主要是对图片进行缩放旋转平移等操作时使用
示例:在坐标点(100,100),顺时针旋转45°绘制图片
关键代码:

//配置矩阵
Matrix matrix = new Matrix();
matrix.postTranslate(100f, 100f);
matrix.preRotate(45f);
//绘制       
canvas.drawBitmap(mFaceRectBitmap, matrix, mPaint);

实现效果:
Android:绘制自定义View人脸识别框_第5张图片

 

五.onDraw()不执行问题

在前文1.2小节里有提到,onDraw()出现不执行问题

实现自定义View时,这也是个常见的问题,一般可以尝试如下几种方法:

  • 主动调用invalidate()
  • 在构造方法里增加setWillNotDraw(false)方法
  • View的宽高是否能被系统确定,如果不能就需要设置确定的宽高,或者修改布局使其能被系统确定
  • 在onMeasure()方法中没设置控件的宽和高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

 

六.结束

到此自定义人脸识别框FaceView及drawBitmap()相关扩展就讲解完了

canvas有十分丰富的功能,能实现多种多样的自定义View,这就要在实际开发中去具体运用了

你可能感兴趣的:(android)