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. PyQt中登录框设计

    很多软件,比如QQ,亦或一些管理系统,运行之后都会先弹出一个登录框,只有登录成功了,才能进入软件主界面. 以前在邮件列表中回答过如何做登录框,这里重新整理下. 从刚开始做Delphi的时候就有不少人纠 ...

  2. 一天一个类 --- StringTokenizer

    这是一个将字符串按照指定的delimiters(分隔符)进行分割的类. 首先看看他的构造函数: public StringTokenizer(String str, String delim, boo ...

  3. static timing analysis 基础

    此博文依据 特权同学在电子发烧友上的讲座PPT进行整理而成. static timing analysis   静态时序分析基础 过约束:有不必要的约束,或者是约束不能再某一情况下满足.——约束过头了 ...

  4. 发送邮件给某人:mailto标签

    mailto标签 1.标签最简式 <a href="mailto:xxx@xx.com">联系站长</a> 2.标签帮你填抄送地址 <a href=& ...

  5. zookeeper perl 版本需求

    [root@wx03 ~]# perl -v This is perl 5, version 22, subversion 1 (v5.22.1) built for x86_64-linux Cop ...

  6. cocos2d-实现读取.plist文件(使用数组CCArray)

    学习札记之cocos2d-x2.1.1实现读取.plist文件(使用数组CCArray) <?xml version="1.0" encoding="UTF-8&q ...

  7. C# 课堂总结4-类(常用的类)

    一.string类 1. str.Length:字符串的长度 *****str[索引号] 2. str.Trim():去除左右两边的空格 *****str.TrimStart():去掉左边的空格str ...

  8. 简单字符串处理 hdu1062 Text Reverse

    虽然这个题目一遍AC,但是心里还是忍不住骂了句shit! 花了一个小时,这个题目已经水到一定程度了,但是我却在反转这个操作上含糊不清,并且还是在采用了辅助数组的情况下,关系的理顺都如此之难. 其实我是 ...

  9. 关于C++异常机制的笔记(SEH, try-catch)

    昨天晚上加班解决了一个问题,是由于无法正确的捕获到异常导致的.刚开始用try-catch,但是没法捕获到异常:后面改成SEH异常才解决.因此今天将这个问题重新梳理了一遍,关于try-catch, SE ...

  10. SAP 标准单价、移动单价在 AP 中的影响--(详细)

    今天我将向大家介绍下SAP中两种单价模式在系统中所产生的影响,先主要讲讲在AP中影响,它主要有两个方面产生影响(物料收货migo,发票校验miro). 演示背景(假设以下都为本位币交易): 库存(单价 ...