Reputation: 105
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
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
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