我使用上面的代码进行撤消,但是通过调用此方法无法更新canvas。 我已经为Sketch功能创建了DrawingView类,我只需要对每个功能进行撤销,例如,1。Smoothline 2.Rectangle 3.Circle 4.Straight line etc.
public class DrawingView extends View {
protected Path mPath = new Path();
public static Paint mPaint;
protected Bitmap mBitmap;
public static Canvas mCanvas;
public int TOUCH_TOLERANCE = 5;
public static int lineThickness = 5;
public static int mCurrentShape;
public boolean isDrawing = false;
float mx,my,mStartX,mStartY;
private int stateToSave;
public static Paint mPaintErase = new Paint();
protected Path mPathErase = new Path();
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
public DrawingView(Context context) {
super(context);
init();
EraseLine();
}
public DrawingView(Context context,AttributeSet attrs)
{
super(context, attrs);
init();
EraseLine();
}
/**
* Create Path from Event.
* @param event
* @return
*/
private Path createPath(MotionEvent event) {
Path path = new Path();
// Save for ACTION_MOVE
this.mStartX = event.getX();
this.mStartY = event.getY();
path.moveTo(this.mStartX, this.mStartY);
return path;
}
protected void init()
{
mPaint = new Paint(Paint.DITHER_FLAG);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(getContext().getResources().getColor(R.color.colorYellow));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(lineThickness);
mPath = new Path();
paths.add(mPath);
}
public Bitmap getBitmap()
{
this.setDrawingCacheEnabled(true);
this.buildDrawingCache();
Bitmap bmp = Bitmap.createBitmap(this.getDrawingCache());
this.setDrawingCacheEnabled(false);
return bmp;
}
@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);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
mx = event.getX();
my = event.getY();
switch (mCurrentShape)
{
case 1:
onTouchEventSmoothLine(event);
break;
case 2:
onTouchEventErase(event);
break;
case 3:
onTouchEventLine(event);
break;
case 4:
onTouchEventCircle(event);
break;
case 5:
onTouchEventRectangle(event);
break;
case 6:
break;
default:
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
if (isDrawing)
{
switch (mCurrentShape)
{
case 1:
break;
case 2:
// EraseLine();
break;
case 3:
onDrawLine(canvas);
break;
case 4:
onDrawCircle(canvas);
break;
case 5:
onDrawRectangle(canvas);
break;
case 6:
onDrawSmoothLine(canvas);
break;
default:
break;
}
}
}
//------------------------------------------------------------------
// Rectangle
//------------------------------------------------------------------
private void onTouchEventRectangle(MotionEvent event) {
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
drawRectangle(mCanvas,mPaint);
invalidate();
break;
}
}
private void onDrawRectangle(Canvas canvas) {
drawRectangle(canvas, mPaint);
}
private void drawRectangle(Canvas canvas,Paint paint){
float right = mStartX > mx ? mStartX : mx;
float left = mStartX > mx ? mx : mStartX;
float bottom = mStartY > my ? mStartY : my;
float top = mStartY > my ? my : mStartY;
canvas.drawRect(left, top, right, bottom, paint);
}
//------------------------------------------------------------------
// Circle
//------------------------------------------------------------------
private void onDrawCircle(Canvas canvas) {
canvas.drawCircle(mStartX, mStartY, calculateRadius(mStartX, mStartY, mx, my), mPaint);
}
private void onTouchEventCircle(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mCanvas.drawCircle(mStartX, mStartY,
calculateRadius(mStartX,mStartY,mx,my),mPaint);
invalidate();
break;
default:
break;
}
}
/**
* @return
*/
protected float calculateRadius(float x1, float y1, float x2, float y2) {
return (float) Math.sqrt(
Math.pow(x1 - x2, 2) +
Math.pow(y1 - y2, 2)
);
}
//------------------------------------------------------------------
// Line
//------------------------------------------------------------------
private void onDrawLine(Canvas canvas) {
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
canvas.drawLine(mStartX, mStartY, mx, my, mPaint);
}
}
private void onTouchEventLine(MotionEvent event) {
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mCanvas.drawLine(mStartX, mStartY, mx, my, mPaint);
invalidate();
break;
}
}
//------------------------------------------------------------------
// Smooth Line
//------------------------------------------------------------------
private void onDrawSmoothLine(Canvas canvas)
{
for (Path p : paths)
{
canvas.drawPath(p, mPaint);
}
}
private void onTouchEventSmoothLine(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
mPath.reset();
mPath.moveTo(mx, my);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mStartX, mStartY, (mx + mStartX) / 2, (my + mStartY) / 2);
mStartX = mx;
mStartY = my;
}
mCanvas.drawPath(mPath, mPaint);
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mPath.lineTo(mStartX, mStartY);
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
paths.add(mPath);
invalidate();
break;
}
}
//------------------------------------------------------------------
// Erase Line
//------------------------------------------------------------------
public void EraseLine()
{
mPaintErase.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaintErase.setColor(Color.TRANSPARENT);
mPaintErase.setAntiAlias(true);
mPaintErase.setDither(true);
mPaintErase.setStyle(Paint.Style.STROKE);
mPaintErase.setStrokeJoin(Paint.Join.ROUND);
mPaintErase.setStrokeCap(Paint.Cap.ROUND);
mPaintErase.setStrokeWidth(lineThickness);
}
private void onTouchEventErase(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
mPathErase.reset();
mPathErase.moveTo(mx, my);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPathErase.quadTo(mStartX, mStartY, (mx + mStartX) / 2, (my + mStartY) / 2);
mStartX = mx;
mStartY = my;
}
mCanvas.drawPath(mPathErase, mPaintErase);
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mPathErase.lineTo(mStartX, mStartY);
mCanvas.drawPath(mPathErase, mPaintErase);
mPathErase.reset();
invalidate();
break;
}
}
/**
* Undo functionality.
*/
public void onClickUndo ()
{
if (paths.size()>0)
{
paths.remove(paths.size()-1);
invalidate();
}
else
{
Toast.makeText(getContext().getApplicationContext(),"Undo not found.",Toast.LENGTH_SHORT).show();
}
//toast the user
}
@Override
public Parcelable onSaveInstanceState() {
//begin boilerplate code that allows parent classes to save state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
//end
ss.stateToSave = this.stateToSave;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
//begin boilerplate code so parent classes can restore state
if(!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
//end
this.stateToSave = ss.stateToSave;
}
static class SavedState extends BaseSavedState {
int stateToSave;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.stateToSave = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(this.stateToSave);
}
//required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
答案 0 :(得分:2)
我在这里找到了解决方案并且它正常工作。
请检查此代码,了解Sketch over image的笔,线,圆,矩形和撤消功能:
package com.thefinal3.Sketch;
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.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.thefinal3.Bean.PathBean;
import com.thefinal3.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Akash patel on 11-04-2016.
*/
public class DrawingView extends View {
protected Path mPath = new Path();
public static Paint mPaint;
protected Bitmap mBitmap;
public static Canvas mCanvas;
public int TOUCH_TOLERANCE = 5;
public static int lineThickness = 5;
public static int ColorStroke;
public static int mCurrentShape;
public boolean isDrawing = false;
float mx,my,mStartX,mStartY;
private int stateToSave;
public static Paint mPaintErase = new Paint();
public static Paint mPaintBitmap;
protected Path mPathErase = new Path();
Context context;
private ArrayList<PathBean> paths = new ArrayList<PathBean>();
public DrawingView(Context context,AttributeSet attrs)
{
super(context, attrs);
setFocusable(true);
setFocusableInTouchMode(true);
init();
EraseLine();
this.context = context;
}
protected void init()
{
mPaint = new Paint(Paint.DITHER_FLAG);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(getContext().getResources().getColor(R.color.colorYellow));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(lineThickness);
ColorStroke = mPaint.getColor();
lineThickness = (int)mPaint.getStrokeWidth();
mCanvas = new Canvas();
}
public Bitmap getBitmap()
{
this.setDrawingCacheEnabled(true);
this.buildDrawingCache();
Bitmap bmp = Bitmap.createBitmap(this.getDrawingCache());
this.setDrawingCacheEnabled(false);
return bmp;
}
@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.setBitmap(mBitmap);
mCanvas = new Canvas(mBitmap);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
mx = event.getX();
my = event.getY();
switch (mCurrentShape)
{
case 1:
onTouchEventSmoothLine(event);
break;
case 2:
// onTouchEventErase(event);
break;
case 3:
onTouchEventLine(event);
break;
case 4:
onTouchEventCircle(event);
break;
case 5:
onTouchEventRectangle(event);
break;
default:
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (PathBean p : paths)
{
mPaint.setColor(p.getColor());
mPaint.setStrokeWidth(p.getLineWidthStroke());
switch (p.getDrawingType())
{
case 1:
canvas.drawPath(p.getmPath(), mPaint);
Log.e("onDraw ;-->", "Path Called...");
break;
case 2:
break;
case 3:
float dx1 = Math.abs(p.getMx() - p.getmStartX());
float dy1 = Math.abs(p.getMy() - p.getmStartY());
if (dx1 >= TOUCH_TOLERANCE || dy1 >= TOUCH_TOLERANCE)
{
canvas.drawLine(p.getmStartX(), p.getmStartY(), p.getMx(), p.getMy(), mPaint);
Log.e("onDraw ;-->", "Line Called...");
}
break;
case 4:
canvas.drawCircle(p.getmStartX(), p.getmStartY(), calculateRadius(p.getmStartX(), p.getmStartY(), p.getMx(), p.getMy()), mPaint);
break;
case 5:
float right = p.getmStartX() > p.getMx() ? p.getmStartX() : p.getMx();
float left = p.getmStartX() > p.getMx() ? p.getMx() : p.getmStartX();
float bottom = p.getmStartY() > p.getMy() ? p.getmStartY() : p.getMy();
float top = p.getmStartY() > p.getMy() ? p.getMy() : p.getmStartY();
canvas.drawRect(left, top, right, bottom,mPaint);
break;
default:
break;
}
}
mPaint.setColor(ColorStroke);
mPaint.setStrokeWidth(lineThickness);
if (isDrawing)
{
switch (mCurrentShape)
{
case 1:
canvas.drawPath(mPath, mPaint);
break;
case 2:
break;
case 3:
onDrawLine(canvas);
break;
case 4:
onDrawCircle(canvas);
break;
case 5:
onDrawRectangle(canvas);
break;
default:
break;
}
}
}
//------------------------------------------------------------------
// Rectangle
//------------------------------------------------------------------
private void onTouchEventRectangle(MotionEvent event) {
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
drawRectangle(mCanvas,mPaint);
PathBean bean = new PathBean();
bean.setColor(ColorStroke);
bean.setLineWidthStroke(lineThickness);
bean.setmStartX(mStartX);
bean.setmStartY(mStartY);
bean.setMx(mx);
bean.setMy(my);
bean.setDrawingType(5);
paths.add(bean);
invalidate();
break;
}
}
private void onDrawRectangle(Canvas canvas) {
drawRectangle(canvas, mPaint);
}
private void drawRectangle(Canvas canvas,Paint paint){
float right = mStartX > mx ? mStartX : mx;
float left = mStartX > mx ? mx : mStartX;
float bottom = mStartY > my ? mStartY : my;
float top = mStartY > my ? my : mStartY;
canvas.drawRect(left, top, right, bottom, paint);
}
//------------------------------------------------------------------
// Circle
//------------------------------------------------------------------
private void onDrawCircle(Canvas canvas) {
canvas.drawCircle(mStartX, mStartY, calculateRadius(mStartX, mStartY, mx, my), mPaint);
}
private void onTouchEventCircle(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mCanvas.drawCircle(mStartX, mStartY,
calculateRadius(mStartX, mStartY, mx, my), mPaint);
PathBean bean = new PathBean();
bean.setColor(ColorStroke);
bean.setLineWidthStroke(lineThickness);
bean.setmStartX(mStartX);
bean.setmStartY(mStartY);
bean.setMx(mx);
bean.setMy(my);
bean.setDrawingType(4);
paths.add(bean);
invalidate();
break;
default:
break;
}
}
/**
* @return
*/
protected float calculateRadius(float x1, float y1, float x2, float y2) {
return (float) Math.sqrt(
Math.pow(x1 - x2, 2) +
Math.pow(y1 - y2, 2)
);
}
//------------------------------------------------------------------
// Line
//------------------------------------------------------------------
private void onDrawLine(Canvas canvas) {
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
canvas.drawLine(mStartX, mStartY, mx, my, mPaint);
}
}
private void onTouchEventLine(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mCanvas.drawLine(mStartX, mStartY, mx, my, mPaint);
PathBean bean = new PathBean();
bean.setColor(ColorStroke);
bean.setLineWidthStroke(lineThickness);
bean.setmStartX(mStartX);
bean.setmStartY(mStartY);
bean.setMx(mx);
bean.setMy(my);
bean.setDrawingType(3);
paths.add(bean);
invalidate();
break;
}
}
//------------------------------------------------------------------
// Smooth Line
//------------------------------------------------------------------
private void onDrawSmoothLine(Canvas canvas) {
for (PathBean p : paths)
{
mPaint.setColor(p.getColor());
canvas.drawPath(p.getmPath(), mPaint);
}
mPaint.setColor(ColorStroke);
canvas.drawPath(mPath, mPaint);
}
private void onTouchEventSmoothLine(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
mPath.reset();
mPath.moveTo(mx, my);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mStartX, mStartY, (mx + mStartX) / 2, (my + mStartY) / 2);
mStartX = mx;
mStartY = my;
}
mCanvas.drawPath(mPath, mPaint);
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mPath.lineTo(mStartX, mStartY);
mCanvas.drawPath(mPath, mPaint);
// mPath.reset();
PathBean bean = new PathBean();
bean.setmPath(mPath);
bean.setColor(mPaint.getColor());
bean.setLineWidthStroke((int)mPaint.getStrokeWidth());
bean.setDrawingType(1);
paths.add(bean);
mPath = new Path();
invalidate();
break;
}
}
//------------------------------------------------------------------
// Erase Line
//------------------------------------------------------------------
public void EraseLine()
{
mPaintErase.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaintErase.setColor(Color.TRANSPARENT);
mPaintErase.setAntiAlias(true);
mPaintErase.setDither(true);
mPaintErase.setStyle(Paint.Style.STROKE);
mPaintErase.setStrokeJoin(Paint.Join.ROUND);
mPaintErase.setStrokeCap(Paint.Cap.ROUND);
mPaintErase.setStrokeWidth(lineThickness);
mPaintBitmap = new Paint(Paint.DITHER_FLAG);
}
private void onTouchEventErase(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
mPathErase.reset();
mPathErase.moveTo(mx, my);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPathErase.quadTo(mStartX, mStartY, (mx + mStartX) / 2, (my + mStartY) / 2);
mStartX = mx;
mStartY = my;
}
mCanvas.drawPath(mPathErase, mPaintErase);
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mPathErase.lineTo(mStartX, mStartY);
mCanvas.drawPath(mPathErase, mPaintErase);
// mPathErase.reset();
PathBean bean = new PathBean();
bean.setmPath(mPathErase);
bean.setLineWidthStroke(lineThickness);
bean.setDrawingType(2);
paths.add(bean);
mPathErase = new Path();
invalidate();
break;
}
}
/**
* Undo functionality.
*/
public void onClickUndo ()
{
if (paths.size()>0)
{
paths.remove(paths.size()-1);
invalidate();
}
else
{
Toast.makeText(getContext().getApplicationContext(),"There is no undo action found.",Toast.LENGTH_SHORT).show();
}
}
@Override
public Parcelable onSaveInstanceState() {
//begin boilerplate code that allows parent classes to save state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
//end
ss.stateToSave = this.stateToSave;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
//begin boilerplate code so parent classes can restore state
if(!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
//end
this.stateToSave = ss.stateToSave;
}
static class SavedState extends BaseSavedState {
int stateToSave;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.stateToSave = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(this.stateToSave);
}
//required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
答案 1 :(得分:0)
你绘制所有的圆圈,对位图进行处理,并且位图不会自行擦除。因此当你从路径中删除路径并使视图无效时,它只会在位图上绘制一个叠加层。
你需要通过将paint的颜色设置为backgroundColor来擦除位图,并在每次需要撤消时调用mCanvas.drawRect(0,0,mBitmap.getWidth(),mBitmap.getHeight(),mPaint)动作,然后你可以重绘剩余的路径。