当前位置: 移动技术网 > 移动技术>移动开发>Android > Android画板开发之撤销反撤销功能

Android画板开发之撤销反撤销功能

2019年07月31日  | 移动技术网移动技术  | 我要评论

一、分析

这篇将会讲解撤销反撤销功能的实现,先讨论一下这个原理是怎么样实现的。

每次撤回的内容,内容是怎么定义呢? 其实就是每一笔,每一笔作为撤回的内容,那每一笔怎么算呢,就是算手指从按下-移动-放开这一个过程就是一笔。

我们只需记录这个过程为一笔,然后用一个已画列表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类来保存每笔的 画笔和路径,然后撤销时候重新绘制。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网