Android画板开发之橡皮擦功能

 更新时间:2018年12月20日 10:11:18   作者:tpnet  
这篇文章主要为大家详细介绍了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

 //当前编辑模式默认为画笔模式
 @EditMode
 private var mMode: Long = EDIT_MODE_PEN

 /**
  * 设置画笔模式
  */
 fun setModel(@EditMode model:Long){
  mMode = model
  when(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: Bitmap
 private 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.x
  preY = 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

 //当前编辑模式默认为画笔模式
 @EditMode
 private var mMode: Long = EDIT_MODE_PEN

 private 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: Bitmap
 private lateinit var mBufferCanvas: Canvas


 init {
  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.x
    preY = event.y
   }
   MotionEvent.ACTION_MOVE -> { //手指移动的时候
    //绘制圆滑曲线,即贝塞尔曲线,贝塞尔曲线这个知识自行了解
    mPath.quadTo(preX,preY,event.x,event.y)

    //在缓存里面绘制
    mBufferCanvas.drawPath(mPath,mPaint)

    //重新绘制,会调用onDraw方法
    invalidate()
    preX = event.x
    preY = event.y
   }
   MotionEvent.ACTION_UP ->{
    //清除路径的内容
    mPath.reset()
   }
  }

  // true:告诉系统,这个触摸事件由我来处理
  // false:告诉系统,这个触摸事件我不处理,这时系统会把触摸事件传递给imageview的父节点
  return true
 }

 /**
  * 设置画笔模式
  */
 fun setModel(@EditMode model:Long){
  mMode = model
  when(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 MQTT与WebSocket协议详细讲解

    Android MQTT与WebSocket协议详细讲解

    MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议
    2022-11-11
  • Android通过手势实现的缩放处理实例代码

    Android通过手势实现的缩放处理实例代码

    Android通过手势实现的缩放处理实例代码,需要的朋友可以参考一下
    2013-05-05
  • Android 使用ViewPager自动滚动循环轮播效果

    Android 使用ViewPager自动滚动循环轮播效果

    本文主要给大家介绍viewpager自动播放,循环滚动的效果,对android viewpager滚动相关知识感兴趣的朋友可以参考下本篇文章
    2015-11-11
  • Android开心消消乐代码实例详解

    Android开心消消乐代码实例详解

    这篇文章主要介绍了Android开心消消乐代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • Android Studio 透明状态栏的实现示例

    Android Studio 透明状态栏的实现示例

    这篇文章主要介绍了Android Studio 透明状态栏的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Android 自定义AlertDialog对话框样式

    Android 自定义AlertDialog对话框样式

    实际的项目开发当中,经常需要根据实际的需求来自定义AlertDialog。最近在开发一个WIFI连接的功能,点击WIFI需要弹出自定义密码输入框,具体代码大家参考下本文
    2017-09-09
  • Android系统开发中log的使用方法及简单的原理

    Android系统开发中log的使用方法及简单的原理

    LOG是广泛使用的用来记录程序执行过程的机制,它既可以用于程序调试,也可以用于产品运营中的事件记录;在平时开发过程中经常需要与log打交道,所以很有必要了解log的使用方法及简单的原理,感兴趣的朋友可以了解下啊
    2013-01-01
  • Android自定义商品购买数量加减控件

    Android自定义商品购买数量加减控件

    这篇文章主要为大家详细介绍了Android自定义商品购买数量加减控件的相关代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • Android全局获取Context实例详解

    Android全局获取Context实例详解

    这篇文章主要介绍了Android全局获取Context实例详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • AndroidStudio代码达到指定字符长度时自动换行实例

    AndroidStudio代码达到指定字符长度时自动换行实例

    这篇文章主要介绍了AndroidStudio代码达到指定字符长度时自动换行实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-03-03

最新评论