PorterDuff.Mode.CLEAR 在画布上绘制黑色

问题描述

我想在我的绘画应用中实现橡皮擦。但是代码

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

在画布上绘制黑线。如果我要更改背景颜色,画布也会在其上绘制黑色。

我也尝试过使用 setLayerType() 但它会在任何颜色的背景上绘制白色。

// In constructor
setLayerType(View.LAYER_TYPE_SOFTWARE,null); 

以下是我的 PaintView 的代码

public class PaintView extends View {
    private Bitmap bitmapBackground,bitmapView;
    private int backgroundColor;
    private int brushSize;
    private int eraserSize;
    private float mX,mY;
    private Canvas canvas=null;
    private final int TOUCH_TOLERANCE=4;
    private int paintColor;

    private int modeStatus;
    /*
    1 for brush
    2 for eraser
    */
    
    private ArrayList<Paint> paints = new ArrayList<>();
    private ArrayList<Path> paths = new ArrayList<>();
    private int historyPointer=0;

    public PaintView(Context context,@Nullable AttributeSet attrs) {
        super(context,attrs);
        initialise();
    }

    private void initialise() {
        eraserSize=12;
        brushSize=12;
        backgroundColor= Color.WHITE;
        paintColor = Color.BLACK;
        modeStatus = 1;

        paints.add(createPaint());
        paths.add(new Path());
        historyPointer++;
    }

    private float toPx(int brushSize) {
        return brushSize*(getResources().getdisplayMetrics().density);
    }

    public void init(int width,int height) {
        bitmapBackground=Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
        bitmapView=Bitmap.createBitmap(width,Bitmap.Config.ARGB_8888);
        canvas=new Canvas(bitmapView);
    }

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

        canvas.drawColor(backgroundColor);
        canvas.drawBitmap(bitmapBackground,null);

        for (int i=0;i<historyPointer;i++) {
            Path path = paths.get(i);
            Paint paint = paints.get(i);

            canvas.drawPath(path,paint);
        }

    }

    private Paint createPaint() {
        Paint paint = new Paint();

        paint.setColor(paintColor);
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStyle(Paint.Style.stroke);
        paint.setstrokeCap(Paint.Cap.ROUND);
        paint.setstrokeJoin(Paint.Join.ROUND);

        if (modeStatus==1) {
            paint.setXfermode(null);
            paint.setShader(null);
            paint.setMaskFilter(null);
            paint.setstrokeWidth(toPx(brushSize));
        }
        else {
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            paint.setstrokeWidth(toPx(eraserSize));
        }

        return paint;
    }

    private Path createPath(float x,float y) {
        Path path = new Path();
        path.moveto(x,y);
        return path;
    }

    public void setBackgroundColor(int backgroundColor) {
        this.backgroundColor=backgroundColor;
        invalidate(); //Redraw
    }

    public void setBrushSize(int brushSize) {
        this.brushSize=brushSize;
        modeStatus=1;
    }

    public void setBrushColor(int color) {
        paintColor=color;
    }

    public void seteraserSize(int eraserSize) {
        this.eraserSize=eraserSize;
        modeStatus=2;
    }

    public int getBrushSize() {
        return this.brushSize;
    }

    public int geteraserSize() {
        return this.eraserSize;
    }

    private void updateHistory(Path path) {
        if (historyPointer==paths.size()) {
            paths.add(path);
            paints.add(createPaint());
            historyPointer++;
        }
        else {
            // For undo and redo
            paths.set(historyPointer,path);
            paints.set(historyPointer,createPaint());
            historyPointer++;

            for (int i=historyPointer,size=paths.size();i<size;i++) {
                paths.remove(historyPointer);
                paints.remove(historyPointer);
            }
        }
    }

    private Path getCurrentPath() {
        return paths.get(historyPointer-1);
    }

    private Paint getCurrentPaint() {
        return paints.get(historyPointer-1);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            // Case when finger touches the screen
            case MotionEvent.ACTION_DOWN:
                touchStart(x,y);
                break;

            // Case when finger moves on screen
            case MotionEvent.ACTION_MOVE:
                touchMove(x,y);
                break;

            // Case when finger is taken away from screen
            case MotionEvent.ACTION_UP:
                touchUp();
                break;

            default :
                return false;
        }
        return true;
    }

    private void touchStart(float x,float y) {
        mX=x;
        mY=y;
        updateHistory(createPath(x,y));
    }

    private void touchMove(float x,float y) {
        float dx = Math.abs(x-mX);
        float dy = Math.abs(y-mY);

        Path path = getCurrentPath();

        if (dx>=TOUCH_TOLERANCE || dy>=TOUCH_TOLERANCE) {
            path.quadTo(x,y,(x+mX)/2,(y+mY)/2);
            mX=x;
            mY=y;
        }
        invalidate();;
    }

    private void touchUp() {
    }
}

解决方法

经过多次研究,我得到了答案,以下是有效的。 设置背景颜色后保存画布层,最后恢复计数。 onDraw方法如下-

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

    canvas.drawColor(backgroundColor);
    canvas.drawBitmap(bitmapBackground,null);

    int layerId = canvas.saveLayer(0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG);  // Line 1 added

    for (int i=0;i<historyPointer;i++) {
        Path path = paths.get(i);
        Paint paint = paints.get(i);

        canvas.drawPath(path,paint);
    }

    canvas.restoreToCount(layerId); // Line 2 added
}