前段时间研究了下涂鸦功能的实现,其实单独的涂鸦实现起来还是挺简单的,关键的技术难点是撤销与重做功能的实现。但是这里暂时只说明下涂鸦功能的实现,高手勿喷哈,而且该功能在Android SDK提供的APIDemo当中就有的,但是如果能够将该地方的知识点搞懂的话,我认为View画图基本上是难不倒你了,特别是里面为什么要用一个中间的Bitmap。老规矩,还是先看看效果图吧:

代码如下:

  1. package cn.ych.tuya;
  2. import java.io.File;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.util.ArrayList;
  7. import java.util.Iterator;
  8. import java.util.List;
  9. import android.content.Context;
  10. import android.graphics.Bitmap;
  11. import android.graphics.Canvas;
  12. import android.graphics.Paint;
  13. import android.graphics.Path;
  14. import android.graphics.Bitmap.CompressFormat;
  15. import android.os.Environment;
  16. import android.view.MotionEvent;
  17. import android.view.View;
  18. /**
  19. *
  20. * @category: View实现涂鸦、撤销以及重做功能
  21. * @author: 锋翼
  22. * @link: www.apkstory.com
  23. * @date: 2012.1.4
  24. *
  25. */
  26. public class TuyaView extends View {
  27. private Bitmap mBitmap;
  28. private Canvas mCanvas;
  29. private Path mPath;
  30. private Paint mBitmapPaint;// 画布的画笔
  31. private Paint mPaint;// 真实的画笔
  32. private float mX, mY;//临时点坐标
  33. private static final float TOUCH_TOLERANCE = 4;
  34. private int screenWidth, screenHeight;// 屏幕長寬
  35. public TuyaView(Context context, int w, int h) {
  36. super(context);
  37. screenWidth = w;
  38. screenHeight = h;
  39. mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
  40. Bitmap.Config.ARGB_8888);
  41. // 保存一次一次绘制出来的图形
  42. mCanvas = new Canvas(mBitmap);
  43. mBitmapPaint = new Paint(Paint.DITHER_FLAG);
  44. mPaint = new Paint();
  45. mPaint.setAntiAlias(true);
  46. mPaint.setStyle(Paint.Style.STROKE);
  47. mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘
  48. mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形状
  49. mPaint.setStrokeWidth(5);// 画笔宽度
  50. }
  51. @Override
  52. public void onDraw(Canvas canvas) {
  53. canvas.drawColor(0xFFAAAAAA);
  54. // 将前面已经画过得显示出来
  55. canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
  56. if (mPath != null) {
  57. // 实时的显示
  58. canvas.drawPath(mPath, mPaint);
  59. }
  60. }
  61. private void touch_start(float x, float y) {
  62. mPath.moveTo(x, y);
  63. mX = x;
  64. mY = y;
  65. }
  66. private void touch_move(float x, float y) {
  67. float dx = Math.abs(x - mX);
  68. float dy = Math.abs(mY - y);
  69. 触摸间隔大于阈值才绘制路径
  70. if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
  71. // 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)
  72. mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
  73. mX = x;
  74. mY = y;
  75. }
  76. }
  77. private void touch_up() {
  78. mPath.lineTo(mX, mY);
  79. mCanvas.drawPath(mPath, mPaint);
  80. }
  81. @Override
  82. public boolean onTouchEvent(MotionEvent event) {
  83. float x = event.getX();
  84. float y = event.getY();
  85. switch (event.getAction()) {
  86. case MotionEvent.ACTION_DOWN:
  87. // 每次down下去重新new一个Path
  88. mPath = new Path();
  89. touch_start(x, y);
  90. invalidate();
  91. break;
  92. case MotionEvent.ACTION_MOVE:
  93. touch_move(x, y);
  94. invalidate();
  95. break;
  96. case MotionEvent.ACTION_UP:
  97. touch_up();
  98. invalidate();
  99. break;
  100. }
  101. return true;
  102. }
  103. }

复制代码

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

老规矩,先上效果图:  
代码如下:

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

复制代码

Android简单涂鸦以及撤销、重做的实现方法相关推荐

  1. android 自定义画笔,在Android Canvas中使用撤销/重做操作的自定义画笔

    我想使用自定义画笔&撤消/重做操作实现画布绘制应用程序.首先,我的代码完美工作,无需使用自定义画笔(包括撤消/重做操作).根据这个答案How to make custom brush for ...

  2. android 简单涂鸦

    部分代码借鉴网上. 看图先: 主界面Activity : package com.mj.candraw; import android.app.Activity; import android.os. ...

  3. C# wpf 实现自定义撤销重做功能

    文章目录 前言 一.具体实现 1.完整代码 二.使用示例 1.拖动控件 (1)MainWindow.xaml (2)MainWindow.xaml.cs (3)效果预览 总结 前言 做wpf界面的时候 ...

  4. 实现编辑功能有哪几个action_Web 应用的撤销重做实现

    背景 前不久,我参与开发了团队中的一个 web 应用,其中的一个页面操作如下图所示: GIF 这个制作间页面有着类似 PPT 的交互:从左侧的工具栏中选择元素放入中间的画布.在画布中可以删除.操作(拖 ...

  5. Mac OS开发—Xcode给Mac应用添加编辑快捷键(剪切 复制 粘贴 全选 删除 撤销 重做)功能

    前言 笔者做Windows开发的,无奈公司有Mac OS的项目需要维护,之前没有编辑菜单的功能,需要笔者加上这个功能,笔者只能硬着头皮上.还好会c++,Mac应用里面一般都会用object-c 也可以 ...

  6. Android简单适配9.0~12.0

    适配6.0~8.0的可以看下这个: 适配Android6.0到8.0详细过程--小白教程 注意:10.0到12.0的都是些网上看到的资料,我就适配到10.0的文件存储,如有不对的,可以留言,我会查阅修 ...

  7. 游戏开发中如何设计一个撤销重做系统DoUnDo

    我现在负责做捏脸的模块,其中需要做撤销重做,老实说上一次做还是大学的时候做绘图程序有用到过,那个时候就是简单用了一个栈来操作.当时的一个核心思想是不管你绘制的是矩形.三角形.还是什么形状,底层他们都有 ...

  8. C++ 实现自定义撤销重做功能

    文章目录 前言 一.完整代码 二.使用示例 1.基本用法 2.gdi画线撤销 总结 前言 在使用c++做界面开发的时候,需要涉及到到撤销重做操作,尤其是实现白板功能时需要自己实现一套撤销重做功能,如果 ...

  9. android os开机画面,Android简单实现启动画面的方法

    本文实例讲述了Android简单实现启动画面的方法.分享给大家供大家参考,具体如下: 核心代码: package com.demo.app; import android.app.Activity; ...

最新文章

  1. 收藏 | 多目标跟踪(MOT)入门
  2. 大数据查询——HBase读写设计与实践--转
  3. AI:2020年6月22日北京智源大会演讲分享之认知神经基础专题论坛——13:40-14:20毕彦超教授《Knowledge representation in the Human brain》
  4. udp重发机制_UDP 协议
  5. 《Go学习笔记 . 雨痕》方法
  6. 2018-2019-2 20165118 《网络对抗技术》Exp4 恶意代码分析
  7. EMNLP'21 | Dialogue System 对话系统
  8. qmessagebox 设置显示屏幕中间_windows7屏幕分辨率设置
  9. 沉浮二十载,奥飞成也萧何败也萧何?
  10. matlab线性规划求解函数:linprog
  11. 【IAST安全左移最佳工具】
  12. 重装系统win10安装教程(超详细)
  13. CTF_RSA_N不互素
  14. CAD文字显示号问题原因和解决办法
  15. 阿里企业邮箱服务器配置
  16. 离群值是什么意思_离群值!离群值?离群值!
  17. Cesium 系列1 - 简介及源码下载发布
  18. TTS(TextToSpeach)语音朗读实例
  19. 2022最新软件测试面试题(含答案)
  20. java绘图技术,演示绘制不同的图形

热门文章

  1. 国内外最流行的 Python编辑器【IDE】 TOP 8
  2. 第八届CUIT电磁智能车比赛参赛学习总结 Part1:工字电感+LMV358型电磁杆
  3. 【关于d2l包】记录Mac M1
  4. 态棒,阿里的长矛还是利盾?
  5. 模糊控制-模糊是什么鬼
  6. 涨知识!智能手机还能一键提取音频文件内的文字,成功地把语音变成文字
  7. CASS11.0又双叒更新了,增加一些实用功能,免狗支持2010-2022
  8. 张正友标定法——相机参数的标定
  9. 从程序员到项目经理(18):不要试图和下属做朋友【转载】
  10. 读灵遁者诗歌:像晚霞一样的谜团