Android画板开发之橡皮擦功能

作者:tpnet 时间:2022-10-23 02:48:52 

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

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

Android画板开发之橡皮擦功能

一、Xfermode

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

  • AvoidXfermode

  • PixelXorXfermode

  • PorterDuffXfermode

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

1.1 PorterDuffXfermode

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

Android画板开发之橡皮擦功能

我们的做橡皮擦的时候,就是用到了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()
}

来源:https://blog.csdn.net/niubitianping/article/details/78400966

标签:Android,画板,橡皮擦
0
投稿

猜你喜欢

  • 关于在IDEA中SpringBoot项目中activiti工作流的使用详解

    2022-12-11 01:24:28
  • Android实现蓝牙聊天功能

    2023-06-10 01:19:48
  • C#中静态的深入理解

    2023-10-18 01:28:27
  • WPF+SkiaSharp实现自绘投篮小游戏

    2023-09-06 22:59:51
  • C#中除去所有在HTML元素中标记

    2023-04-14 04:09:56
  • IDEA启动tomcat控制台中文乱码问题的解决方法(100%有效)

    2021-06-25 10:45:23
  • C# TabControl控件中TabPage选项卡切换时的触发事件问题

    2022-10-30 08:11:13
  • Android开发返回键明暗点击效果的实例代码

    2022-06-08 06:39:27
  • Java在web页面上的编码解码处理及中文URL乱码解决

    2023-08-25 11:10:19
  • C#判断日期是否到期的方法

    2021-09-10 14:40:00
  • C#保存listbox中数据到文本文件的方法

    2023-02-20 10:37:27
  • SpringCloud搭建netflix-eureka微服务集群的过程详解

    2023-09-02 18:11:52
  • SpringBoot集成redis实现分布式锁的示例代码

    2022-08-24 19:21:56
  • Android 触摸事件监听(Activity层,ViewGroup层,View层)详细介绍

    2023-02-07 00:58:51
  • 利用Jetpack Compose绘制可爱的天气动画

    2022-04-06 18:50:35
  • c#中var关键字用法浅谈

    2022-03-14 00:21:51
  • 深入理解C#窗体关闭事件

    2023-06-01 14:38:56
  • Android组合控件自定义标题栏

    2021-11-04 01:12:36
  • SpringBoot 2.x整合Log4j2日志的详细步骤

    2022-08-05 09:50:19
  • 浅试仿 mapstruct实现微服务编排框架详解

    2022-07-12 13:20:44
  • asp之家 软件编程 m.aspxhome.com