Question

I have the following code to activate/deactivate the eraser:

public PorterDuffXfermode clear = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);    
eraseB.setOnClickListener(new OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        if (!eraser) {
                            eraser = true;
                            eraseB.setImageResource(R.drawable.erase_on);
                            paint = new Paint(Paint.DITHER_FLAG);
                            paint.setColor(0x00000000);
                            paint.setAlpha(0x00);
                            paint.setXfermode(clear);
                            paint.setStyle(Paint.Style.STROKE);
                            paint.setStrokeJoin(Paint.Join.ROUND);
                            paint.setStrokeCap(Paint.Cap.ROUND);
                            paint.setStrokeWidth(stroke);
                            paintv.setPaint(paint);
                        } else {
                            eraser = false;
                            eraseB.setImageResource(R.drawable.erase);
                            paint = new Paint(Paint.DITHER_FLAG);
                            paint.setDither(true);
                            paint.setXfermode(null);
                            paint.setColor(Color.RED);
                            paint.setStyle(Paint.Style.STROKE);
                            paint.setStrokeJoin(Paint.Join.ROUND);
                            paint.setStrokeCap(Paint.Cap.ROUND);
                            paint.setStrokeWidth(stroke);
                            paintv.setPaint(paint);
                        }
                    }
                });

setPaint is from my customView:

public void setPaint(Paint paint) {
    this.paint = paint;
    LogService.log("in setPaint", "paint = " + paint);
}

and onDraw I use:

canvas.drawPath(mPath, paint);

If I deactivate the eraser, it will draw with a red line, but instead, if I activate the eraser, instead of erasing, it will draw a black line. How can I fix this

Was it helpful?

Solution

Canvas does not support eraser while Bitmap does.

Basic workaround flow:

  1. Create another canvas

  2. Create a bitmap

  3. Set that bitmap to that canvas

    public void init(int width, int height) {
        Log.i(TAG,"init with "+width+"x"+height);
        foreground = Bitmap.createBitmap(width, height, Config.ARGB_8888);
        cacheCanvas = new Canvas();
        cacheCanvas.setBitmap(foreground);
    }
    
  4. Record the touches on that bitmap, including paint and eraser

    public boolean onTouchEvent(MotionEvent event) {
        float eventX = event.getX();
        float eventY = event.getY();
    
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            currentStroke = new Stroke();
            currentStroke.color = paint;
            currentStroke.path.moveTo(eventX, eventY);
            currentStroke.path.lineTo(eventX, eventY);
    
            synchronized (strokes) {
                strokes.add(currentStroke);
            }
            lastTouchX = eventX;
            lastTouchY = eventY;
            // There is no end point yet, so don't waste cycles invalidating.
            return true;
    
        case MotionEvent.ACTION_MOVE:
        case MotionEvent.ACTION_UP:
            // Start tracking the dirty region.
            resetDirtyRect(eventX, eventY);
    
            // When the hardware tracks events faster than they are delivered,
            // the
            // event will contain a history of those skipped points.
            int historySize = event.getHistorySize();
            for (int i = 0; i < historySize; i++) {
                float historicalX = event.getHistoricalX(i);
                float historicalY = event.getHistoricalY(i);
            expandDirtyRect(historicalX, historicalY);
                if (i == 0) {
                    lastX = historicalX;
                    lastY = historicalY;
                    currentStroke.path.lineTo(historicalX, historicalY);
                } else {
                    currentStroke.path.quadTo(lastX, lastY,
                        (historicalX + lastX) / 2,
                        (historicalY + lastY) / 2);
                }
            }
    
        // After replaying history, connect the line to the touch point.
            if(historySize==0){
                long duration=event.getEventTime()-event.getDownTime();
                float offset=0.1f;
                if(duration<300){
                    offset=50.0f/duration;
                }
                currentStroke.path.lineTo(eventX+offset, eventY+offset);
            }else{
                currentStroke.path.lineTo(eventX, eventY);
            }
            synchronized (strokes) {
                strokes.add(currentStroke);
            }
    
            break;
    
        default:
        return false;
        }
    
    // Include half the stroke width to avoid clipping.
        float width = paint.getStrokeWidth() / 2;
        invalidate((int) (dirtyRect.left - width),
            (int) (dirtyRect.top - width), (int) (dirtyRect.right + width),
            (int) (dirtyRect.bottom + width));
    
        lastTouchX = eventX;
        lastTouchY = eventY;
    
        return true;
        }
    
  5. Draw that bitmap on the canvas of your View

    protected void onDraw(Canvas canvas) {
        synchronized (strokes) {
            if (strokes.size() > 0) {
                for (Stroke s : strokes) {
                    cacheCanvas.drawPath(s.path, s.color);
                }
                canvas.drawBitmap(foreground, 0, 0, null);
                strokes.clear();
            }
        }
    }
    

OTHER TIPS

If you are drawing on solid color background, use the paint of same color in brush to that of background and the brush will behave like an eraser.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top