I am drawing on the canvas properly and saving it into a bitmap.
However, I want to reset the canvas to white by clicking a button.
Here is my code:
public class Canvas extends View {
Paint paint;
Path path;
boolean cc = false;
public Canvas(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
path = new Path();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5f);
}
#Override
protected void onDraw(android.graphics.Canvas canvas) {
super.onDraw(canvas);
if (!cc) {
canvas.drawPath(path, paint);
}
else {
canvas.drawColor(Color.WHITE);
cc = false;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float xPos = event.getX();
float yPos = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(xPos, yPos);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(xPos, yPos);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
invalidate();
return true;
}
public void clear() {
cc = true;
invalidate();
}
my clear() function set cc to "true" then invalidate() calls the onDraw() function. But it seems that "cc" is not recognized inside the onDraw() or it has always the same value inside.
I tried the path.reset() with no result.
calling clear() does not return any error.
Seems you'd want the path to get cleared too when your clear() method is called, so do that, then use the fact that the path is empty to clear the canvas.
public void clear() {
path.reset();
invalidate();
}
#Override
protected void onDraw(android.graphics.Canvas canvas) {
super.onDraw(canvas);
if (path.isEmpty()) {
canvas.drawColor(Color.WHITE);
} else {
canvas.drawPath(path, paint);
}
}
That entirely eliminates the cc field.
To clear all your canvas use this:
Paint transparent = new Paint();
transparent.setAlpha(0);
Update:
Add this line in your button onclick():
canvas.drawColor(Color.WHITE);
And remove it from the draw function.
Related
I wanted to draw the custom shape using multiple straight lines. For that, i used the canvas. But I can draw only one line. When I draw second, previous disappears.
My code is given.
public class CanvasBackground extends View {
public Paint paint;
public Context context;
public Canvas canvas;
public ScaleGestureDetector scaleGestureDetector;
float scalfactor = 1f;
boolean isDrawing;
private PointF startPoint, endPoint;
public CanvasBackground(Context context) {
super(context);
this.context = context;
paint = new Paint();
scaleGestureDetector = new ScaleGestureDetector(context, new CanvasScale());
setDrawingCacheEnabled(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
paint.setColor(Color.WHITE);
canvas.drawPaint(paint);
canvas.save();
DrawingZoomingCanvas(canvas);
DrawingLine(canvas);
canvas.restore();
Log.e("OnDraw >>>", "CALLING");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startPoint = new PointF(event.getX(), event.getY());
endPoint = new PointF();
isDrawing = true;
break;
case MotionEvent.ACTION_MOVE:
if (isDrawing) {
endPoint.x = event.getX();
endPoint.y = event.getY();
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (isDrawing) {
endPoint.x = event.getX();
endPoint.y = event.getY();
//isDrawing = false;
invalidate();
}
break;
default:
break;
}
//scaleGestureDetector.onTouchEvent(event);
Log.e("OnTouch >>>", "CALLING" + isDrawing);
return true;
}
//drawing Matrix Canvas With Zoom
private void DrawingZoomingCanvas(Canvas canvas) {
//drawing Matarix
canvas.translate(scalfactor * 10, scalfactor * 10);
canvas.scale(scalfactor, scalfactor);
paint.setColor(Color.rgb(220, 220, 220));
for (int i = 0; i <= canvas.getHeight() * scalfactor; i += 10) {
canvas.drawLine(i, 0, i, canvas.getHeight(), paint);
canvas.drawLine(0, i, canvas.getWidth(), i, paint);
}
}
//drawing a line
private void DrawingLine(Canvas canvas) {
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setAntiAlias(true);
if (isDrawing)
canvas.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, paint);
}
private class CanvasScale extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
scalfactor *= scaleGestureDetector.getScaleFactor();
scalfactor = Math.max(0.1f, Math.min(scalfactor, 10.0f));
invalidate();
return true;
}
}
}
You are clearing your canvas each time you draw a line, so the previous line will be erased as you draw the new one.
You need to store the previous lines in a bitmap so you can draw these when you draw the new one.
i'm working on a project where i create mirror of drawing. my main logic is working fine.Only thing causing Problem is Redo and undo functionality.
i have searched a lot. implement may methods but couldn't get success. Following is my Drawing Class.
DrawingView.java
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
public DrawingView(Context context, AttributeSet attrs){
super(context, attrs);
this.context=context;
setupDrawing();
}
//setup drawing
private void setupDrawing(){
//prepare for drawing and setup paint stroke properties
brushSize = getResources().getInteger(R.integer.small_size);
lastBrushSize = brushSize;
drawPath = new Path();
drawPath1 = new Path();
drawPaint = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(brushSize);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
//size assigned to view
#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);
width=w;
height=h;
Log.d("width,height", w + " , " + h);
drawCanvas = new Canvas(canvasBitmap);
}
//draw the view - will be called after touch event
#Override
protected void onDraw(Canvas canvas) {
for (Path p : paths){canvas.drawPath(p, drawPaint);}
canvas.drawPath(drawPath, drawPaint);
Log.i("OnDRAWING", "REACH ON DRAW");
/*canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
canvas.drawPath(drawPath1, drawPaint);*/
}
//register user touches as drawing action
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
//respond to down, move and up events
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.reset();
undonePaths.clear();
drawPath.moveTo(touchX, touchY);
undonePaths.clear();
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(touchX, touchY);
drawCanvas.drawPath(drawPath, drawPaint);// commit the path to our offscreen
paths.add(drawPath);
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
drawCanvas.drawPath(drawPath1, drawPaint);
drawPath1.reset();
break;
default:
return false;
}
//redraw
invalidate();
return true;
}
What i am missing here?
Any suggestions/ideas/examples which is the best way to implement this kind of functionality on my project?
Canvas drawing is sort of hierarchal in that drawing happens in the order that you do them.
So all drawing should be done in onDraw and only here.
Your draw events should be pushed on a draw stack. You do not store a canvas. You just store the operations that are supposed to happen when the drawing eventually occurs (coordinates, width, and color of a path for example).
An "undo" operation can be done by popping from the drawing stack and pushing the event to a "redo" stack. The "redo" event can be done by popping from the "redo" stack and pushing back on to the "drawing" stack.
In onDraw method of your View, just scan through the drawing events and draw on the canvas.
EDIT:
The onDraw() method would just iterate through your drawing operations. So first you could have an interface called DrawEvent like so:
public interface DrawEvent {
void draw(Canvas c);
}
Then in your View class have your collection of DrawEvents and iterate through them in onDraw(Canvas c).
public class MyView extends View {
Deque<DrawEvent> drawEvents = new LinkedList<DrawEvent>();
#Override
public void onDraw(Canvas c) {
for (DrawEvent e : drawEvents) {
e.draw(c);
}
}
}
'I want to remove contents of the image that has been drawn on canvas like finger eraser but drawCircle is not removing.Here my code
'
public class PaintView extends View {
Canvas c1;
Paint mPaintt;
Bitmap mBitmap;
Matrix mMatrix;
RectF mSrcRectF;
RectF mDestRectF;
boolean mPause;
Bitmap bit;
Path path;
int X = -100;
int Y = -100;
Paint mPaint;
Bitmap mBitmaps;
public PaintView(Context context, AttributeSet attributeSet){
super(context,attributeSet);
mPaintt=new Paint();
mMatrix = new Matrix();
mSrcRectF = new RectF();
mDestRectF = new RectF();
mPause = false;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaint.setAntiAlias(true);
mPaint.setColor(Color.TRANSPARENT);
mPaint.setMaskFilter(new BlurMaskFilter(15, BlurMaskFilter.Blur.NORMAL));
}
public void addBitmap(Bitmap bitmap){
mBitmap = bitmap;
}
public Bitmap getBitmap(){
return mBitmap;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
X = (int) ev.getX();
Y = (int) ev.getY();
invalidate();
break;
}
case MotionEvent.ACTION_MOVE: {
X = (int) ev.getX();
Y = (int) ev.getY();
invalidate();
break;
}
case MotionEvent.ACTION_UP:
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(!mPause){
if(mBitmap!=null){
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
//mPaint.setColor(Color.TRANSPARENT);
// Setting size of Source Rect
mSrcRectF.set(0, 0,mBitmap.getWidth(),mBitmap.getHeight());
// Setting size of Destination Rect
mDestRectF.set(0, 0, getWidth(), getHeight());
// Scaling the bitmap to fit the PaintView
mMatrix.setRectToRect( mSrcRectF , mDestRectF, Matrix.ScaleToFit.CENTER);
canvas.drawBitmap(mBitmap, mMatrix,mPaintt);
canvas.drawCircle(X,Y,30,mPaint);
}
}
// Redraw the canvas
invalidate();
}
// Pause or resume onDraw method
public void pause(boolean pause){
mPause = pause;
}
}
So I would like to implement an undo button for a drawing app I am working on for android. My current idea is to put the current draw path into a list of paths on MotionEvent.ACTION_UP. In onDraw(), simply draw everything that is in the list of paths. When the user presses undo, you remove the last drawn path from the list of paths and call invalidate(), forcing onDraw() to be called, which will draw everything in the list of paths. Since we removed the previous path from the list, that path should not be drawn and therefore should be "undone".
The problem I am having is the path never seems to actually be undone. It seems like the logic in my head is correct, but this implementation does not work correctly. Any help will be greatly appreciated.
Here is my code:
DrawingView.java:
Instance variables (to clarify following methods):
private Context context;
private Path drawPath;
private Paint drawPaint;
private Paint canvasPaint;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
private int previousPaintColor;
private int paintColor;
private float brushSize;
private float eraserSize;
private float lastBrushSize;
private boolean isErasing = false;
private List<Path> moveList = null;
private List<Path> undoList = null;
private List<Path> currentMoveList = null;
Called from constructor:
private void setupDrawing() {
drawPath = new Path();
drawPaint = new Paint();
brushSize = getResources().getInteger(R.integer.default_brush_size);
lastBrushSize = brushSize;
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(brushSize);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
onDraw:
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
for (Path path : currentMoveList) {
canvas.drawPath(path, drawPaint);
}
for (Path path : moveList) {
canvas.drawPath(path, drawPaint);
}
}
onTouchEvent:
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
currentMoveList.add(drawPath);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(touchX, touchY);
drawCanvas.drawPath(drawPath, drawPaint);
moveList.add(drawPath);
drawPath = new Path();
currentMoveList.clear();
break;
default:
return false;
}
invalidate();
return true;
}
undo() :
public void undo() {
if (moveList.size() > 0) {
undoList.add(moveList.remove(moveList.size() - 1));
invalidate();
}
}
Regards,
Kyle.
After some trial and error, here is the fix:
Remove this line of code:
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
from onDraw() method and everything works perfectly :)
**Undo your onDraw override method ((TESTED)) **
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
for (Path path : currentMoveList) {
canvas.drawPath(path, drawPaint);
}
for (Path path : moveList) {
canvas.drawPath(path, drawPaint);
}
super.onDraw(canvas);
}
I'm building a crossword game. Ideally, I would like to use similar functionality like the code below. However, I just need help figuring out how to draw a textfield on the screen so the user can input his/her words. Here's the code I have so far. How would I modify this code to draw a textfield instead of paint? Appreciate any help.
public class Word extends View {
private Paint paint = new Paint();
private Path path = new Path();
public Word(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setStrokeWidth(30f);
paint.setColor(Color.GRAY);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
// Schedules a repaint.
invalidate();
return true;
}
Use a TextView in addition to your "Word" view, and set its visibility to GONE, until you need it to show, then set it to VISIBLE when you want it.