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

 

View实现涂鸦、撤销以及重做功能的更多相关文章

  1. C#中泛型容器Stack<T>的用法,以及借此实现”撤销/重做”功能

    .Net为我们提供了众多的泛型集合.比如,Stack<T>先进后出,Queue<T>先进先出,List<T>集合元素可排序,支持索引,LinkedList<T ...

  2. AE二次开发技巧之撤销、重做

    原文地址:http://www.cnblogs.com/wylaok/articles/2363208.html 可以把AE自带的重做.撤销按钮或工具添加到axToolBarControl上,再把ax ...

  3. Vim的撤销与重做

    命令模式下 u:撤销 Ctrl+r:重做(撤销撤销)

  4. 功能强大的Northwoods GoDiagram控件库

    Northwoods GoDiagram控件库用于开发图形应用 Northwoods GoDiagram控件库是付费软件,其官方网址为http://www.nwoods.com/ Northwoods ...

  5. Windows 7/8 创建WIFI热点

    问题描述:很多人(特别是中国的大学生)都拥有一台联网的笔记本电脑,而手机使用的却是电信运营商提供的限制数量和速度的GPRS. 很多人不敢想象:联网的笔记本电脑能够将其流量以WiFi的形式共享出来供其它 ...

  6. 11 个 Linux 上最佳的图形化 Git 客户端

    Git是软件开发和若干其他版本控制任务免费和开源的分布式版本控制系统.它被设计用来以应付一切从小到非常大的项目,基于速度,效率和数据完整性. Linux用户主要通过命令行管理Git,不过,一些图形用户 ...

  7. 1.1.Core Data是什么(Core Data 应用程序实践指南)

    Core Data是个框架,把数据当作对象来操作. 由Core Data提供的数据对象叫托管对象(managed objecgt),而Core Data 位于程序和持久化存储区之间. 托管对象模型里有 ...

  8. 【WPF】学习笔记(一)——做一个简单的电子签名板

    参加实习(WPF)已经有两个多周的时间了,踩了一些坑,也算积累了一些小东西,准备慢慢拿出来分享一下.(●'◡'●) 这次呢就讲讲一个简单的电子签名板的实现. 先上张图(PS:字写得比较丑,不要太在意哈 ...

  9. Snapde和常用的CSV文件编辑器对比

    Snapde,一个专门为编辑超大型数据量CSV文件而设计的单机版电子表格软件:它运行的速度非常快,反应非常灵敏. CSV是一种用逗号分隔列.回车分割行的文本文件,市面上常用的CSV编辑软件有:Snap ...

随机推荐

  1. enum型常量

    就像结构体一样,定义一个枚举类型是不分配内存的,仅仅是定义了一个类型的名字,下面可以使用这个名字定义枚举类型的变量 枚举即将变量的值一一列举出来变量的值只限于列举出来的值得范围内 简单的应用如下 #i ...

  2. BZOJ 1004: [HNOI2008]Cards( 置换群 + burnside引理 + 背包dp + 乘法逆元 )

    题意保证了是一个置换群. 根据burnside引理, 答案为Σc(f) / (M+1). c(f)表示置换f的不动点数, 而题目限制了颜色的数量, 所以还得满足题目, 用背包dp来计算.dp(x,i, ...

  3. 将 jsp 页面的值 传到struts2 action中(不是表单中的值)

    JSP: 页面: <%@ page language="java"  pageEncoding="GBK"%> <%@taglib prefi ...

  4. mysqli_set_charset和SET NAMES优劣分析

    bool mysqli_set_charset ( mysqli $link , string $charset ) 这应该是首选的用于改变字符编码的方法,不建议使用 mysqli_query()执行 ...

  5. 将EC2里的实例导出到RAW文件并进行修改

    你可能有自己的instance在amazon云环境里面,或者是你想深度修改一下marketplace里面提供的那些系统又估计运行中的instance改动不方便 亚马逊作为云计算领域的大哥大,我不得不说 ...

  6. 文本导出到pdf文件(使用QPrinter和QPainter和QTextDocument)

    程序中数据导出是经常有的需求,今天学习把文本导出到pdf文件.主要是用QPrinter,QPainter TextEditToPdf::TextEditToPdf(QWidget *parent, Q ...

  7. WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]

    原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...

  8. 设计模式(三)建造者模式Builder(创建型)

    1. 概述 在软件开发的过程中,当遇到一个“复杂的对象”的创建工作,该对象由一定各个部分的子对象用一定的算法构成,由于需求的变化,复杂对象的各个部分经常面临剧烈的变化,但将它们组合在一起的算法相对稳定 ...

  9. TF-IDF与余弦相似性的应用(一):自动提取关键词 - 阮一峰的网络日志

    TF-IDF与余弦相似性的应用(一):自动提取关键词 - 阮一峰的网络日志     TF-IDF与余弦相似性的应用(一):自动提取关键词     作者: 阮一峰     日期: 2013年3月15日 ...

  10. Django的url解析

    Django中采用正则表达式来匹配所请求的url,这个叫做URLconf,作为MVC中的C(控制器), 这样再调用相应的试图,达到控制器控制试图的显示的效果. 看一下Django的url解析过程 第一 ...