Undo and redo functionality NOT working with below code. Below is the my drawing code implementation.
Is there any problem in below code?
public class DrawingArea extends View {
private Path drawPath;
private Paint drawPaint, canvasPaint;
private int paintColor = 0xFF660000;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
private ArrayList paths = new ArrayList();
private ArrayList undonePaths = new ArrayList();
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
// for Undo, Redo
private int historyPointer = 0;
private boolean erase = false;
public DrawingArea(Context context) {
super(context);
setupDrawing();
}
public DrawingArea(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
public void setupDrawing() {
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(20);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
//paths.add(drawPath);
}
public void setColor(String newColor) {
invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
}
#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);
drawCanvas = new Canvas(canvasBitmap);
}
#Override
public boolean onTouchEvent(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;
}
private void touch_start(float x, float y) {
drawPath.reset();
drawPath.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) {
drawPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
drawPath.lineTo(mX, mY);
// commit the path to our offscreen
drawCanvas.drawPath(drawPath, drawPaint);
// kill this so we don't double draw
drawPath = new Path();
paths.add(drawPath);
}
public void setErase() {
canvasBitmap.eraseColor(Color.TRANSPARENT);
drawPath.reset();
invalidate();
}
public void startNew() {
drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
public void undo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
}
}
public void redo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
}
}
}
Change your OnDraw() like this
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, canvasPaint);
canvas.drawPath(mPath,drawPaint);
for (Path p : paths) {
canvas.drawPath(p, drawPaint);
}
}
and don't forget to add type of data hold by paths arraylist
ArrayList<Path> paths = new ArrayList<Path>();
Related
I have created an Android 5.1 application with which I can write with a stylus on a tablet using SurfaceView and Canvas. You can find my code below. Unfortunately, the writing is very slow, especially when lot of text is written it starts getting slow. I think the problem is that it is rendered in software. Of course, a possibility to speedup would be to use OpenGL but I don't know it and I think this has a too steep learning curve for me right now.
Nevertheless, is there a possibility to speedup my code (i.e. to make it more responsible when writing)? Or else is it easy to change my code to OpenGL?
public class DrawingView extends SurfaceView implements OnTouchListener {
private Bitmap mBitmap;
private Canvas m_Canvas;
private Path m_Path;
private Paint m_Paint;
private ArrayList<Pair<Path, Paint>> paths = new ArrayList<>();
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private boolean isEraserActive = false;
private int pathCount = 0;
private View rectangleView;
public DrawingView(Context context, AttributeSet attr) {
super(context, attr);
setFocusable(true);
setFocusableInTouchMode(true);
setBackgroundColor(Color.TRANSPARENT);
this.setOnTouchListener(this);
onCanvasInitialization();
setAlpha(0.99f);
}
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(2);
m_Path = new Path();
Paint newPaint = new Paint(m_Paint);
paths.add(new Pair<>(m_Path, newPaint));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
m_Canvas = new Canvas(mBitmap);
}
#Override
public boolean onTouch(View arg0, MotionEvent event) {
float x = event.getX();
float y = event.getY();
if (rectangleView != null) {
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) rectangleView.getLayoutParams();
params.leftMargin = (int) x;
params.topMargin = (int) y;
rectangleView.setLayoutParams(params);
return true;
} else {
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
isEraserActive = true;
} else {
isEraserActive = false;
}
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.setStrokeWidth(20);
m_Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
Paint newPaint = new Paint(m_Paint);
paths.add(new Pair<>(m_Path, newPaint));
} else {
m_Paint.setColor(Color.BLACK);
m_Paint.setStrokeWidth(2);
m_Paint.setXfermode(null);
Paint newPaint = new Paint(m_Paint);
paths.add(new Pair<>(m_Path, newPaint));
pathCount++;
}
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);
m_Canvas.drawPath(m_Path, m_Paint);
m_Path = new Path();
Paint newPaint = new Paint(m_Paint);
paths.add(new Pair<>(m_Path, newPaint));
}
public void reset() {
for (Pair<Path, Paint> p : paths) {
p.first.reset();
}
paths.clear();
pathCount = 0;
mBitmap.eraseColor(Color.TRANSPARENT);
invalidate();
}
}
I have different set of colors in paint app. I am able to draw lines on canvas view with default color. But when i change the color it is changing color for all lines including old lines.
below is the code.
public class DrawingArea extends View {
private Path drawPath;
private Paint drawPaint, canvasPaint;
private int paintColor = 0xFF660000;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private ArrayList<Integer> colors = new ArrayList<Integer>();
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private static boolean mRedoStatus = false;
private static boolean mUndoStatus = false;
private float currentBrushSize;
private float lastBrushSize;
// for Undo, Redo
private int historyPointer = 0;
private boolean erase = false;
public DrawingArea(Context context) {
super(context);
setupDrawing();
}
public DrawingArea(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
public void setupDrawing() {
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(20);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
//paths.add(drawPath);
}
public void setColor(String newColor) {
invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
#Override
protected void onDraw(Canvas canvas) {
for (Path p : paths) {
canvas.drawPath(p, drawPaint);
}
canvas.drawPath(drawPath, drawPaint);
}
#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);
drawCanvas = new Canvas(canvasBitmap);
}
#Override
public boolean onTouchEvent(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;
}
private void touch_start(float x, float y) {
undonePaths.clear();
drawPath.reset();
drawPath.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) {
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
public void setErase() {
canvasBitmap.eraseColor(Color.TRANSPARENT);
drawPath.reset();
invalidate();
}
private void touch_up() {
drawPath.lineTo(mX, mY);
// commit the path to our offscreen
drawCanvas.drawPath(drawPath, drawPaint);
paths.add(drawPath);
// kill this so we don't double draw
drawPath = new Path();
}
public void startNew() {
drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
public void undo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
setEmptyStatus(false);
}
}
public void setEmptyStatus(boolean status) {
mRedoStatus = status;
mUndoStatus = status;
}
public void redo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
setEmptyStatus(false);
}
}
public void setBrushSize(float newSize) {
float pixelAmount = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
newSize, getResources().getDisplayMetrics());
currentBrushSize = pixelAmount;
canvasPaint.setStrokeWidth(newSize);
}
public void setLastBrushSize(float lastSize) {
lastBrushSize = lastSize;
}
public float getLastBrushSize() {
return lastBrushSize;
}
}
It looks like you're setting the new color on your drawPaint variable. However, in your onDraw method:
for (Path p : paths) {
canvas.drawPath(p, drawPaint);
}
This loop is drawing all of the previous paths using the same Paint object, drawPaint, which is why they are all drawn with the same color. A possible solution to consider is saving each Paint object along with the Path, and call drawPath for each path and its corresponding Paint.Just as you have a list of all the old paths, you can also create a list of old paints.
private ArrayList<Paint> paints = new ArrayList<Paint>();
When you save each path to the paths list, you can add the current drawPaint to the list of paints. Then, in your onDraw method, you can change your loop to look something like this:
for(int i = 0 ; i < paths.size() ; i++) {
canvas.drawPath(paths.get(i), paints.get(i));
}
This would draw each path with its corresponding paint, preserving each paths color.
I want to implement a canvas drawing application with custom brush & undo/redo operation. First of all my code works perfectly without using the custom brush (including the undo/redo operation). According to this answer How to make custom brush for canvas in android? I used simple image spikes for bitmap draw.
Now the issue are,
The undo, redo operation doesn't works, custom brush paints over and over again whenever moving the touch points.
Q: How to make the undo/redo operation work?
The custom brush stokers aren't smooth as they should. Right now they looks rough and artificial.
Q. How to make the painting smooth and natural with custom brush strokes?
Check my sample code here,
public class DrawingView extends View {
private Context ctx;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private Map<Path, Float> brushMap = new HashMap<Path, Float>();
private Map<Path, List<Vector2>> customBrushMap = new HashMap<Path, List<Vector2>>();
private Bitmap mBitmapBrush;
private Vector2 mBitmapBrushDimensions;
private List<Vector2> mPositions = new ArrayList<Vector2>(100);
private boolean isCustomBrush = false;
private int selectedColor;
private float brushSize, lastBrushSize;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private Path drawPath;
private Paint drawPaint, canvasPaint;
private int paintColor = 0xFF660000, paintAlpha = 255;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
private static final class Vector2 {
public Vector2(float x, float y) {
this.x = x;
this.y = y;
}
public final float x;
public final float y;
}
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
ctx = context;
setupDrawing();
}
private void setupDrawing() {
brushSize = getResources().getInteger(R.integer.small_size);
lastBrushSize = brushSize;
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setDither(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);
}
#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);
drawCanvas = new Canvas(canvasBitmap);
}
private void touch_start(float x, float y) {
undonePaths.clear();
drawPath.reset();
drawPath.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) {
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
customBrushMap.put(drawPath, mPositions);
}
private void touch_up() {
drawPath.lineTo(mX, mY);
drawCanvas.drawPath(drawPath, drawPaint);
paths.add(drawPath);
brushMap.put(drawPath, brushSize);
drawPath = new Path();
drawPath.reset();
invalidate();
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
}
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//detect user touch
float x = event.getX();
float y = event.getY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
if (isCustomBrush) {
mPositions.add(new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2));
}
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_POINTER_DOWN:
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
for (Path p : paths) {
drawPaint.setColor(colorsMap.get(p));
drawPaint.setShader(shaderMap.get(p));
drawPaint.setStrokeWidth(brushMap.get(p));
drawPaint.setAlpha(opacityMap.get(p));
if (isCustomBrush) {
if (customBrushMap.get(p) != null) {
for (Vector2 pos : customBrushMap.get(p)) {
Paint paint = new Paint();
ColorFilter filter = new PorterDuffColorFilter(selectedColor, PorterDuff.Mode.SRC_IN);
paint.setColorFilter(filter);
canvas.drawBitmap(mBitmapBrush, pos.x, pos.y, paint);
}
}
} else {
canvas.drawPath(p, drawPaint);
drawPaint.setColor(selectedColor);
drawPaint.setStrokeWidth(brushSize);
canvas.drawPath(drawPath, drawPaint);
}
}
canvas.restore();
}
public void setCustomBrush(Activity activity, String customBrush) {
isCustomBrush = true;
invalidate();
int patternID = getResources().getIdentifier(customBrush, "drawable", "com.androidapp.drawingstutorial");
mBitmapBrush = BitmapFactory.decodeResource(getResources(), patternID);
mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());
}
}
To make your brush look "smoother" not 100% sure what is meant by that, but you would need to tighten up the dots on the image you are using for your custom brush. Making them more compact will make the edges look smoother.
As for undo redo, in your touchUp method your never adding to customBrushMap only brushMap. So in the ondraw there is nothing to draw since that map will always be empty.
I am developing drawing module for my app. All features working fine. The problem is, everytime when I change the color, or set the brushSize or change to eraser, the first touch on the screen will draw with the previous settings. The second touch then will have the newest changes. Same goes to undo. Nothing change on first click. For second click and so on, it is working fine. This is my reference. The sample app in the reference also has the same problem. This is my code:
public class DrawingView extends View {
//drawing path
private Path drawPath;
private Point pointDraw;
//drawing and canvas paint
private Paint drawPaint, canvasPaint;
//initial color
private int paintColor;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;
//brush sizes
private float brushSize, lastBrushSize;
//private int defaultBrush = 10;
//erase flag
private boolean erase=false;
private int tempColor;
private boolean checkDraw = false;
private ArrayList<PathPoints> paths = new ArrayList<PathPoints>();
private ArrayList<PathPoints> undonePaths = new ArrayList<PathPoints>();
private ArrayList<Point> tempCanvas = new ArrayList<Point>();
private int countPoint = 0;
int x,y;
public DrawingView(Context context, AttributeSet attrs){
super(context, attrs);
setupDrawing();
}
//setup drawing
private void setupDrawing(){
//prepare for drawing and setup paint stroke properties
//brushSize = getResources().getInteger(R.integer.medium_size);
lastBrushSize = brushSize;
drawPath = 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);
paths.add(new PathPoints(drawPath, paintColor, brushSize, false));
drawCanvas = new Canvas();
}
//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);
drawCanvas = new Canvas(canvasBitmap);
}
//draw the view - will be called after touch event
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
//canvas.drawPath(drawPath, drawPaint);
for (PathPoints p : paths) {
drawPaint.setColor(p.getColor());
drawPaint.setStrokeWidth(p.getStrokeSize());
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(p.getPath(), drawPaint);
}
setCheckDraw(true);
}
public int getArr()
{
return paths.size();
}
public boolean isCheckDraw() {
return checkDraw;
}
public void setCheckDraw(boolean checkDraw) {
this.checkDraw = checkDraw;
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 0;
private void touch_start(float x, float y) {
drawPath.reset();
drawPath.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) {
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
drawPath.lineTo(mX, mY);
// commit the path to our offscreen
drawCanvas.drawPath(drawPath, drawPaint);
// kill this so we don't double draw
drawPath = new Path();
paths.add(new PathPoints(drawPath, paintColor, brushSize, false));
}
//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:
touch_start(touchX, touchY);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
touch_up();
//invalidate();
break;
}
//redraw
invalidate();//do not call frequently
return true;
}
public void validateDraw()
{
invalidate();
}
//update color
public void setColor(String newColor){
//invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
public void setColor(int newColor){
//invalidate();
paintColor = newColor;
drawPaint.setColor(paintColor);
tempColor = paintColor;
}
//set brush size
public void setBrushSize(float newSize){
float pixelAmount = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
newSize, getResources().getDisplayMetrics());
brushSize=pixelAmount;
}
//get and set last brush size
public void setLastBrushSize(float lastSize){
lastBrushSize=lastSize;
}
public float getLastBrushSize(){
drawPath.reset();
return lastBrushSize;
}
//set erase true or false
public void setErase(boolean isErase)
{
//drawPath.reset();
erase=isErase;
if(erase)
this.setColor("#FFFFFFFF");
else
//drawPaint.setXfermode(null);
this.setColor(tempColor);
}
//// new drawing
public void startNew(){
//drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
paths.clear();
undonePaths.clear();
invalidate();
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
}
// toast the user
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
}
// toast the user
}
Thank in advance
I'm trying to implement a erase function on the Fingerpaint App in the Api Demo's in Android. I change some code based on my needs. At first the erase function works well. I used:
drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
It works well when the path has been drawn in the bitmap. But when I implement the Undo and Redo function(the way I achieve it is by using two arraylist and save the path there), PorterDuffXfermode doesn't work. Please take a look at this:
At image 1 I draw some paths(stored in an arraylist. Not drawn in the bitmap canvas)
Then at 2 thats the time I pressed the erase icon. I dont know what causing all the paths to became black. Then at 3 I tried to erase the paths I made(PorterDuffXFermode some says its black). Then when I switch back to drawing mode. It seems that instead or erasing, all it did was draw another path.
Any solutions for this? or workarounds?
Here is the code when to my drawingClass:
//this is where the drawing takes place
public class DrawingClass extends View{
//drawing path
private Path drawPath;
//drawing and canvas paint
private Paint drawPaint, canvasPaint;
//initial color
private int paintColor = 0xFF660000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;
private float brushSize,lastBrushSize;
private boolean erase = false;
//array list for paths
private ArrayList<PathPoints> paths = new ArrayList<PathPoints>();
private ArrayList<PathPoints> undonePaths = new ArrayList<PathPoints>();
public Context context;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
public DrawingClass(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
this.context = context;
setupDrawing();
}
private void setupDrawing(){
setFocusable(true);
setFocusableInTouchMode(true);
//instantiate the variable to get the last brush size
brushSize = getResources().getInteger(R.integer.medium_size);
lastBrushSize = brushSize;
//get drawing area setup for interaction
drawCanvas = new Canvas();
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setColor(paintColor);
//initial properties
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);
//paths.add(new PathPoints(drawPath, paintColor, brushSize));
}
public void setBrushSize(float newSize){
//update brush size
float pixelAmount = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
newSize, getResources().getDisplayMetrics());
brushSize = pixelAmount;
drawPaint.setStrokeWidth(brushSize);
invalidate();
}
public void setLastBrushSize(float lastSize){
//set the last brush size
lastBrushSize = lastSize;
invalidate();
}
public float getLastBrushSize(){
//return the last brush size
return lastBrushSize;
}
public void setErase(boolean isErase){
//set erase true or false
erase = isErase;
if (erase) {
//drawPaint.setColor(Color.WHITE);
drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
else drawPaint.setXfermode(null);
}
public void startNew(){
//creates a new canvass
drawCanvas.drawColor(0,PorterDuff.Mode.CLEAR);
drawCanvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
setDrawingCacheEnabled(true);
String imgSaved = MediaStore.Images.Media.insertImage(
context.getContentResolver(), getDrawingCache(),
UUID.randomUUID().toString()+".png", "drawing");
destroyDrawingCache();
paths.clear();
undonePaths.clear();
invalidate();
}
#Override
protected void onSizeChanged(int w,int h,int oldw,int oldh){
//view given size
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
#Override
protected void onDraw(Canvas canvas){
//draw view
//save the strokes in the bitmap
//canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
for (PathPoints p : paths){
drawPaint.setColor(p.getColor());
drawPaint.setStrokeWidth(p.getBrush());
invalidate();
canvas.drawPath(p.getPath(), drawPaint);
}
}
private void touch_start(float x, float y){
drawPath.reset();
drawPath.moveTo(x, (float) (y+.1));
drawPath.lineTo(x, y);
drawPath.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) {
drawPath.quadTo(mX, mY, (x + mX)/2, (y+ mY)/2);
mX = x;
mY = y;
}
}
private void touch_up(){
drawPath.lineTo(mX, mY);
drawCanvas.drawPath(drawPath, drawPaint);
drawPath = new Path();
paths.add(new PathPoints(drawPath, paintColor, brushSize));
}
#Override
public boolean onTouchEvent(MotionEvent event){
//detect user touch
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
invalidate();
touch_start(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
invalidate();
touch_move(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
invalidate();
touch_up();
drawPath.reset();
break;
default:
return false;
}
invalidate();
return true;
}
public void undoPath(){
if (paths.size()>0) {
undonePaths.add(paths.remove(paths.size()-1));
} else {
Log.d("Undo", "No paths left");
}
invalidate();
}
public void redoPath(){
if (undonePaths.size()>0) {
paths.add(undonePaths.remove(undonePaths.size()-1));
} else {
Log.d("Redo", "No paths left");
}
invalidate();
}
public void setColor(String newColor){
//set color
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
invalidate();
}
class PathPoints{
private Path path;
private int color;
private float brushsize;
private int x, y;
//holds the paths data
public PathPoints(Path path, int color, float brushsize) {
this.path = path;
this.color = color;
this.brushsize = brushsize;
}
public Path getPath() {
return path;
}
public void setPath(Path path) {
this.path = path;
}
public float getBrush(){
return brushsize;
}
public void setBrush(float brushsize){
this.brushsize = brushsize;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
}