先上效果:
刮刮乐
class EraseView : View {
private val mPaint by lazy {
Paint().apply {
color = Color.RED
isAntiAlias = true
strokeWidth = 50f // 设置画笔粗细
style = Paint.Style.STROKE // 设置画笔样式为描边
strokeCap = Paint.Cap.ROUND // 设置画笔端点为圆形
strokeJoin = Paint.Join.ROUND // 设置拐角为圆形
}
}
private val mTextPaint by lazy {
Paint().apply {
isAntiAlias = true
color = Color.RED // 设置擦除画笔为红色
style = Paint.Style.FILL // 设置画笔样式为描边
strokeWidth = 50f// 设置画笔粗细
strokeCap = Paint.Cap.ROUND // 设置画笔端点为圆形
strokeJoin = Paint.Join.ROUND // 设置拐角为圆形
textSize = 44f
}
}
private val mPath = Path()
// 源图像
private lateinit var srcBitmap: Bitmap
// 目标图层
private lateinit var dstBitmap: Bitmap
private var showText = ""//文案
private var isShowResultDirect = false//是否直接展示结果
constructor(context: Context?) : this(context, null)
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
init {
showText = getRandomStr()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
// 初始化图片资源
val originBitmap = BitmapFactory.decodeResource(resources, R.drawable.image_convor)
srcBitmap = Bitmap.createScaledBitmap(originBitmap, w, h, false)
dstBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
}
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (!this::srcBitmap.isInitialized || !this::dstBitmap.isInitialized) return
kotlin.runCatching {
canvas.drawColor(Color.parseColor("#BBBBBB"))
}
val textWidth = mTextPaint.measureText(showText) //文本宽度
val fontMetrics = mTextPaint.fontMetrics
val baselineOffset = (fontMetrics.bottom + fontMetrics.top) / 2
//显示的文案居中绘制
canvas.drawText(
showText,
(width - textWidth) / 2f,
height / 2f - baselineOffset,
mTextPaint
)
val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null)
// 1. 在画布上绘制擦除路径
canvas.drawPath(mPath, mPaint)
// 2. 绘制背景图像(目标图像)
canvas.drawBitmap(dstBitmap, 0f, 0f, mPaint)
// 3. 手动擦除,设置混合模式为 SRC_OUT,实现擦除效果;直接展示结果,用DST_OUT
mPaint.xfermode =
PorterDuffXfermode(if (isShowResultDirect) PorterDuff.Mode.DST_OUT else PorterDuff.Mode.SRC_OUT)
canvas.drawBitmap(srcBitmap, 0f, 0f, mPaint)
mPaint.xfermode = null
canvas.restoreToCount(layerId)
}
// 处理触摸事件,动态更新擦除路径
override fun onTouchEvent(event: MotionEvent): Boolean {
val x = event.x
val y = event.y
when (event.action) {
MotionEvent.ACTION_DOWN -> {
// 请求父视图不要拦截事件
parent.requestDisallowInterceptTouchEvent(true)
mPath.moveTo(x, y) // 路径起点
}
MotionEvent.ACTION_MOVE -> {
mPath.lineTo(x, y) // 路径延续
invalidate() // 重新绘制
}
MotionEvent.ACTION_UP -> {
parent.requestDisallowInterceptTouchEvent(false)
}
}
return true
}
/**
* 重置文案
*/
private fun getRandomStr(): String {
return listOf("谢谢惠顾", "特等奖", "一等奖", "二等奖", "三等奖").random()
}
/**
* 重置状态,再刮一次
*/
fun resetState() {
mPath.reset()
isShowResultDirect = false
showText = getRandomStr()
invalidate()
}
/**
* 直接展示结果
*/
fun showResultDirect() {
isShowResultDirect = true
invalidate()
}
}