Android简单涂鸦以及撤销、重做的实现方法
前段时间研究了下涂鸦功能的实现,其实单独的涂鸦实现起来还是挺简单的,关键的技术难点是撤销与重做功能的实现。但是这里暂时只说明下涂鸦功能的实现,高手勿喷哈,而且该功能在Android SDK提供的APIDemo当中就有的,但是如果能够将该地方的知识点搞懂的话,我认为View画图基本上是难不倒你了,特别是里面为什么要用一个中间的Bitmap。老规矩,还是先看看效果图吧:
代码如下:
- package cn.ych.tuya;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.Path;
- import android.graphics.Bitmap.CompressFormat;
- import android.os.Environment;
- import android.view.MotionEvent;
- import android.view.View;
- /**
- *
- * @category: View实现涂鸦、撤销以及重做功能
- * @author: 锋翼
- * @link: www.apkstory.com
- * @date: 2012.1.4
- *
- */
- public class TuyaView extends View {
- private Bitmap mBitmap;
- private Canvas mCanvas;
- private Path mPath;
- private Paint mBitmapPaint;// 画布的画笔
- private Paint mPaint;// 真实的画笔
- private float mX, mY;//临时点坐标
- private static final float TOUCH_TOLERANCE = 4;
- private int screenWidth, screenHeight;// 屏幕長寬
- public TuyaView(Context context, int w, int h) {
- super(context);
- screenWidth = w;
- screenHeight = h;
- mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
- Bitmap.Config.ARGB_8888);
- // 保存一次一次绘制出来的图形
- mCanvas = new Canvas(mBitmap);
- mBitmapPaint = new Paint(Paint.DITHER_FLAG);
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘
- mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形状
- mPaint.setStrokeWidth(5);// 画笔宽度
- }
- @Override
- public void onDraw(Canvas canvas) {
- canvas.drawColor(0xFFAAAAAA);
- // 将前面已经画过得显示出来
- canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
- if (mPath != null) {
- // 实时的显示
- canvas.drawPath(mPath, mPaint);
- }
- }
- private void touch_start(float x, float y) {
- 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(mY - y);
- 触摸间隔大于阈值才绘制路径
- if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
- // 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)
- mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
- mX = x;
- mY = y;
- }
- }
- private void touch_up() {
- mPath.lineTo(mX, mY);
- mCanvas.drawPath(mPath, mPaint);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float x = event.getX();
- float y = event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // 每次down下去重新new一个Path
- mPath = new Path();
- 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;
- }
- }
复制代码
上一讲当中,已经讲解了普通View实现涂鸦的功能,现在再来给涂鸦添加上撤销与重做的功能吧。撤销与重做在很多地方都是很重要的功能,比如PS里面、Word里面等等,而且大部分童鞋都能够想到要实现该功能应该需要用到堆栈,对于一些大牛的话可能就直接想到设计模式上面去了,比如命令模式就可以解决撤销与重做的问题。我们这里要讲解的是利用集合来完成该功能,其实也就是模拟栈,我相信你懂得。
老规矩,先上效果图:
代码如下:
- package cn.ych.tuya;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.Path;
- import android.graphics.Bitmap.CompressFormat;
- import android.os.Environment;
- import android.view.MotionEvent;
- import android.view.View;
- /**
- *
- * @category: View实现涂鸦、撤销以及重做功能
- * @author: 锋翼
- * @link: www.apkstory.com
- * @date: 2012.1.4
- *
- */
- public class TuyaView extends View {
- private Bitmap mBitmap;
- private Canvas mCanvas;
- private Path mPath;
- private Paint mBitmapPaint;// 画布的画笔
- private Paint mPaint;// 真实的画笔
- private float mX, mY;// 临时点坐标
- private static final float TOUCH_TOLERANCE = 4;
- // 保存Path路径的集合,用List集合来模拟栈
- private static List<DrawPath> savePath;
- // 记录Path路径的对象
- private DrawPath dp;
- private int screenWidth, screenHeight;// 屏幕長寬
- private class DrawPath {
- public Path path;// 路径
- public Paint paint;// 画笔
- }
- public TuyaView(Context context, int w, int h) {
- super(context);
- screenWidth = w;
- screenHeight = h;
- mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
- Bitmap.Config.ARGB_8888);
- // 保存一次一次绘制出来的图形
- mCanvas = new Canvas(mBitmap);
- mBitmapPaint = new Paint(Paint.DITHER_FLAG);
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘
- mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形状
- mPaint.setStrokeWidth(5);// 画笔宽度
- savePath = new ArrayList<DrawPath>();
- }
- @Override
- public void onDraw(Canvas canvas) {
- canvas.drawColor(0xFFAAAAAA);
- // 将前面已经画过得显示出来
- canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
- if (mPath != null) {
- // 实时的显示
- canvas.drawPath(mPath, mPaint);
- }
- }
- private void touch_start(float x, float y) {
- 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(mY - y);
- if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
- // 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)
- mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
- mX = x;
- mY = y;
- }
- }
- private void touch_up() {
- mPath.lineTo(mX, mY);
- mCanvas.drawPath(mPath, mPaint);
- //将一条完整的路径保存下来(相当于入栈操作)
- savePath.add(dp);
- mPath = null;// 重新置空
- }
- /**
- * 撤销的核心思想就是将画布清空,
- * 将保存下来的Path路径最后一个移除掉,
- * 重新将路径画在画布上面。
- */
- public void undo() {
- mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
- Bitmap.Config.ARGB_8888);
- mCanvas.setBitmap(mBitmap);// 重新设置画布,相当于清空画布
- // 清空画布,但是如果图片有背景的话,则使用上面的重新初始化的方法,用该方法会将背景清空掉...
- if (savePath != null && savePath.size() > 0) {
- // 移除最后一个path,相当于出栈操作
- savePath.remove(savePath.size() - 1);
- Iterator<DrawPath> iter = savePath.iterator();
- while (iter.hasNext()) {
- DrawPath drawPath = iter.next();
- mCanvas.drawPath(drawPath.path, drawPath.paint);
- }
- invalidate();// 刷新
- /*在这里保存图片纯粹是为了方便,保存图片进行验证*/
- String fileUrl = Environment.getExternalStorageDirectory()
- .toString() + "/android/data/test.png";
- try {
- FileOutputStream fos = new FileOutputStream(new File(fileUrl));
- mBitmap.compress(CompressFormat.PNG, 100, fos);
- fos.flush();
- fos.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 重做的核心思想就是将撤销的路径保存到另外一个集合里面(栈),
- * 然后从redo的集合里面取出最顶端对象,
- * 画在画布上面即可。
- */
- public void redo(){
- //如果撤销你懂了的话,那就试试重做吧。
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float x = event.getX();
- float y = event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // 每次down下去重新new一个Path
- mPath = new Path();
- //每一次记录的路径对象是不一样的
- dp = new DrawPath();
- dp.path = mPath;
- dp.paint = mPaint;
- 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;
- }
- }
复制代码
Android简单涂鸦以及撤销、重做的实现方法相关推荐
- android 自定义画笔,在Android Canvas中使用撤销/重做操作的自定义画笔
我想使用自定义画笔&撤消/重做操作实现画布绘制应用程序.首先,我的代码完美工作,无需使用自定义画笔(包括撤消/重做操作).根据这个答案How to make custom brush for ...
- android 简单涂鸦
部分代码借鉴网上. 看图先: 主界面Activity : package com.mj.candraw; import android.app.Activity; import android.os. ...
- C# wpf 实现自定义撤销重做功能
文章目录 前言 一.具体实现 1.完整代码 二.使用示例 1.拖动控件 (1)MainWindow.xaml (2)MainWindow.xaml.cs (3)效果预览 总结 前言 做wpf界面的时候 ...
- 实现编辑功能有哪几个action_Web 应用的撤销重做实现
背景 前不久,我参与开发了团队中的一个 web 应用,其中的一个页面操作如下图所示: GIF 这个制作间页面有着类似 PPT 的交互:从左侧的工具栏中选择元素放入中间的画布.在画布中可以删除.操作(拖 ...
- Mac OS开发—Xcode给Mac应用添加编辑快捷键(剪切 复制 粘贴 全选 删除 撤销 重做)功能
前言 笔者做Windows开发的,无奈公司有Mac OS的项目需要维护,之前没有编辑菜单的功能,需要笔者加上这个功能,笔者只能硬着头皮上.还好会c++,Mac应用里面一般都会用object-c 也可以 ...
- Android简单适配9.0~12.0
适配6.0~8.0的可以看下这个: 适配Android6.0到8.0详细过程--小白教程 注意:10.0到12.0的都是些网上看到的资料,我就适配到10.0的文件存储,如有不对的,可以留言,我会查阅修 ...
- 游戏开发中如何设计一个撤销重做系统DoUnDo
我现在负责做捏脸的模块,其中需要做撤销重做,老实说上一次做还是大学的时候做绘图程序有用到过,那个时候就是简单用了一个栈来操作.当时的一个核心思想是不管你绘制的是矩形.三角形.还是什么形状,底层他们都有 ...
- C++ 实现自定义撤销重做功能
文章目录 前言 一.完整代码 二.使用示例 1.基本用法 2.gdi画线撤销 总结 前言 在使用c++做界面开发的时候,需要涉及到到撤销重做操作,尤其是实现白板功能时需要自己实现一套撤销重做功能,如果 ...
- android os开机画面,Android简单实现启动画面的方法
本文实例讲述了Android简单实现启动画面的方法.分享给大家供大家参考,具体如下: 核心代码: package com.demo.app; import android.app.Activity; ...
最新文章
- 收藏 | 多目标跟踪(MOT)入门
- 大数据查询——HBase读写设计与实践--转
- AI:2020年6月22日北京智源大会演讲分享之认知神经基础专题论坛——13:40-14:20毕彦超教授《Knowledge representation in the Human brain》
- udp重发机制_UDP 协议
- 《Go学习笔记 . 雨痕》方法
- 2018-2019-2 20165118 《网络对抗技术》Exp4 恶意代码分析
- EMNLP'21 | Dialogue System 对话系统
- qmessagebox 设置显示屏幕中间_windows7屏幕分辨率设置
- 沉浮二十载,奥飞成也萧何败也萧何?
- matlab线性规划求解函数:linprog
- 【IAST安全左移最佳工具】
- 重装系统win10安装教程(超详细)
- CTF_RSA_N不互素
- CAD文字显示号问题原因和解决办法
- 阿里企业邮箱服务器配置
- 离群值是什么意思_离群值!离群值?离群值!
- Cesium 系列1 - 简介及源码下载发布
- TTS(TextToSpeach)语音朗读实例
- 2022最新软件测试面试题(含答案)
- java绘图技术,演示绘制不同的图形