Android画板开发之撤销反撤销功能
程序员文章站
2022-08-20 17:14:12
一、分析
这篇将会讲解撤销反撤销功能的实现,先讨论一下这个原理是怎么样实现的。
每次撤回的内容,内容是怎么定义呢? 其实就是每一笔,每一笔作为撤回的内容,那每一笔怎...
一、分析
这篇将会讲解撤销反撤销功能的实现,先讨论一下这个原理是怎么样实现的。
每次撤回的内容,内容是怎么定义呢? 其实就是每一笔,每一笔作为撤回的内容,那每一笔怎么算呢,就是算手指从按下-移动-放开这一个过程就是一笔。
我们只需记录这个过程为一笔,然后用一个已画列表list列表来记录这个过程的paint画笔和路径path。
撤销的时候就把后面的一个数据移到另一个撤销列表
反撤销的时候,就把撤销列表的最后面那条数据移动到已画列表。
然后,还有一个重点,就是画笔的保存数量,上面说记录每一笔画笔,这当然是有个限度,不可能画了好几百笔都记录下来,这样子内存消耗很大的,所以超出显示画笔数量的时候,我们就把以前的画死在画板上。
基本原理是这样子的。接下来跟着我实现
二、实现
如何实现撤回功能
2.1 定义数据类
首先,需要一个bean类存储每一笔的数据,这里定义一个paintdata,里面需要定义个draw方法,因为撤销的时候,需要重新绘制。
data class paintdata( var mpaint: paint, //保存画笔 var mpath: path //保存路径 ) { /** * 撤销和反撤销之后 重新绘制 * @param canvas 绘制的画布 */ fun draw(canvas: canvas){ canvas.drawpath(mpath,mpaint) } }
2.2 修改清空画板方法
因为多了列表,所以清空画板的方法需要把列表也清除了
/** * 清空画布 * @param isclearlist 时候清空数据列表 */ fun clear(isclearlist:boolean) { if(isclearlist){ mrevokedlist.clear() mpaintedlist.clear() } mbuffercanvas.drawcolor(0, porterduff.mode.clear) invalidate() }
2.3 实现撤销方法
在view定义两个列表,一个是已经画的内容列表,一个是撤销内容的列表
//储存已经写的笔画 private var mpaintedlist: mutablelist<paintdata> = arraylist<paintdata>() //已经撤销的列表 private var mrevokedlist: mutablelist<paintdata> = arraylist<paintdata>()
添加固话层canvas和bitmap,超出记录的画笔就写死在固化层了
//固化层,超出最大笔画就先绘制到这个层 private lateinit var mholdbitmap: bitmap private lateinit var mholdcanvas: canvas //最多记录20画笔迹 private val max_paint_recored = 20 //在测量的时候进行初始化: override fun onmeasure(widthmeasurespec: int, heightmeasurespec: int) { if(mbuffercanvas == null){ mbufferbitmap = bitmap.createbitmap(measuredwidth, measuredheight, bitmap.config.argb_8888) //canvas绘制的内容,将会在这个mbufferbitmap内 mbuffercanvas = canvas(mbufferbitmap) } if(mholdcanvas == null){ mholdbitmap = bitmap.createbitmap(measuredwidth, measuredheight, bitmap.config.argb_8888) mholdcanvas = canvas(mholdbitmap) } }
然后定义撤回的方法
/** * 撤回一个笔迹 * @return 是否撤回成功 */ fun revoked(){ redraw(mpaintedlist) }
反撤销方法基本一致,只是改了个列表:
/** * 反撤回一个笔迹 */ fun unrevoked(){ redraw(mrevokedlist) }
然后重新绘制的方法为:
/** * 重新绘制 * @param paintlist 需要操作的list */ private fun redraw(paintlist:mutablelist<paintdata>){ if(paintlist.size > 0){ val paint = paintlist.removeat(paintlist.size-1) if(paintlist === mpaintedlist){ mrevokedlist.add(paint) }else{ mpaintedlist.add(paint) } //清空缓存画板 mbuffercanvas.drawcolor(0, porterduff.mode.clear) invalidate() } }
然后就是画笔的保存,在触摸按下的时候,进行画笔的保存
override fun ontouchevent(event: motionevent): boolean { when(event.action){ motionevent.action_down -> { //手指按下的时候 //将起始点移动到当前坐标 mpath.moveto(event.x,event.y) //记录上次触摸的坐标,注意action_down方法只会执行一次 prex = event.x prey = event.y //保存画笔 mpaintedlist.add(paintdata(paint(mpaint),path(mpath))) } motionevent.action_move -> { //手指移动的时候 //绘制圆滑曲线,即贝塞尔曲线,贝塞尔曲线这个知识自行了解 mpaintedlist.get(mpaintedlist.size-1).mpath.quadto(prex,prey,event.x,event.y) //重新绘制,会调用ondraw方法 invalidate() prex = event.x prey = event.y } motionevent.action_up ->{ //清除路径的内容 mpath.reset() } } // true:告诉系统,这个触摸事件由我来处理 // false:告诉系统,这个触摸事件我不处理,这时系统会把触摸事件传递给imageview的父节点 return true }
最后绘制的时候:
override fun ondraw(canvas: canvas) { super.ondraw(canvas) //超出缓存的就固化到缓存bitmap while(mpaintedlist.size > max_paint_recored){ val paintdata = mpaintedlist.removeat(0) paintdata.draw(mholdcanvas) } //绘制固化的内容到缓存canvas mbuffercanvas.drawbitmap(mholdbitmap,0f,0f,null) //绘制记录的画笔 for(paint in mpaintedlist){ //重新绘制每个path paint.draw(mbuffercanvas) } //画出缓存bitmap的内容 canvas.drawbitmap(mbufferbitmap,0f,0f,null) }
最后清除画布的时候,需要把画笔列表也清除了:
/** * 清空画布 */ fun clear() { mrevokedlist.clear() mpaintedlist.clear() mholdcanvas.drawcolor(0, porterduff.mode.clear) mbuffercanvas.drawcolor(0, porterduff.mode.clear) invalidate() }
重点就是在于利用一个bean类来保存每笔的 画笔和路径,然后撤销时候重新绘制。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。