Android Canvas清屏失效

自定义控件时经常用到Canvas,画新的东西之前需要先清除画布内容,人脸识别项目中需要准确画出当前人脸位置,清空上一帧位置。

关于清除画布内容网上有两种非常流行的方法:

方法一:

        mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
方法二:

        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        mCanvas.drawPaint(paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
遗憾的是这两种方法在我自定义的SurfaceView中都不起作用,尝试结果如出一辙,左右晃一下 画面如下:
Android Canvas清屏失效_第1张图片

我的解决方案是在每次设置Path画线前,清空一下Path,问题迎刃而解。

清除Path中的内容有两个方法:
reset( )不保留内部数据结构,但会保留FillType.
rewind( )会保留内部的数据结构,但不保留FillType

关于Path其他更多方法解释详见这篇博客。

自定义SurfaceView代码如下:

/**
 * Title:
 * Description:
 * Company: 北京****科技有限公司,010-62538800,[email protected]
 *
 * @author Created by ylwang on 2018/1/30
 */


public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mHolder;
    private Canvas mCanvas;//绘图的画布
    private volatile boolean mIsDrawing;//控制绘画线程的标志位

    private Paint mPaint;
    private Path mPath;

    public CustomSurfaceView(Context context) {
        super(context);
        initView();
    }

    public CustomSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public CustomSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private Gson gson;
    private List listFaces;


    public void setFaceInfo(String faceInfo) {
        FaceBean bean = gson.fromJson(faceInfo, FaceBean.class);
        if (null != bean && null != bean.getFaces()) {
            listFaces = bean.getFaces();
        }
    }

    private void initView() {
        mPaint = new Paint();
        mPaint.setColor(Color.GREEN);//画笔颜色
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setStrokeWidth(6);//画笔宽度
        mPaint.setStyle(Paint.Style.STROKE);//空心

        mPath = new Path();

        mHolder = getHolder();//获取SurfaceHolder对象
        //TODO:保证该SurfaceView在最上层,避免两个SurfaceView叠加,遮挡问题
        mHolder.setFormat(PixelFormat.TRANSPARENT);
        setZOrderOnTop(true);

        mHolder.addCallback(this);//注册SurfaceHolder的回调方法
        setFocusable(true);// 能否用键盘获得焦点
        setFocusableInTouchMode(true);//能否通过触摸获得焦点
        this.setKeepScreenOn(true);//屏幕常亮

        gson = new Gson();
        listFaces = new ArrayList<>();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    public static final int TIME_IN_FRAME = 20;

    @Override
    public void run() {
        long start;
        while (mIsDrawing) {
            start = SystemClock.uptimeMillis();//开始绘制
            synchronized (this) {
                setPathToCanvas();
            }
            //保证稳定帧率刷新界面
            while (SystemClock.uptimeMillis() - start <= TIME_IN_FRAME) {
                Thread.yield();//线程让出
            }
        }
    }

    private void setPathToCanvas() {
        mPath.rewind();//TODO:必须重置,否则不能清屏
        //mPath.reset();
        // mPath = new Path();
        for (FaceBean.FacesEntity face : listFaces) {
            int[] ps = getPositions(face.getFace_rectangle());
            mPath.moveTo(ps[0], ps[1]);
            for (int i = 2; i < ps.length - 1; i += 2) {
                mPath.lineTo(ps[i], ps[i + 1]);
            }
            mPath.close();//第一个点连接到最后一个点,形成一个闭合区域
            //mPath.lineTo(ps[0], ps[1]);
            drawSth();
        }
    }

    /**
     * 获取每个人脸的四点位置坐标
     *
     * @param fre
     * @return
     */
    private int[] getPositions(FaceBean.FacesEntity.Face_rectangleEntity fre) {
        int[] faces = new int[8];
        faces[0] = fre.getLeft_top().getX();
        faces[1] = fre.getLeft_top().getY();
        faces[2] = fre.getRight_top().getX();
        faces[3] = fre.getRight_top().getY();
        faces[4] = fre.getRight_bottom().getX();
        faces[5] = fre.getRight_bottom().getY();
        faces[6] = fre.getLeft_bottom().getX();
        faces[7] = fre.getLeft_bottom().getY();
        for (int i = 0; i < faces.length; i++) {
            faces[i] *= 2;
            if (i % 2 == 0) {
                faces[i] = (640 * 2) - faces[i];
            }
        }
        return faces;
    }

    private void drawSth() {
        try {
            mCanvas = mHolder.lockCanvas(); //拿到当前画布 然后锁定
            mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            mCanvas.drawPath(mPath, mPaint);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas);//保证每次都将绘图的内容提交
            }
        }
    }
}
说明一下,多个SurfaceView叠加可能会遇到的问题, 请参考这篇博客。

        mHolder = getHolder();//获取SurfaceHolder对象
        //TODO:保证该SurfaceView在最上层,避免两个SurfaceView叠加,遮挡问题
        mHolder.setFormat(PixelFormat.TRANSPARENT);
        setZOrderOnTop(true);

你可能感兴趣的:(Android)