Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing an eraser in an Android drawing app - black trail and then transparent

I have an drawing app for Android and I am currently trying to add a real eraser to it. Before, I had just used white paint for an eraser, but that won't do anymore since now I allow background colors and images. I do this by having an image view underneath my transparent canvas.

The problem that I am facing is that whenever I enable my eraser, it draws a solid black trail while I have my finger down, but once I release it goes to transparent. See the screen shots below:

This is how it looks while my finger is on the screen - a solid black trail How it looks while my finger is still on the screen

This is what it looks like once I remove my finger from the screen How it looks once I let go

So, it seems like I am getting close, but I can't find the right combination of settings to avoid the black trail while my finger is touching while erasing. Here are some relevant code snippets:

onDraw

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawColor(Color.TRANSPARENT);
    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
    canvas.drawPath(mPath, mPaint);
    canvas.drawPath(mPreviewPath, mPaint);
}

onTouchEvent

@Override
public boolean onTouchEvent(MotionEvent event) {
    float currentX = event.getX();
    float currentY = event.getY();

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            touchStart(currentX, currentY);
            invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            touchMove(currentX, currentY);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            touchUp(currentX, currentY);
            invalidate();
            break;
    }
    return true;
}

Current attempt at eraser settings

public void startEraser() {
    mPaint.setAlpha(0);
    mColor = Color.TRANSPARENT;
    mPaint.setColor(Color.TRANSPARENT);
    mPaint.setStrokeWidth(mBrushSize);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setMaskFilter(null);
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    mPaint.setAntiAlias(true);
}

There are several other posts about erasers, but most of them just say to use PorterDuff.Mode.CLEAR, setMakFilter(null) and that that should work. In my case, it doesn't. No matter what I try, I get the black trail first and then the desired result only after I release.

I can provide more code if necessary.

like image 938
Scott Avatar asked Aug 02 '14 12:08

Scott


3 Answers

I could suggest you to read the official sample of FingerPaint.java
It exactly matches what you are trying to achieve here.

To not show the trail when you erase content, take a look at the onDraw() method and the eraserMode variable:

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawColor(0xFFAAAAAA);
    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
    if (!eraserMode) {
        canvas.drawPath(mPath, mPaint);
    }
}

boolean eraserMode = false;

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    eraserMode = false;
    mPaint.setXfermode(null);
    mPaint.setAlpha(0xFF);
    switch (item.getItemId()) {
        /*...*/
        case ERASE_MENU_ID:
            // Add this line
            eraserMode = true;
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            return true;
        /*...*/
    }
    return super.onOptionsItemSelected(item);
}
like image 183
Simon Marquis Avatar answered Oct 18 '22 13:10

Simon Marquis


People Still looking for a shorter way to remove that black line while erasing. Just addsetLayerType(View.LAYER_TYPE_SOFTWARE, drawPaint); this line to the constructor and here you go. Cheers!

like image 40
Fahid Nadeem Avatar answered Oct 18 '22 14:10

Fahid Nadeem


None of the above answers, worked properly for me. After some hit and trial, and some logics as well, solved it, check the ACTION_MOVE

@Override
public boolean onTouchEvent(MotionEvent event) {

    float touchX = event.getX();
    float touchY = event.getY();

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            isEdited = true;
            drawPath.moveTo(touchX, touchY);
            break;
        case MotionEvent.ACTION_MOVE:
            if(isEraser) {
                drawPath.lineTo(touchX, touchY);
                drawCanvas.drawPath(drawPath, drawPaint);
                drawPath.reset();
                drawPath.moveTo(touchX, touchY);
            } else {
                drawPath.lineTo(touchX, touchY);
            }
            break;
        case MotionEvent.ACTION_UP:
            drawCanvas.drawPath(drawPath, drawPaint);
            drawPath.reset();
            break;
        default:
            return false;
    }

    invalidate();
    return true;
}

and this

public void setEraser(boolean isEraser) {
    this.isEraser = isEraser;
    if (isEraser) {
        drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    } else {
        drawPaint.setXfermode(null);
    }
}
like image 38
Rakesh Yadav Avatar answered Oct 18 '22 14:10

Rakesh Yadav