Samkough
Samkough

Reputation: 105

Every time I try to change a property of my paint (thickness or color), it changes existing paint that's already on the Canvas (android) (java)

I'm trying to create a simple drawing app that allows you to draw on a canvas, and you're able to change the color and thickness of it. The problem that's occurring is that every time I try to change the color or thickness of the paint, it changes ALREADY existing paint that's on the canvas. I understand why it's not working, but I just don't know how to fix it and where exactly the problem lies.

CanvasView Class

package samkough.com.painter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;

public class CanvasView extends View
{
    /*
    * When the user touches the screen and moves their finger to draw,
    * we will use a Path to trace their drawing action on the canvas.
    * Both the canvas and the drawing on top of it are represented by Paint
    * objects. The initial paint color corresponds to the first color
    * in the palette we created last time, which will be initially selected
    * when the app launches. Finally we declare variables for the canvas
    * and bitmap - the user paths drawn with drawPaint will be drawn onto
    * the canvas, which is drawn with canvasPaint.
    * */
    //drawing paint
    private Paint paint = new Paint();
    // canvas paint
    private Paint canvasPaint = new Paint();
    //drawing path
    private Path path = new Path();
    // canvas
    private Canvas canvas = new Canvas();
    //canvas bitmap
    private Bitmap canvasBitmap;
    // brush size and pixel size
    private float brushSize, pixelAmount;

    public CanvasView(Context context, AttributeSet attrs)
    {
        // Setting the anti-alias, stroke join and cap styles will make the user's drawings appear smoother.
        super(context, attrs);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(5);
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(canvasBitmap);
    }

    @Override
    protected void onDraw(Canvas drawCanvas)
    {
        drawCanvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
        drawCanvas.drawPath(path, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e)
    {
        // get the coords of the touch event
        float eventX = e.getX();
        float eventY = e.getY();

        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // set a new starting point
                path.moveTo(eventX, eventY);
                path.reset();
                path.moveTo(eventX, eventY);
                return true;
            case MotionEvent.ACTION_MOVE:
                // connect the points
                path.lineTo(eventX, eventY);
                break;
            default:
                return false;
        }

        // makes you view repaint and call ondraw
        invalidate();
        return true;
    }

    public void clearCanvas()
    {
        path.reset();
        invalidate();
    }

    public void setStrokeWidth(float f)
    {
        pixelAmount = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, f, getResources().getDisplayMetrics());
        brushSize = pixelAmount;

        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(brushSize);
        invalidate();
    }

    public void setColor(int p)
    {
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setColor(p);
        invalidate();
    }
}

MainActivity Class

package samkough.com.painter;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;

import com.samkough.painter.R;

public class MainActivity extends Activity {

    private CanvasView canvasView;
    private int orange;
    private int purple;
    private float strokeWidth;

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

        canvasView = (CanvasView) findViewById(R.id.canvasView);
        orange = Color.rgb(250, 128, 0);
        purple = Color.rgb(128, 0, 128);
        strokeWidth = 0;

        // REGULAR BUTTONS: save, about, reset
        Button saveB = (Button) findViewById(R.id.saveButton);
        Button aboutB = (Button) findViewById(R.id.aboutButton);
        Button resetB = (Button) findViewById(R.id.resetButton);

        // IMAGE BUTTONS: red, blue, green, yellow, black, purple, orange, erase, brush thickness plus, brush thickness minus
        ImageButton redIb = (ImageButton) findViewById(R.id.redButton);
        ImageButton blueIb = (ImageButton) findViewById(R.id.blueButton);
        ImageButton greenIb = (ImageButton) findViewById(R.id.greenButton);
        ImageButton yellowIb = (ImageButton) findViewById(R.id.yellowButton);
        ImageButton blackIb = (ImageButton) findViewById(R.id.blackButton);
        ImageButton purpleIb = (ImageButton) findViewById(R.id.purpleButton);
        ImageButton orangeIb = (ImageButton) findViewById(R.id.orangeButton);
        ImageButton eraseIb = (ImageButton) findViewById(R.id.eraseButton);
        ImageButton plusIb = (ImageButton) findViewById(R.id.plusButton);
        ImageButton minusIb = (ImageButton) findViewById(R.id.minusButton);

        minusIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v)
            {
                strokeWidth -= 2;
                canvasView.setStrokeWidth(strokeWidth);
            }
        });

        plusIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                strokeWidth += 2;
                canvasView.setStrokeWidth(strokeWidth);
            }
        });

        eraseIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(Color.TRANSPARENT);
            }
        });

        orangeIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(orange);
            }
        });

        purpleIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(purple);
            }
        });

        blackIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(Color.BLACK);
            }
        });

        yellowIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(Color.YELLOW);
            }
        });

        greenIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(Color.GREEN);
            }
        });

        blueIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(Color.BLUE);
            }
        });

        redIb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.setColor(Color.RED);
            }
        });

        saveB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            }
        });

        aboutB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), AboutActivity.class);

                startActivity(intent);
            }
        });

        resetB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                canvasView.clearCanvas();
            }
        });
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Upvotes: 2

Views: 1902

Answers (2)

Henry
Henry

Reputation: 17851

You have created canvas, but you are not using that to draw anything on it. So essentially, the line drawCanvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint); doesn't do anything.

What you need to do instead is draw the path on the canvas. Then draw the canvasBitmap using drawCanvas. This way you can maintain a single path and a single paint instead of multiple ones.

@Override
protected void onDraw(Canvas drawCanvas)
{
    canvas.drawPath(path, paint);
    drawCanvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
}

When you do the above, each time the user draw's a new path, the canvasBitmap will hold all the old paths in it. So each time the onDraw is called, only the new path is drawn.

Always remember: It's a bad practice to create multiple PAINT objects. Try to reuse the Paint object as much as possible.

Upvotes: 3

Michiyo
Michiyo

Reputation: 1201

In CanvasView.java, this line

private Paint paint = new Paint();

declares one paint object for the whole class, and this line

private Path path = new Path();

declares one path object for the whole class.

When onDraw is called, your whole canvas area is being redrawn. That means that

drawCanvas.drawPath(path, paint);

is drawing the entire path that has been added since the activity was created with whatever the current value of paint is.

One way of working around this is to have a list of Path objects and a list of Paint objects. The first element of the Path list would store the first path that was drawn--say, everything drawn up until the point when they change paint. The first element of the paint list would store the corresponding paint that was used. Whenever the user changes the paint and begins painting, you'll need to create a new Path object, and add it and the new Paint object to the lists.

Once you have lists of the paths and the paints they were drawn with, you can do something like this in onDraw:

for (int i = 0; i < paths.size(); i++) {
  Path path = paths.get(i);
  Paint paint = paints.get(i);
  drawCanvas.drawPath(path, paint);
} 

Upvotes: 2

Related Questions