Maintain background in Drawingview

Asked

Viewed 113 times

8

I’m needing an application to draw on a specific background, the code is working all right, but when I’m going to delete/correct a wrong line, it erases the background and the background should be maintained. However, it is deleting the background image together (the lines).

INITIAL IMAGE (Before Drawing)

Imagem inicial, antes de iniciar o desenho

PICTURE WITH SOMETHING DRAWN

Imagem com um desenho já iniciado

AFTER MAKING A CORRECTION TO THE DRAWING

Imagem após efetuar uma correção no desenho

Drawingview code

public class DrawingView extends View implements View.OnTouchListener {

    private Canvas m_Canvas;

    private Path m_Path;

    private Paint m_Paint;

    private ArrayList<Pair<Path, Paint>> paths = new ArrayList<Pair<Path, Paint>>();

    private ArrayList<Pair<Path, Paint>> undonePaths = new ArrayList<Pair<Path, Paint>>();

    private float mX, mY;

    private static final float TOUCH_TOLERANCE = 4;

    private boolean isEraserActive = false;

    public DrawingView(Context context, AttributeSet attr) {
        super(context, attr);

        setFocusable(true);

        setFocusableInTouchMode(true);

        //setBackgroundColor(Color.WHITE);
        //setBackgroundColor(Color.TRANSPARENT);

        this.setOnTouchListener(this);

        onCanvasInitialization();
    }

    public void onCanvasInitialization() {

        m_Paint = new Paint();

        m_Paint.setAntiAlias(true);
        m_Paint.setDither(true);
        m_Paint.setColor(Color.parseColor("#000000"));
        m_Paint.setStyle(Paint.Style.STROKE);
        m_Paint.setStrokeJoin(Paint.Join.ROUND);
        m_Paint.setStrokeCap(Paint.Cap.ROUND);
        m_Paint.setStrokeWidth(3);


        m_Canvas = new Canvas();
        m_Path = new Path();
        Paint newPaint = new Paint(m_Paint);
        paths.add(new Pair<Path, Paint>(m_Path, newPaint));

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    public boolean onTouch(View arg0, MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (Pair<Path, Paint> p : paths) {
            canvas.drawPath(p.first, p.second);
        }
    }

    private void touch_start(float x, float y) {

        if (isEraserActive) {

            //m_Paint.setColor(Color.WHITE);
            m_Paint.setColor(Color.WHITE);
            //m_Paint.reset();
            m_Paint.setFilterBitmap(true);
            m_Paint.clearShadowLayer();

            m_Paint.setStrokeWidth(20);
            Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
            paths.add(new Pair<Path, Paint>(m_Path, newPaint));

        } else {
            m_Paint.setColor(Color.BLACK);
            m_Paint.setStrokeWidth(3);
            Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
            paths.add(new Pair<Path, Paint>(m_Path, newPaint));

        }


        m_Path.reset();
        m_Path.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            m_Path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
    }

    private void touch_up() {
        m_Path.lineTo(mX, mY);

        // commit the path to our offscreen
        m_Canvas.drawPath(m_Path, m_Paint);

        // kill this so we don't double draw
        m_Path = new Path();
        Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
        paths.add(new Pair<Path, Paint>(m_Path, newPaint));
    }

    public void activateEraser()
    {
        m_Paint.setColor(Color.TRANSPARENT);
        isEraserActive = true;
    }


    public void deactivateEraser()
    {
        isEraserActive = false;
    }

    public boolean isEraserActive()
    {
        return isEraserActive;
    }


    public void reset()
    {
        paths.clear();

        invalidate();
    }

}

Note: I’ve tried using Color.TRANSPARENT, but it doesn’t work.

1 answer

6


Instead of drawing the paths directly into Canvas of View use one another Canvas associated with a Bitmap and draw on it.

In the onDraw() draw this Bitmap on the canvas of View.

Declare two new attributes in the class Drawingview:

public class DrawingView extends View implements View.OnTouchListener {

    //Bitmap e Canvas usados para desenhar.
    private Bitmap strokes;
    private Canvas strokeCanvas;

    private Canvas m_Canvas;
    ........
    ........
    ........
}

Create them in the method onSizeChanged()

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    //Cria o Bitmap e o Canvas onde o desenho será feito
    if (w != oldw || h != oldh) {
        strokes = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        strokeCanvas = new Canvas(strokes);
    }

    super.onSizeChanged(w, h, oldw, oldh);
}

Change the method onDraw so as paths be drawn in that Canvas and draw the Bitmap in the Canvas of View:

@Override
protected void onDraw(Canvas canvas) {

    for (Pair<Path, Paint> p : paths) {
        //As paths são desenhadas no strokeCanvas
        strokeCanvas.drawPath(p.first, p.second);
    }

    //O Bitmap que tem o desenho é desenhado no canvas da view.
    canvas.drawBitmap(strokes,0,0,null);
} 

For testing I used this layout:

Main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btErase"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="Erase"
        android:onClick="eraseClicked"/>

    <Button
        android:id="@+id/btDraw"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="Draw"
        android:onClick="drawClicked"/>

    <aSua.packageName.DrawingView
        android:id="@+id/drawingView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/fundo" />

</LinearLayout>

With the following Activity:

public class MainActivity extends Activity {

    DrawingView drawingView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        drawingView = (DrawingView)findViewById(R.id.drawingView);
    }

    public void eraseClicked(View view) {
        drawingView.activateEraser();
    }

    public void drawClicked(View view) {
        drawingView.deactivateEraser();
    }

}

It has nothing to do with this but during the tests I noticed that I needed to add m_Paint.setXfermode(null); in the method deactivateEraser()

public void deactivateEraser()
{
    m_Paint.setXfermode(null);//<===== Faltava isto
    isEraserActive = false;
}
  • In the case where this the m_Paint.setColor(Color.WHITE) I change it to m_Paint.setColor(Color.TRANSPARENT) ?

  • No. Let it be as it is.

  • It this m_Paint.setColor(Color.WHITE); there it will not erase, will paint white over

  • I say this in the method touch_start, in the first if (if the user is deleting)

  • I tested only with the changes I indicated in the reply and it works.

  • I’ll check again, thanks.

  • Could you edit the answer and show how you prompted the Drawingview class ? I did everything in your reply and it didn’t work.

  • I added to the Activity response I used for testing.

  • Thanks @ramaral, it worked correctly now! In 6 hours send the points.

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.