最近学习了如何使用View与SurfaceView制作简单的手绘板,在此做个小结。

自定义VIew实现手绘板:

首先是使用View来实现手绘板:

package com.app.superxlcr.mydrawboard.myView;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.view.MotionEvent;
import android.view.View;/*** Created by Superxlcr* On 2016/3/18* 普通画板View*/
public class NormalDrawBoardView extends View {// 点击的坐标private float lastX = 0, lastY = 0;private Path path;private Paint paint;// 使用内存中的图片作为缓冲区private Bitmap cacheBitmap;// 缓冲区上的Canvas对象private Canvas cacheCanvas;public NormalDrawBoardView(Context context, int width, int height) {super(context);// 创建确定大小的bitmapcacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 初始化缓存画布,把bitmap内容画到缓存画布上cacheCanvas = new Canvas();cacheCanvas.setBitmap(cacheBitmap);path = new Path();// 初始化画笔// 防抖动paint = new Paint(Paint.DITHER_FLAG);paint.setColor(Color.BLACK);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(1);// 反锯齿paint.setAntiAlias(true);paint.setDither(true);}/*** 设置画笔的颜色* @param color,颜色的字符串*/public void setPaintColor(String color) {switch (color) {case "red" :paint.setColor(Color.RED);break;case "green" :paint.setColor(Color.GREEN);break;case "blue" :paint.setColor(Color.BLUE);break;case "yellow" :paint.setColor(Color.YELLOW);break;default:paint.setColor(Color.BLACK);break;}}/*** 设置画笔粗细* @param width*/public void setPaintWidth(int width) {paint.setStrokeWidth(width);}public void clearCanvas() {// 清除path轨迹path.reset();path.moveTo(lastX, lastY);// 清除cacheCanvas图像Paint paint = new Paint();paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));cacheCanvas.drawPaint(paint);invalidate();
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));}@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX(), y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN: // 记录点击坐标,把当前点定义为线段的前一个点lastX = x;lastY = y;path.moveTo(x, y);break;case MotionEvent.ACTION_MOVE: // 绘制线段path.quadTo(lastX, lastY, x, y);lastX = x;lastY = y;break;case MotionEvent.ACTION_UP: // 把线段绘制到画布上cacheCanvas.drawPath(path, paint);path.reset();break;}invalidate();return true;}@Overrideprotected void onDraw(Canvas canvas) {Paint bmpPaint = new Paint();// 绘制之前画的轨迹canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint);// 绘制正在画的轨迹canvas.drawPath(path, paint);}
}

在上面的代码中我们首先初始化了以下变量:

  • path:用于记录用户手指滑动的路径类
  • paint:画笔类
  • cacheBitmap:用于存储用户画的手绘的图片缓存
  • cacheCanvas:用于描绘用户画的手绘的画布缓存
  • lastX,lastY:记录上一个点击的点的坐标
然后我们重写了onTouchEvent方法来处理用户的手指点击事件:
  • Motion.Action.DOWN:代表手指按下的事件,此时我们记录下手指点击坐标,并把path的起点设置为该坐标
  • Motion.Action.MOVE:代表手指拖动事件,此时我们根据获取的坐标与前一个点的坐标画出一条线段,并更新记录的坐标
  • Motion.Action.UP:代表手指抬起事件,此时我们把path记录的路径绘制到缓存中,并重置path
在每次触发onTouchEvent方法的时候我们都在最后调用invalidate方法触发我们的View调用onDraw进行重绘。
我们在onDraw方法中先把bitmap缓存的手绘记录绘制到画布上,再把当前的路径也绘制到画布上。
此时我们自定义View实现的手绘板就大功告成啦:

SurfaceView实现手绘板:

接下来我们来谈谈使用SurfaceView实现手绘板:
package com.app.superxlcr.mydrawboard.myView;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;import java.util.concurrent.TimeUnit;/*** Created by Superxlcr* On 2016/3/19*/
public class SurfaceViewDrawBoardView extends SurfaceViewimplements SurfaceHolder.Callback {private final String TAG = "SurfaceViewDrawBoard";// 绘制背景的线程private MyThread myThread;// 缓存用的bitmap和canvasprivate Bitmap cacheBitmap;private Canvas cacheCanvas;// 画笔和路径private Paint paint;private Path path;// 上一个点的坐标private float lastX, lastY;public SurfaceViewDrawBoardView(Context context, int width, int height) {super(context);// 设置bitmap和canvascacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);cacheCanvas = new Canvas();cacheCanvas.setBitmap(cacheBitmap);// 设置画笔paint = new Paint(Paint.DITHER_FLAG);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(1);paint.setColor(Color.BLACK);paint.setAntiAlias(true);paint.setDither(true);// 初始化路径path = new Path();// 设置SurfaceHolder的回调函数getHolder().addCallback(this);// 初始化绘画线程myThread = new MyThread(getHolder());Log.v(TAG, Thread.currentThread().getName());}/*** 设置画笔的颜色** @param color,颜色的字符串*/public void setPaintColor(String color) {switch (color) {case "red":paint.setColor(Color.RED);break;case "green":paint.setColor(Color.GREEN);break;case "blue":paint.setColor(Color.BLUE);break;case "yellow":paint.setColor(Color.YELLOW);break;default:paint.setColor(Color.BLACK);break;}}/*** 设置画笔粗细** @param width*/public void setPaintWidth(int width) {paint.setStrokeWidth(width);}public void clearCanvas() {// 清除path轨迹path.reset();path.moveTo(lastX, lastY);// 清除cacheCanvas图像Paint paint = new Paint();paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));cacheCanvas.drawPaint(paint);//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));}@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX(), y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN: // 记录点击坐标,把当前点定义为线段的前一个点lastX = x;lastY = y;path.moveTo(x, y);break;case MotionEvent.ACTION_MOVE: // 绘制线段path.quadTo(lastX, lastY, x, y);lastX = x;lastY = y;break;case MotionEvent.ACTION_UP: // 把线段绘制到画布上cacheCanvas.drawPath(path, paint);path.reset();break;}return true;}@Overridepublic void surfaceCreated(final SurfaceHolder holder) {Log.v(TAG, "surfaceCreated");myThread.isRun = true;myThread.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {Log.v(TAG, "surfaceChanged");}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {Log.v(TAG, "surfaceDestoryed");myThread.isRun = false;}class MyThread extends Thread {private SurfaceHolder holder;public boolean isRun = false;private int red = 0, green = 0, blue = 0;private int colorValue = 0;private float hsbValue[];MyThread(SurfaceHolder holder) {this.holder = holder;hsbValue = new float[]{0, 1, 1};}@Overridepublic void run() {while (isRun) {Log.v(TAG, Thread.currentThread().getName());Canvas canvas = holder.lockCanvas();// 背景色渐变hsbValue[0] = hsbValue[0] + 1 <= 360 ? hsbValue[0] + 1 : 0;if (canvas != null) {// 绘制背景色canvas.drawColor(Color.HSVToColor(hsbValue));Paint bmpPaint = new Paint();// 绘制之前画的轨迹canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint);// 绘制正在画的轨迹canvas.drawPath(path, paint);holder.unlockCanvasAndPost(canvas);}try {// 休眠20msTimeUnit.MILLISECONDS.sleep(20);} catch (Exception e) {e.printStackTrace();}}}}
}

surfaceView允许我们在新开的线程中更新UI界面,我们首先实现了SurfaceHolder.Callback接口,该接口有以下几个重要方法:

  • surfaceCreated:在Surface创建的时候调用,我们在该方法中开启了绘制UI的子线程
  • surfaceChanged:在Surface大小发生改变时调用
  • surfaceDestroyed:在Surface销毁时调用,我们在该方法中停止了绘制UI的子线程
实现了SurfaceHolder.Callback接口后,我们在初始化时使用getHolder方法可以获取SurfaceHolder,然后调用其addCallback方法加入实现的接口即可。
代码有大部分都与自定义View类似,此处我们重点讲讲子线程执行的工作。
由于我们使用的是SurfaceView,因此重写其onDraw方法并不能在界面上绘制出图像,正确的方法是调用SurfaceHolder的lockCanvas方法获取画布(有可能为null),然后绘制完成后调用unlockCanvasAndPost把画布给更新到屏幕上,所以我们就在子线程中每过20ms就调用一次以上方法来刷新我们的界面,此处本人还加入了背景变色功能:利用颜色的HSV属性,色调从0~360变化,亮度和对比度恒定为1。
最终效果如下图:
实际上的手绘效果感觉没有自定义View来的好,有时候感觉会有卡顿现象出现,本人分析的原因为子线程绘制界面的间隔时间不够短的缘故。
经过本次自定义View和SurfaceView手绘板的实战,本人有如下体会:
  1. View适合实现被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
  2. 而主动更新的画面应该使用SurfaceView。比如背景在一直变色。这就需要一个单独的thread不停的重绘背景的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。
有兴趣的童鞋可以到github上下载我的项目: https://github.com/superxlcr/MyDrawBoard

Android View与SurfaceView的手绘板制作相关推荐

  1. 手绘板的制作——画布缩放(4)

    前言 在这一篇中,我们讲解下画布的缩放,也就是做一个根据手势缩放进行画布缩放的功能. 我们先来梳理下逻辑: 监听手势,当为一根手指的时候,就延续之前的操作,执行手绘操作,当操作为两根手指的时候,则执行 ...

  2. 手绘板的制作——画布保存(6)

    「手绘板的制作--手绘(1)」 「手绘板的制作--重置与橡皮擦(2)」 「手绘板的制作--命令模式与撤销.重制(3)」 「手绘板的制作--画布缩放(4)」 「手绘板的制作--画布移动(5)」 前言 经 ...

  3. 安卓开发-手绘板自定义绘画的保存,清空与恢复

    写这篇文章是因为代码中刚实现过这些功能,害怕自己之后会忘记,所以把整个方法写出来,方便自己日后复习用. 还是老样子,先上图: 1.首页 2.点击手绘板图片后跳出的窗口 3.用手指进行绘制 4.点击保存 ...

  4. java画图颜色_手绘板,多种颜色选择。我抄的《疯狂java讲义》的,包我乱导的,但代码能用。...

    [java]代码库import javax.swing.*; import java.awt.image.*; import java.awt.datatransfer.*; import javax ...

  5. 什么是数位板? 数位板,又名绘图板、绘画板、手绘板等等,是计算机输入设备的一种,通常是由一块板子和一支压感笔组成,它和手写板等作为非常规的输入产品相类似,都针对一定的使用群体。 与手写板所不同的是

    什么是数位板? 数位板,又名绘图板.绘画板.手绘板等等,是计算机输入设备的一种,通常是由一块板子和一支压感笔组成,它和手写板等作为非常规的输入产品相类似,都针对一定的使用群体. 与手写板所不同的是,数 ...

  6. 解决win10下Photoshop2018CC手绘板画画变上下

    有次用手绘板画画的时候, 手绘板的画笔变成上下了, 不能正常画画. 笔飘来飘去. 后来知道是因为win10系统更新导致, 那么把最近的更新卸载掉就可以了. 在系统设置里找到"更新和安全&qu ...

  7. Photoshop---Wacom手绘板绘画画变成了拖动,根本不能画画

    前述 以下步骤都是基于windows10上的结果,而且的确是解决了我的问题,windows7没有试过,估计差不多,分享出来希望能帮到众位仙家. 背景&问题 近段时间photoshop手绘的时候 ...

  8. wacom怎么调压感_怎样在ps里用手绘板画出有压感的效果

    展开全部 一  数位板没有32313133353236313431303231363533e4b893e5b19e31333433616331压感的解决设置方法. 我们在使用数位板绘画的过程中经常会遇 ...

  9. Win10手绘板无压感故障解决方法

    Win10手绘板无压感故障解决方法 参考文章: (1)Win10手绘板无压感故障解决方法 (2)https://www.cnblogs.com/huangzhewei/p/11053214.html ...

最新文章

  1. InfoQ趋势报告:架构和设计领域技术演变详解
  2. MPTCP 源码分析(五) 接收端窗口值
  3. 【ActiveMQ】消息生产者自动注入报错:Could not autowire. No beans of 'JmsMessagingTemplate' type found...
  4. 如果记录没有跟得上创造和学习
  5. Spring框架版本命名规则
  6. MATLAB 牛顿迭代算法
  7. 第十一届蓝桥杯校园赛---原题+解析+答案
  8. 爬取外网数据(twitter、facebook)-易数云可视化爬虫软件
  9. 3dmax渲染卡顿崩溃怎么办?(一)
  10. 智慧城市顶层设计与不确定性
  11. 放大电路中反馈及类型的判断
  12. skype 无法连接
  13. webpack4.0关闭开发环境的代码压缩UglifyJsPlugin
  14. 我的CSDN之旅:2020年终总结
  15. 曹操梦中杀人应该是可信的
  16. 宝塔面板隐藏网站服务器真实IP
  17. 微信小程序(数据可视化、Canvas、绘制线段、图形、太极图、文本、图像、渐变、变形)
  18. 【Redis】基础篇
  19. 算法训练Day49 | Leetcode121. 买卖股票的最佳时机(只能买卖一次);LeetCode122. 买卖股票的最佳时机II(可以买卖多次)
  20. 苹果CEO史蒂夫·乔布斯在斯坦福演讲(一)

热门文章

  1. 20172266遥感一班李安娜第一次笔记
  2. Rancher学习日记2
  3. 02-04 控制AutoCAD环境(四) 锁定和解锁文档
  4. FileInputStream文件字节输入流
  5. java 多线程。 编写10个线程,第一个线程从1加到10,第二个线程第11加到20,。。。第10个线程从91加到100.最够把10个线程结果相加
  6. JMeter安装以及使用以及断言
  7. OpenCV玩九宫格数独(二):knn数字识别
  8. 命令行修改host文件
  9. win7桌面计算机怎么调出来,电脑系统教程:win7系统怎么打开控制面板
  10. js将北京时间转换为当前时区的时间