Android画板开发(一) 基本画笔的实现
Android画板开发(二) 橡皮擦实现
Android画板开发(三) 撤销反撤销功能实现
Android画板开发(四) 添加背景和保存画板内容为图片
Android画板开发(五) 添加文本文字

在上一篇实现了简单的画板功能, 这篇实现橡皮擦功能,首先分析一下应该如何实现:

在Andriod有个图像混合(Xfermode)概念,利用这个概念我们就可以实现橡皮擦功能。

一、Xfermode

Paint有一个方法setXfermode(Xfermode),这个方法设置图像的混合模式。参数有三个子类:

  • AvoidXfermode
  • PixelXorXfermode
  • PorterDuffXfermode

前面两个因为不支持硬件加速在API 16已经已经过时弃用了。 简单讲一下第三个。

1.1 PorterDuffXfermode

该类有且只有一个含参的构造方法PorterDuffXfermode(PorterDuff.Mode mode),参数就是设置图像的混合模式,下面这张图片形象地说明了各种模式的作用

我们的做橡皮擦的时候,就是用到了PorterDuff.Mode.CLEAR这个模式清除图像,所以说橡皮擦也是Path,只是绘制的模式不一样了。

二、实现

在上一篇的文章中,实现了最简单笔画画板,就是只有一个画笔模式,所以首先添加一个橡皮擦的绘制模式。

    companion object {const val EDIT_MODE_PEN = 0x1L       //画笔模式const val EDIT_MODE_ERASER = 0x2L    //橡皮擦模式}@Retention(AnnotationRetention.SOURCE)@IntDef(EDIT_MODE_PEN, EDIT_MODE_ERASER)annotation class EditMode//当前编辑模式默认为画笔模式@EditModeprivate var mMode: Long = EDIT_MODE_PEN/*** 设置画笔模式*/fun setModel(@EditMode model:Long){mMode = modelwhen(model){EDIT_MODE_PEN -> {//画线mPaint.xfermode = null}EDIT_MODE_ERASER ->{mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)}}}

然后捋一下整个流程:

  • 画笔模式,在onTouch时候画出Path,绘制到view上
  • 然后切换到橡皮擦模式,画出Path,clear擦掉原来的内容
  • 再来回切换绘制

现在重点是解决第2点,一个Path怎么做到不改变原来的path基础上换个绘制模式继续画呢?

如果你考虑第2点的话,效果是这样子的:

What the fuck?(黑人问号) 这什么情况? 其实是因为path只有一条,一直没改变。所以,引入缓存Canvas和缓存Bitmap,添加两个变量:

    //想要绘制的内容先绘制到这个增加的canvas对应的bitmap上,// 写完后再把这个bitmap的ARGB信息一次提交给上下文的canvas去绘制private lateinit var mBufferBitmap: Bitmapprivate lateinit var mBufferCanvas: Canvas

然后在onMeasure中进行初始化:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)if(mBufferCanvas == null){mBufferBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888)//canvas绘制的内容,将会在这个mBufferBitmap内mBufferCanvas = Canvas(mBufferBitmap)}}

然后在onTouchEvent方面里面手指移动的时候,我们在缓存Canvas里面进行绘制path:

MotionEvent.ACTION_MOVE -> {  //手指移动的时候//绘制圆滑曲线,即贝塞尔曲线,贝塞尔曲线这个知识自行了解mPath.quadTo(preX,preY,event.x,event.y)//在缓存里面绘制mBufferCanvas.drawPath(mPath,mPaint)//重新绘制,会调用onDraw方法invalidate()preX = event.xpreY = event.y}

然后onDraw的时候,就把缓存的Canvas的bitmap当前view的Canvas:

    override fun onDraw(canvas: Canvas) {super.onDraw(canvas)//画出缓存bitmap的内容canvas.drawBitmap(mBufferBitmap,0f,0f,null)}

就可以了,看看完整的代码100多行:

class TPEraserView(context: Context, attr: AttributeSet) : View(context,attr) {companion object {const val EDIT_MODE_PEN = 0x1L       //画笔模式const val EDIT_MODE_ERASER = 0x2L    //橡皮擦模式}@Retention(AnnotationRetention.SOURCE)@IntDef(EDIT_MODE_PEN, EDIT_MODE_ERASER)annotation class EditMode//当前编辑模式默认为画笔模式@EditModeprivate var mMode: Long = EDIT_MODE_PENprivate var preX: Float = 0.0f //上一次的触摸点x坐标private var preY: Float = 0.0f //上一次触摸点y坐标private var mPath = Path()   //path路径//画笔private var mPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)//想要绘制的内容先绘制到这个增加的canvas对应的bitmap上,// 写完后再把这个bitmap的ARGB信息一次提交给上下文的canvas去绘制private lateinit var mBufferBitmap: Bitmapprivate lateinit var mBufferCanvas: Canvasinit {mPaint.style = Paint.Style.STROKE //画笔为实心mPaint.color = Color.RED         //颜色mPaint.strokeCap = Paint.Cap.ROUND //笔触为圆形mPaint.strokeWidth = 10f            //画笔大小}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int){super.onMeasure(widthMeasureSpec, heightMeasureSpec)mBufferBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888)//canvas绘制的内容,将会在这个mBufferBitmap内mBufferCanvas = Canvas(mBufferBitmap)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)//画出缓存bitmap的内容canvas.drawBitmap(mBufferBitmap,0f,0f,null)}override fun onTouchEvent(event: MotionEvent): Boolean {when(event.action){MotionEvent.ACTION_DOWN -> {  //手指按下的时候//将起始点移动到当前坐标mPath.moveTo(event.x,event.y)//记录上次触摸的坐标,注意ACTION_DOWN方法只会执行一次preX = event.xpreY = event.y}MotionEvent.ACTION_MOVE -> {  //手指移动的时候//绘制圆滑曲线,即贝塞尔曲线,贝塞尔曲线这个知识自行了解mPath.quadTo(preX,preY,event.x,event.y)//在缓存里面绘制mBufferCanvas.drawPath(mPath,mPaint)//重新绘制,会调用onDraw方法invalidate()preX = event.xpreY = event.y}MotionEvent.ACTION_UP ->{//清除路径的内容mPath.reset()}}// true:告诉系统,这个触摸事件由我来处理// false:告诉系统,这个触摸事件我不处理,这时系统会把触摸事件传递给imageview的父节点return true}/*** 设置画笔模式*/fun setModel(@EditMode model:Long){mMode = modelwhen(model){EDIT_MODE_PEN -> {mPaint.xfermode = null}EDIT_MODE_ERASER ->{mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)}}}}

效果:

三、清空画布实现

添加一个方法,按照上面的套路,把缓存canvas绘制清除即可。

    /*** 清空画布*/fun clear() {mBufferCanvas.drawColor(0, PorterDuff.Mode.CLEAR)invalidate()}

Android画板开发(二) 橡皮擦实现相关推荐

  1. Android画板开发(四) 添加背景和保存画板内容为图片

    Android画板开发(一) 基本画笔的实现 Android画板开发(二) 橡皮擦实现 Android画板开发(三) 撤销反撤销功能实现 Android画板开发(四) 添加背景和保存画板内容为图片 A ...

  2. Android画板开发(一) 基本画笔的实现

    Android画板开发(一) 基本画笔的实现 Android画板开发(二) 橡皮擦实现 Android画板开发(三) 撤销反撤销功能实现 Android画板开发(四) 添加背景和保存画板内容为图片 A ...

  3. 【Android游戏开发二十七】讲解游戏开发与项目下的hdpi 、mdpi与ldpi资源文件夹以及游戏高清版本的设置...

    今天一个开发者问到我为什么游戏开发要删除项目下的hdpi.mdpi和ldpi文件夹:下面详细给大家解答一下: 首先童鞋们如果看过我写的<[Android游戏开发二十一]Android os设备谎 ...

  4. Android 蓝牙开发(二) --手机与蓝牙音箱配对,并播放音频

    Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 上一章中,我们已经学习了传统蓝牙的开发,这一章,我们来学习如 ...

  5. 【Android游戏开发二十二】(图文详解)游戏中灵活实现动画播放!简述J2me的游戏类库与Android游戏开发!

    本站文章均为 李华明Himi 原创,转载务必在明显处注明:(作者新浪微博: @李华明Himi ) 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/andr ...

  6. CSDN Android客户端开发(二):详解如何基于Java用Jsoup爬虫HTML数据

    本文参考鸿洋大大的链接详细介绍如何使用Jsoup包抓取HTML数据,是一个纯java工程,并将其打包成jar包.希望了解如何用java语言爬虫网页的可以看下. 杂家前文就又介绍用HTTP访问百度主页得 ...

  7. 【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/374.html L ...

  8. 【Android游戏开发二十五】在Android上的使用《贝赛尔曲线》!

    首先对于<赛贝尔曲线>不是很了解的童鞋,请自觉白度百科.google等等... 为了方便偷懒的童鞋,这里给个<贝赛尔曲线>百科地址,以及一段话简述<贝赛尔曲线>: ...

  9. Android添加拍照功能,Android相机开发(二): 给相机加上偏好设置

    Android Camera Develop: add settings to camera app 概述 继上一篇实现了一个最简单的相机APP后,本篇主要介绍实现相机的各种偏好设置,比如分辨率.闪光 ...

最新文章

  1. JavaScript 的使用简单总结
  2. 汽车的燃油清洁剂有什么用处?
  3. WD强势出击 推出全球业界首款2TB硬盘
  4. css餐厅_餐厅的评分预测
  5. LeetCode 563二叉树的坡度-简单
  6. BugkuCTF-MISC题啊哒,白哥的鸽子
  7. 三天学好ADO(转)
  8. ProtoBuf3语法指南(Protocol Buffers)_下
  9. 学习ARM的一些基本知识,个人整理
  10. excel服务器2010网站,勤哲Excel服务器2010企业版(完整安装包)
  11. 基于libmodbus库实现modbus TCP/RTU通信
  12. 【网络安全基础】数字签名原理及应用
  13. C语言float去尾法输出,c语言去尾法怎么写
  14. 威刚SU600固态硬盘不识别通电无反应慧荣SM系列主控数据恢复![图]
  15. 杂感-ThinkPad E570
  16. 2019年架构软考论文押题(二)
  17. 基于大数据的软件项目知识图谱构造及问答方法
  18. 调试 GPS 1.575G干扰无法搜星问题
  19. 西安电子科技大学计算机专硕调剂,西安电子科技大学人工智能学院2020研究生调剂通知...
  20. [ZZ]Windows磁盘驱动基础教程

热门文章

  1. 搜索词与关键词报告区别
  2. 实现生产者消费者的三种方式
  3. 对seed()的个人理解
  4. c语言编程输出我的学号名字,如何用C语言编写自己的姓名和学号
  5. 最小绝对偏差(LAD)
  6. 游戏制作之路(54)自制天空盒
  7. java启动参数xmm_JVM所有参数一览
  8. QVGA、WVGA、VGA、WQVGA、SQVGA等几种手机分辨率扫盲!
  9. 马尔受伤后,巴西媒体透露斯科拉里在训练中演练了三后腰阵型
  10. 泰拉瑞亚服务器修改物品,泰拉瑞亚1.4自定义物品名称方法 自定义物品名称详细教程_逗游网...