# 1. 前言
对于模式:PorterDuff.Mode.SRC_OUT;
计算方式为:\[Sa \* (1 - Da), Sc \* (1 - Da)\],表示如果相交处的目标色的alpha是完全不透明的,这时候源图像会完全被过滤掉,否则会受到相交处目标色 alpha 影响,呈现出对应色值。
也就是绘图效果取决于目标图的不透明度Da,如果源图像和目标图像不相交,那么就绘制源图,否则就根据公式计算出对应的图像值。
根据这个效果,我们就可以尝试做一个橡皮擦效果。也就是将要擦除的图像作为源图,将手指绘制的区域作为目标图,然后动态绘制手指的范围,根据公式,如果两图之间的目标图不透明度为1,那么就计算为0,可以达到清除的目的。
~~~
class PorterDuffXfermodeDemo3 : View {
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init()
}
private lateinit var mRectPaint: Paint
private lateinit var mRect: Rect
private val resBitmap = BitmapFactory.decodeResource(resources, R.drawable.logo)
private val mPath = Path()
private fun init() {
mRect = Rect(200, 200, 400, 400)
mRectPaint = Paint()
mRectPaint.isAntiAlias = true
mRectPaint.color = Color.RED
mRectPaint.isDither = true
mRectPaint.strokeWidth = 30f
mRectPaint.style = Paint.Style.STROKE
// 关闭硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null)
}
// 记录坐标
private var mX = 0f
private var mY = 0f
override fun onTouchEvent(event: MotionEvent?): Boolean {
var flag = super.onTouchEvent(event)
when(event?.action){
MotionEvent.ACTION_DOWN -> {
mX = event.x
mY = event.y
mPath.moveTo(mX, mY)
flag = true
}
MotionEvent.ACTION_MOVE -> {
val cX = event.x
val cY = event.y
val midX = (cX + mX) / 2
val midY = (cY + mY) / 2
mPath.quadTo(midX, midY, cX, cY)
mX = cX
mY = cY
invalidate()
}
MotionEvent.ACTION_UP -> {
mX = event.x
mY = event.y
mPath.lineTo(mX, mY)
}
}
return flag
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.apply { // this == canvas
// 新建图层
val saveLayerId = saveLayer(0f, 0f, width.toFloat(), height.toFloat(), mRectPaint)
// 绘制源图
canvas.drawBitmap(resBitmap, 100f, 100f, mRectPaint)
// 设置图像混合模式
mRectPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
// 绘制目标图像
drawPath(mPath, mRectPaint)
// 清空图像混合模式
mRectPaint.xfermode = null
restoreToCount(saveLayerId)
}
}
}
~~~
效果,比如在图上画个S:
![](https://img.kancloud.cn/79/5d/795d164cd22ae8e1b1ce0e0d6cf6ebc8_261x259.png)
当然,这里为了观察方便,可以为图层指定一个颜色:
![](https://img.kancloud.cn/c0/f5/c0f57c32133a24df36db70994a0d2786_281x264.png)
当然,可以为画笔指定一个圆角特效:
~~~
// 设置画笔圆角特效
mRectPaint.pathEffect = CornerPathEffect(100f)
~~~
同样的,也可以指定线帽样式:
~~~
// 设置线帽样式为圆角
mRectPaint.strokeCap = Paint.Cap.ROUND
~~~
效果:
![](https://img.kancloud.cn/13/13/1313379fd721c855dbcb5d49ccbc5331_270x252.png)
从上图中可以看出,如果我们擦除了图片那么,底层就是画布的背景颜色,所以我们如果可以在其后预先设置一个背景文字,就可以做到刮刮乐效果了。比如这里做简单修改onDraw:
~~~
drawColor(Color.GRAY)
drawText("Hello", 200f, 200f, mRectPaint)
...
~~~
然后调整一下字体大小、线条粗细和颜色:
~~~
mRectPaint.strokeWidth = 18f
mRectPaint.textSize = 100f
mRectPaint.color = Color.BLACK
~~~
效果:
![](https://img.kancloud.cn/60/8c/608c6b8fd77774d1b6e404c216658e41_273x260.png)
- 介绍
- UI
- MaterialButton
- MaterialButtonToggleGroup
- 字体相关设置
- Material Design
- Toolbar
- 下拉刷新
- 可折叠式标题栏
- 悬浮按钮
- 滑动菜单DrawerLayout
- NavigationView
- 可交互提示
- CoordinatorLayout
- 卡片式布局
- 搜索框SearchView
- 自定义View
- 简单封装单选
- RecyclerView
- xml设置点击样式
- adb
- 连接真机
- 小技巧
- 通过字符串ID获取资源
- 自定义View组件
- 使用系统控件重新组合
- 旋转菜单
- 轮播图
- 下拉输入框
- 自定义VIew
- 图片组合的开关按钮
- 自定义ViewPager
- 联系人快速索引案例
- 使用ListView定义侧滑菜单
- 下拉粘黏效果
- 滑动冲突
- 滑动冲突之非同向冲突
- onMeasure
- 绘制字体
- 设置画笔Paint
- 贝赛尔曲线
- Invalidate和PostInvalidate
- super.onTouchEvent(event)?
- setShadowLayer与阴影效果
- Shader
- ImageView的scaleType属性
- 渐变
- LinearGradient
- 图像混合模式
- PorterDuffXfermode
- 橡皮擦效果
- Matrix
- 离屏绘制
- Canvas和图层
- Canvas简介
- Canvas中常用操作总结
- Shape
- 圆角属性
- Android常见动画
- Android动画简介
- View动画
- 自定义View动画
- View动画的特殊使用场景
- LayoutAnimation
- Activity的切换转场效果
- 属性动画
- 帧动画
- 属性动画监听
- 插值器和估值器
- 工具
- dp和px的转换
- 获取屏幕宽高
- JNI
- javah命令
- C和Java相互调用
- WebView
- Android Studio快捷键
- Bitmap和Drawable图像
- Bitmap简要介绍
- 图片缩放和裁剪效果
- 创建指定颜色的Bitmap图像
- Gradle本地仓库
- Gradle小技巧
- RxJava+Okhttp+Retrofit构建网络模块
- 服务器相关配置
- node环境配置
- 3D特效