Android的指纹撤销/重做实施

我正在开发一个类似于Android SDK演示中FingerPaint示例的testing项目。 我试图在我的项目中实现撤销/重做function,但我尝试的东西并没有按照我的预期工作。 我在网上find类似这样的一些问题,但是他们没有帮助我,所以我问了一个新问题。

这里有一些想法,我在做什么:

  public class MyView extends View { //private static final float MINP = 0.25f; //private static final float MAXP = 0.75f; private Path mPath; private Paint mBitmapPaint; public MyView(Context c) { super(c); 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); mCanvas.drawColor(Color.WHITE); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); 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; } } 

任何build议/想法/例子,这是在我的项目上实现这种function的最佳方式?

Solutions Collecting From Web of "Android的指纹撤销/重做实施"

我不知道这是你在想什么,但这是我如何做到这一点。 而不是只将它存储在一个path中,而是存储一个包含所有path的数组,这样用户可以绘制多条线,只需稍作修改即可添加多点触摸。

要进行撤销和重做,只需从pathsvariables中删除或添加最后一个pathpath,并将它们存储在一个新的数组中。 就像是:

 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 } 

这是我修改的面板,我现在不能尝试,但上面的方法应该工作! 希望能帮助到你! (有几个额外的variables只是删除它们:)

 private ArrayList<Path> undonePaths = new ArrayList<Path>(); public class DrawingPanel extends View implements OnTouchListener { private Canvas mCanvas; private Path mPath; private Paint mPaint,circlePaint,outercirclePaint; private ArrayList<Path> paths = new ArrayList<Path>(); 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); cx = 400*DrawActivity.scale; cy = 30*DrawActivity.scale; circleRadius = 20*DrawActivity.scale; xleft = cx-10*DrawActivity.scale; xright = cx+10*DrawActivity.scale; xtop = cy-10*DrawActivity.scale; xbottom = cy+10*DrawActivity.scale; } 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; } } 

最好的解决scheme是你实现你自己的撤销/重做引擎。

  1. 在数组中执行每一个单独的动作(例如,x1,y1,[1]中的[0]圈,x2,y2到x3,y3等)

  2. 如果您需要撤消,请清除canvas并重新绘制所有从[0]到[n – 1]的n – 1动作

  3. 如果你撤消更多,只需要从[0]到[n – 2]等

我希望它给你一个提示

干杯!

实现do / redofunction的一种方法是将方法调用和调用所需的所有信息封装在一个对象中,以便可以存储它并稍后调用它 – 命令模式 。

在这个模式中,每个动作都有自己的对象:DrawCircleCommand,DrawPathCommand,FillColorCommand等。在每个对象中,draw()方法都是以独特的方式实现的,但总是被称为draw(Canvas canvas),它允许CommandManager迭代命令。 要撤消你迭代调用undo()方法的对象;

每个命令对象都实现一个接口

 public interface IDrawCommand { public void draw(Canvas canvas); public void undo(); } 

一个对象看起来像:

 public class DrawPathCommand implements IDrawCommand{ public Path path; public Paint paint; public void setPath(path){ this.path = path } public void draw(Canvas canvas) { canvas.drawPath( path, paint ); } public void undo() { //some action to remove the path } 

}

你的命令被添加到CommandManager中:

 mCommandManager.addCommand(IDrawCommand command) 

并撤销您刚才调用的命令:

 mCommandManager.undo(); 

CommandManager将这些命令存储在数据结构中,例如允许它在命令对象上迭代的列表。

你可以在这里find一个完整的教程,了解如何在Android上使用Canvas绘图的do / undo实现命令模式。

这里是另一个关于如何在Java中实现命令模式的教程;

我认为在这种情况下你可以使用两个canvas。 你知道什么时候用户开始绘画,什么时候结束。 因此,在touch_start您可以创build当前canvas的副本。 当用户点击撤消时,将您当前的canvasreplace为以前保存的canvas。

这应该保证你会有先前的状态,但我不确定performance。

幸运的是,我今天解决了这个问题。 我find了一个办法做到这一点。

 private ArrayList<Path> paths = new ArrayList<>(); private ArrayList<Path> undonePaths = new ArrayList<>(); public void undo() { if (paths.size() > 0) { LogUtils.d("undo " + paths.size()); clearDraw(); undonePaths.add(paths.remove(paths.size() - 1)); invalidate(); } } public void redo() { if (undonePaths.size() > 0) { LogUtils.d("redo " + undonePaths.size()); clearDraw(); paths.add(undonePaths.remove(undonePaths.size() - 1)); invalidate(); } } public void clearDraw() { mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); mCanvas.setBitmap(mBitmap); invalidate(); } public void clear() { paths.clear(); undonePaths.clear(); invalidate(); } 

在活动。

 private DrawView mDrawView; if (v == mIvUndo) { mDrawView.undo(); } else if (v == mIvRedo) { mDrawView.redo(); 

在xml中

 <com.cinread.note.view.DrawView android:id="@+id/paintView" android:layout_width="match_parent" android:layout_height="match_parent"/> 

如果你有更好的解决scheme,你可以联系我。

[这是我写的博客

http://blog.csdn.net/sky_pjf/article/details/51086901%5D