Android: Draw a custom View face recognition box

1. Implementation of drawing a rectangular frame

Project development requires a custom View to implement a face frame. The code implementation is very common. Record some details for easy reference later.

Code:

1.1 Custom face recognition frame:

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<Rect> 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<Rect> 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 Add FaceView to the layout file:

<com.android.example.ui.view.FaceView
    android:id="@+id/face_rects_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Notice:

Here I set the layout_width and layout_height of FaceView to "match_parent"

There is a pit here that is easy to step on
. During this development, the width and height settings of the FaceView control in the xml all the way to the first-layer parent layout are:
layout_width="match_parent"
layout_height="wrap_content"

causes the width and height of the View Unable to determine, so onDraw() has never been executed.
Later I found out that this is the problem here.
Just set the width and height of the FaceView control to a fixed size.

<com.android.example.ui.view.FaceView
    android:id="@+id/face_rects_view"
    android:layout_width="800dp"
    android:layout_height="800dp"/>

That is to say, if the width and height of the custom View cannot be determined, OnDraw() will not be executed. At
this time, you need to check and set the width and height, or modify the layout configuration so that the value can be determined.

1.3 Code call:

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

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

1.4 Achieve results

A complete custom face recognition frame FaceView is now implemented

By the way, you can create other different effects in onDraw.

2. Implementation of drawing line segments

Use the canvas.drawLine() function to draw line segments and implement a face recognition frame that only displays four corners.

2.1 Code:

@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 Effect

There are other interesting effects: circles, ovals, etc., I won’t list them all.

3. Draw Bitmap implementation

3.1 Implementation process

The previous section described how to implement face frames using APIs such as Cavas and Paint to draw line segments and rectangles.

This section talks about how to use UI designed pictures to draw face frames, which is actually drawing Bitmap in onDraw().

Face frame picture facerect.png:

onDraw() drawing Bitmap code:

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

Note:  The brush Paint only needs to have an instance object, and no further configuration is required.

3.2 Achieve results

4.canvas.drawBitmap()

As mentioned in the code comments in the previous section, the canvas.drawBitmap() function has multiple overloads

Let's study in detail the method of drawing Bitmap in Canvas:

  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)

The only difference between Method 1 and Method 2 is the dst parameter type Rect and RectF.
The functions to be implemented by these two methods are to draw the src area of ​​the Bitmap into the dst area of ​​the canvas.
If dst cannot completely cover src, and the proportion of src and dst is asymmetric, Stretching or scaling will occur

Method 3 is to draw (left, top) as the upper left corner of the Bitmap without stretching or compressing it. If it exceeds the canvas, it will not be drawn. It is a Buddhist drawing.

Method 4 is to use the matrix matrix to draw the Bitmap. Set the properties of the matrix before drawing.
This method is mainly used when scaling, rotating and translating the picture.
Example: At the coordinate point (100,100), rotate 45° clockwise to draw the picture.
Key code:

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

Realization effect:

5.onDraw() does not execute problem

As mentioned in the previous section 1.2, onDraw() has a non-execution problem.

This is also a common problem when implementing a custom View. Generally, you can try the following methods:

  • Actively call invalidate()
  • Add setWillNotDraw(false) method to the constructor
  • Whether the width and height of the View can be determined by the system. If not, you need to set a certain width and height, or modify the layout so that it can be determined by the system.
  • The width and height of the control are not set in the onMeasure() method
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

6. End

This concludes the explanation of the custom face recognition frame FaceView and drawBitmap() related extensions.

Canvas has very rich functions and can implement a variety of customized views, which must be used in actual development.

Guess you like

Origin blog.csdn.net/geyichongchujianghu/article/details/131241383