涂鸦撤销与重做的功能实现

撤销与重做在很多地方都是很重要的功能,比如PS里面、Word里面等等,而且大部分童鞋都能够想到要实现该功能应该需要用到堆栈,对于一些大牛的话可能就直接想到设计模式上面去了,比如命令模式就可以解决撤销与重做的问题。我们这里要讲解的是利用集合来完成该功能,其实也就是模拟栈,我相信你懂得。

老规矩,先上效果图:

代码如下:

  1. public class TuyaView extends View {  
  2.   
  3. private Bitmap mBitmap;  
  4. private Canvas mCanvas;  
  5. private Path mPath;  
  6. private Paint mBitmapPaint;// 画布的画笔  
  7. private Paint mPaint;// 真实的画笔  
  8. private float mX, mY;// 临时点坐标  
  9. private static final float TOUCH_TOLERANCE = 4;  
  10.   
  11. // 保存Path路径的集合,用List集合来模拟栈  
  12. private static List<DrawPath> savePath;  
  13. // 记录Path路径的对象  
  14. private DrawPath dp;  
  15.   
  16. private int screenWidth, screenHeight;// 屏幕長寬  
  17.   
  18. private class DrawPath {  
  19. public Path path;// 路径  
  20. public Paint paint;// 画笔  
  21. }  
  22.   
  23. public TuyaView(Context context, int w, int h) {  
  24. super(context);  
  25. screenWidth = w;  
  26. screenHeight = h;  
  27.   
  28. mBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888);  
  29. // 保存一次一次绘制出来的图形  
  30. mCanvas = new Canvas(mBitmap);  
  31.   
  32. mBitmapPaint = new Paint(Paint.DITHER_FLAG);  
  33. mPaint = new Paint();  
  34. mPaint.setAntiAlias(true);  
  35. mPaint.setStyle(Paint.Style.STROKE);  
  36. mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘  
  37. mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形状  
  38. mPaint.setStrokeWidth(5);// 画笔宽度  
  39.   
  40. savePath = new ArrayList<DrawPath>();  
  41. }  
  42.   
  43. @Override  
  44. public void onDraw(Canvas canvas) {  
  45. canvas.drawColor(0xFFAAAAAA);  
  46. // 将前面已经画过得显示出来  
  47. canvas.drawBitmap(mBitmap, 00, mBitmapPaint);  
  48. if (mPath != null) {  
  49. // 实时的显示  
  50. canvas.drawPath(mPath, mPaint);  
  51. }  
  52. }  
  53.   
  54. private void touch_start(float x, float y) {  
  55. mPath.moveTo(x, y);  
  56. mX = x;  
  57. mY = y;  
  58. }  
  59.   
  60. private void touch_move(float x, float y) {  
  61. float dx = Math.abs(x - mX);  
  62. float dy = Math.abs(mY - y);  
  63. if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {  
  64. // 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)  
  65. mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);  
  66. mX = x;  
  67. mY = y;  
  68. }  
  69. }  
  70.   
  71. private void touch_up() {  
  72. mPath.lineTo(mX, mY);  
  73. mCanvas.drawPath(mPath, mPaint);  
  74. //将一条完整的路径保存下来(相当于入栈操作)  
  75. savePath.add(dp);  
  76. mPath = null;// 重新置空  
  77. }  
  78. /** 
  79. * 撤销的核心思想就是将画布清空, 
  80. * 将保存下来的Path路径最后一个移除掉, 
  81. * 重新将路径画在画布上面。 
  82. */  
  83. public void undo() {  
  84. mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,  
  85. Bitmap.Config.ARGB_8888);  
  86. mCanvas.setBitmap(mBitmap);// 重新设置画布,相当于清空画布  
  87. // 清空画布,但是如果图片有背景的话,则使用上面的重新初始化的方法,用该方法会将背景清空掉...  
  88. if (savePath != null && savePath.size() > 0) {  
  89. // 移除最后一个path,相当于出栈操作  
  90. savePath.remove(savePath.size() - 1);  
  91.   
  92. Iterator<DrawPath> iter = savePath.iterator();  
  93. while (iter.hasNext()) {  
  94. DrawPath drawPath = iter.next();  
  95. mCanvas.drawPath(drawPath.path, drawPath.paint);  
  96. }  
  97. invalidate();// 刷新  
  98.   
  99. /*在这里保存图片纯粹是为了方便,保存图片进行验证*/  
  100. String fileUrl = Environment.getExternalStorageDirectory()  
  101. .toString() + "/android/data/test.png";  
  102. try {  
  103. FileOutputStream fos = new FileOutputStream(new File(fileUrl));  
  104. mBitmap.compress(CompressFormat.PNG, 100, fos);  
  105. fos.flush();  
  106. fos.close();  
  107. catch (FileNotFoundException e) {  
  108. e.printStackTrace();  
  109. catch (IOException e) {  
  110. e.printStackTrace();  
  111. }  
  112.   
  113. }  
  114. }  
  115. /** 
  116. * 重做的核心思想就是将撤销的路径保存到另外一个集合里面(栈), 
  117. * 然后从redo的集合里面取出最顶端对象, 
  118. * 画在画布上面即可。 
  119. */  
  120. public void redo(){  
  121. //如果撤销你懂了的话,那就试试重做吧。  
  122. }  
  123.   
  124. @Override  
  125. public boolean onTouchEvent(MotionEvent event) {  
  126. float x = event.getX();  
  127. float y = event.getY();  
  128.   
  129. switch (event.getAction()) {  
  130. case MotionEvent.ACTION_DOWN:  
  131. // 每次down下去重新new一个Path  
  132. mPath = new Path();  
  133. //每一次记录的路径对象是不一样的  
  134. dp = new DrawPath();  
  135. dp.path = mPath;  
  136. dp.paint = mPaint;  
  137. touch_start(x, y);  
  138. invalidate();  
  139. break;  
  140. case MotionEvent.ACTION_MOVE:  
  141. touch_move(x, y);  
  142. invalidate();  
  143. break;  
  144. case MotionEvent.ACTION_UP:  
  145. touch_up();  
  146. invalidate();  
  147. break;  
  148. }  
  149. return true;  
  150. }  
  151.   
  152. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值