I am working on a drawing project. My code is working perfectly other than canvas redo and undo operations. My undo operation removes paths from the paths ArrayList and saves to the undonePaths ArrayList, and the redo operation removes the last element from undonePaths and saves to paths.
Here's my code:
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class DrawView extends View implements OnTouchListener {
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private Bitmap im;
public DrawView(Context context)
{
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFFFFFF);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPath = new Path();
paths.add(mPath);
im=BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
//mPath = new Path();
//canvas.drawPath(mPath, mPaint);
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.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) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath = new Path();
paths.add(mPath);
}
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
}
#Override
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;
}
}
This code is working perfectly for drawing but not working perfectly for undo and redo operations. What's wrong with my code?
Here's my full source code:
http://www.4shared.com/rar/8PQQEZdH/test_draw.html
UPDATED:
Finally my problem was solved; here's my drawing class:
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class DrawView extends View implements OnTouchListener {
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private Bitmap im;
public DrawView(Context context)
{
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFFFFFF);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPath = new Path();
im=BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
//mPath = new Path();
//canvas.drawPath(mPath, mPaint);
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.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) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
paths.add(mPath);
mPath = new Path();
}
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
}
#Override
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;
}
}
That code is working perfectly...!
At first glance I see the following problems:
By adding your empty Path to paths as soon as you make it, you're going to have a problem as soon as you undo: you're popping that empty Path first, making the first undo not seem to work. Then if you draw into that Path, it's not added to paths. The solution is to add the completed Path to paths in touch_up() before creating a new one.
That is, remove
paths.add(mPath);
from the constructor, and in touch_up(), change
mPath = new Path();
paths.add(mPath);
to
paths.add(mPath);
mPath = new Path();
You'll also want to add
canvas.drawPath(mPath, mPaint);
after your for loop in onDraw() in order to draw the in-progress Path.
You're not emptying undonePaths when the user starts drawing again.
Please check below code it is working..
package com.testpath;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.LinearLayout;
public class TesUndoPaintActivity extends Activity {
/** Called when the activity is first created. */
LinearLayout linearLayout2;
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private ArrayList<Path> paths = new ArrayList<Path>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
linearLayout2 = (LinearLayout) findViewById(R.id.linearLayout2);
final DrawingPanel dp = new DrawingPanel(this);
linearLayout2.addView(dp);
((Button) findViewById(R.id.button1))
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (paths.size() > 0) {
undonePaths.add(paths
.remove(paths.size() - 1));
dp.invalidate();
}
}
});
((Button) findViewById(R.id.button2))
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (undonePaths.size()>0) {
paths.add(undonePaths.remove(undonePaths.size()-1));
dp.invalidate();
}
}
});
}
public class DrawingPanel extends View implements OnTouchListener {
private Canvas mCanvas;
private Path mPath;
private Paint mPaint, circlePaint, outercirclePaint;
// private ArrayList<Path> undonePaths = new ArrayList<Path>();
private float xleft, xright, xtop, xbottom;
public DrawingPanel(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
circlePaint = new Paint();
mPaint = new Paint();
outercirclePaint = new Paint();
outercirclePaint.setAntiAlias(true);
circlePaint.setAntiAlias(true);
mPaint.setAntiAlias(true);
mPaint.setColor(0xFFFFFFFF);
outercirclePaint.setColor(0x44FFFFFF);
circlePaint.setColor(0xAADD5522);
outercirclePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStyle(Paint.Style.FILL);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
outercirclePaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPath = new Path();
paths.add(mPath);
}
public void colorChanged(int color) {
mPaint.setColor(color);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 0;
private void touch_start(float x, float y) {
mPath.reset();
mPath.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) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath = new Path();
paths.add(mPath);
}
#Override
public boolean onTouch(View arg0, MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// if (x <= cx+circleRadius+5 && x>= cx-circleRadius-5) {
// if (y<= cy+circleRadius+5 && cy>= cy-circleRadius-5){
// paths.clear();
// return true;
// }
// }
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;
}
}
}
And Below XML File.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Undo" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Redo" />
</LinearLayout>
<LinearLayout
android:id="#+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout>
</LinearLayout>
Please check it.
Problem
The path is only shown when you finish drawing it which leaves the user clueless as to what he is drawing
Solution
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
//Draw path along with the finger
canvas.drawPath(mPath, mPaint);
}
Add canvas.drawPath(mPath,mPaint) to onDraw() so the user gets a feeling of actually painting on the canvas.
Related
I want to create project like drawing which is needed to support drawing, undo, redo and eraser. Eraser must delete only for drawing view not delete background. Below code implements undo and redo functionality. I want to add eraser option but it's not done. How can implement eraser option using below code?
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.view.MotionEvent;
import java.util.ArrayList;
public class CanvasView extends View {
private Paint mPenPainter;
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
Context context;
private Paint mPaint;
private float mX, mY;
private static final float TOLERANCE = 5;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private int paintColor = 0xFF000000;
public CanvasView(Context c, AttributeSet attrs) {
super(c, attrs);
context = c;
// we set a new Path
mPath = new Path();
// and we set a new Paint with the desired attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(paintColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(4f);
//float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
mPenPainter = new Paint();
mPenPainter.setColor(Color.BLUE);
}
// override onSizeChanged
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// your Canvas will draw onto the defined Bitmap
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw the mPath with the mPaint on the canvas when onDraw
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
// paths.add(mPath);
}
private void startTouch(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
//Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
// Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
// when ACTION_MOVE move touch according to the x,y values
private void moveTouch(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOLERANCE || dy >= TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void upTouch() {
mPath.lineTo(mX, mY);
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
paths.add(mPath);
mPath = new Path();
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float mCurX;
float mCurY;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mX = event.getX();
mY = event.getY();
startTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
}
return true;
}
It seems a bit late answer but i finally made the solution after two days hardwork..
public class DrawView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
Boolean eraserOn = false;
Boolean newAdded = false;
Boolean allClear = false;
private Path drawPath;
private ArrayList<Bitmap> bitmap = new ArrayList<>();
private ArrayList<Bitmap> undoBitmap = new ArrayList<>();
public DrawView(Context c) {
super(c);
context=c;
drawPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
drawPaint = new Paint();
drawPaint.setAntiAlias(true);
drawPaint.setDither(true);
drawPaint.setColor(Color.BLACK);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
drawPaint.setStrokeWidth(20);
// drawPaint.setAlpha(80);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(mBitmap==null) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// if(!eraserOn)
canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(drawPath, drawPaint);
canvas.drawPath( circlePath, circlePaint);
}
public void onClickEraser(boolean isEraserOn)
{
if (isEraserOn) {
eraserOn = true;
drawPaint.setColor(getResources().getColor(android.R.color.transparent));
drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
else {
eraserOn = false;
drawPaint.setColor(mPaint.getColor());
drawPaint.setXfermode(null);
}
}
public void onClickUndo () {
if(newAdded) {
bitmap.add(mBitmap.copy(mBitmap.getConfig(), mBitmap.isMutable()));
newAdded=false;
}
if (bitmap.size()>1)
{
undoBitmap.add(bitmap.remove(bitmap.size()-1));
mBitmap= bitmap.get(bitmap.size()-1).copy(mBitmap.getConfig(),mBitmap.isMutable());
mCanvas = new Canvas(mBitmap);
invalidate();
if(bitmap.size()==1)
allClear=true;
}
else
{
}
//toast the user
}
public void onClickRedo (){
if (undoBitmap.size()>0)
{
bitmap.add(undoBitmap.remove(undoBitmap.size()-1));
mBitmap= bitmap.get(bitmap.size()-1).copy(mBitmap.getConfig(),mBitmap.isMutable());
mCanvas = new Canvas(mBitmap);
invalidate();
}
else
{
}
//toast the user
}
#Override
public boolean performClick() {
return super.performClick();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(penSelected || eraserSelected) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
newAdded = true;
if(!allClear)
bitmap.add(mBitmap.copy(mBitmap.getConfig(),mBitmap.isMutable()));
else allClear=false;
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
if (eraserOn) {
drawPath.lineTo(touchX, touchY);
mCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
drawPath.moveTo(touchX, touchY);
} else {
drawPath.lineTo(touchX, touchY);
}
break;
case MotionEvent.ACTION_UP:
mCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
case MotionEvent.ACTION_CANCEL:
return false;
default:
return false;
}
invalidate();
return true;
}
return false;
}
}
for erasing you need to find out intersect between current selection and draw paths. refer below code
package opensourcecode.com.paginationwebview;
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.graphics.Point;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
/**
* Created by damodhar.meshram on 4/26/2017.
*/
public class CanvasView extends View {
private Paint mPenPainter;
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
Context context;
private Paint mPaint;
private float mX, mY;
private static final float TOLERANCE = 5;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private boolean isErasemode = false;
private int paintColor = 0xFF000000;
public CanvasView(Context c, AttributeSet attrs) {
super(c, attrs);
context = c;
// we set a new Path
mPath = new Path();
// and we set a new Paint with the desired attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(paintColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(4f);
//float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
mPenPainter = new Paint();
mPenPainter.setColor(Color.BLUE);
}
public CanvasView(Context c) {
super(c);
context = c;
// we set a new Path
mPath = new Path();
// and we set a new Paint with the desired attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(paintColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(4f);
//float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
mPenPainter = new Paint();
mPenPainter.setColor(Color.BLUE);
}
// override onSizeChanged
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// your Canvas will draw onto the defined Bitmap
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw the mPath with the mPaint on the canvas when onDraw
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
// paths.add(mPath);
}
private void startTouch(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
//Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
public void onEraser(){
if(!isErasemode){
isErasemode = true;
}else{
isErasemode = false;
}
}
private void remove(int index){
paths.remove(index);
invalidate();
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
// Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
// when ACTION_MOVE move touch according to the x,y values
private void moveTouch(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOLERANCE || dy >= TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void upTouch() {
mPath.lineTo(mX, mY);
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
paths.add(mPath);
mPath = new Path();
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float mCurX;
float mCurY;
if(isErasemode){
for(int i = 0;i<paths.size();i++){
RectF r = new RectF();
Point pComp = new Point((int) (event.getX()), (int) (event.getY() ));
Path mPath = paths.get(i);
mPath.computeBounds(r, true);
if (r.contains(pComp.x, pComp.y)) {
Log.i("need to remove","need to remove");
remove(i);
break;
}
}
return false;
}else {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mX = event.getX();
mY = event.getY();
startTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
}
return true;
}
}
}
I think you should use this pattern :
https://sourcemaking.com/design_patterns/command
I used it to my application to use undo/redo
I'm creating custom view and a bitmap from drawable and drawing paint on x, y points. I'm able to draw paint, but its going outside the boundaries.
Here's my code.
package com.example.finalpaint;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class CustomView extends View implements OnTouchListener {
private Canvas mCanvas;
private Path mPath;
public Paint mPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
Bitmap bmp;
public CustomView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
setOnTouchListener((OnTouchListener) this);
setBackgroundColor(Color.GRAY);
// setDrawingCacheEnabled(true);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.human_body_front);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(30);
mCanvas = new Canvas();
mPath = new Path();
paths.add(mPath);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bmp, 0, 0, mPaint);
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 0;
//Draw dw = new Draw();
private void touch_start(float x, float y) {
mPath.reset();
mPath.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) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath = new Path();
paths.add(mPath);
}
#Override
public boolean onTouch(View arg0, MotionEvent event) {
float x = event.getX();
float y = event.getY();
if ((int) event.getX() < 1)
return false;
if ((int) event.getY() < 1)
return false;
if ((int) event.getX() > bmp.getWidth())
return false;
if ((int) event.getY() > bmp.getHeight())
return false;
int color = bmp.getPixel((int)event.getX(), (int)event.getY());
if(color == Color.TRANSPARENT)
return false;
if(color == Color.WHITE);
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;
}
}
This is MainActivity, setting content view to custom view.
package com.example.finalpaint;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
CustomView view = new CustomView(this);
setContentView(view);
}
}
I want to draw paint inside of an image within the boundaries when OnTouch.
Here is my screenshot.
This is the code for implementing undo and redo in Android Canvas and is working fine.
package com.example.canva;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
public class MainView extends View {
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
public MainView(Context context)
{
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFFFFFF);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPath = new Path();
}
// im=BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher);
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
//mPath = new Path();
//canvas.drawPath(mPath, mPaint);
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.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) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
paths.add(mPath);
mPath = new Path();
}
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
}
#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;
}
}
But in my app I need to change color, stroke width, style etc. when I try to change the Color using the following method, entire thing in the Canvas is changing to that Color.
public void setColor(int color){
mPaint.setColor(color);}
Save the paths in array and perform operations on that array to undo or redo.
Basically, I've created "button click" on the canvas bitmap layout, but after compile the coding, I can't see my button click, is it overlapped? Can I know what should I do seems even I change the (w,h)size but it also the same.
I guess i have to put all the code here for better understanding.
Here is my sample code(MainActivity.java):
package com.abcdrawing2;
import android.os.Bundle;
import android.app.Activity;
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.view.Menu;
import android.view.MotionEvent;
import android.view.View;
public class MainActivity extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private Paint mPaint;
public MainActivity(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFF00FF00);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(8);
}
public void color_Clicked(View sender)
{
mPaint.setColor(0xFFFF0000);
}
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_8888);
mCanvas = new Canvas(mBitmap);
}
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.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) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#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;
}
public void clear(){
mBitmap.eraseColor(Color.TRANSPARENT);
invalidate();
System.gc();
}}
AND ( Scribble.java)
package com.abcdrawing2;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
public class Scribbler extends Activity {
MainActivity drawView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
drawView = new MainActivity(this);
drawView.setBackgroundColor(Color.TRANSPARENT);
setContentView(drawView);
drawView.requestFocus();
}}
I have used the MyView class from the FingerPaint API DEMO (from Here) and would like to be able to clear the view of any drawn lines. In effect resetting it to when it as first rendered on the start of the activity. My MyView view is declared in my XML layout (not programmed).
What's the simplest way of doing this with a button?
Many thanks :)
I managed to achieve this by modifying the MyView class as below:
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MyView extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private Paint mPaint;
private List<Path> mPathsArray = new ArrayList<Path>();
public MyView(Context c, AttributeSet attrs) {
super(c, attrs);
mPaint = new Paint();
mPaint.setColor(0xFF00F1F5);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#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_8888);
mCanvas = new Canvas(mBitmap);
}
public void clear(){
mBitmap = Bitmap.createBitmap(480, 480, Bitmap.Config.ARGB_8888);
mPath = new Path();
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.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) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPathsArray.add(mPath);
}
#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;
}
}
Just incase other people want to achieve the same effect.