“我正在参加「启航计划」”
前段时间做了一个圆形物品扫码相关的需求,要求做一个扫描动画,扫描线只在圆形区域显现 效果图如下:
思路如下:
看到这东西,第一反响便是运用自定义view
- 画出一个蒙层,该蒙层能够运用
PorterDuff.Mode
的clear - 画一个圆形白色圆框
- 制作中心的扫描线(扫描线运用的是bitmap图片),给扫描线做动画使其上下移动
- 因为扫描线是矩形图,如何使扫描线只在白色圆框中显现,就又要运用
PorterDuff.Mode
利用他的色彩图层的规矩,关于图层交互规矩可检查文档:Google 的官方文档,这儿根据文档的规矩我运用了PorterDuff.Mode.DST_IN
,//只在源图画和方针图画相交的当地制作方针图画 - 这儿所以我画了一个特殊的形状的bitmap,可检查下面办法
createCircularBitmap
,白色圆形上面是一个矩形,避免我的扫描线bitmap超出我的制作区域。 - 需要注意的是bitmap必须要有内容填充,因为
PorterDuff.Mode
其实是对图画内容的处理
源码如下:注释写的很清楚
/**
* 识别蒙层
* 参阅:https://www.jianshu.com/p/2feac6a535a3
* https://www.jianshu.com/p/134cd2dbb43b
*/
class CoinScanMaskView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private var circleRadius = 0f
private var width = 0f
private var height = 0f
private var circleDrawLeft = 0f
private var circleDrawTop = 0f
private val mPaint = Paint()
private val mTextPaint = Paint().apply {
color = resources.getColor(R.color.white)
textSize = 14.dp.toFloat()
isAntiAlias = true
isDither = true
style = Paint.Style.FILL_AND_STROKE
textAlign = Paint.Align.CENTER
}
private val clearMode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
private val circleBitmap: Bitmap =
BitmapFactory.decodeResource(resources, R.drawable.ic_coin_recognition_circle)
private var tipText = "Ready in a moment…"
// 扫描线
private val scanBitmap: Bitmap =
BitmapFactory.decodeResource(resources, R.drawable.identify_ic_camera_scan_line)
private var mScanLineTop = 0f
private val mScanLineRectF = RectF()
private val clipScanClearMode =
PorterDuffXfermode(PorterDuff.Mode.DST_IN)//只在源图画和方针图画相交的当地制作方针图画
private var clipBitmap: Bitmap
private var needCanvasScan = false
init {
clipBitmap = createCircularBitmap()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
circleRadius = ((circleBitmap.width / 2)).toFloat()
width = measuredWidth.toFloat()
height = measuredHeight.toFloat()
circleDrawLeft = (width - circleRadius * 2) / 2
circleDrawTop = (height - circleRadius * 2) / 2
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
mPaint.reset()
val layerID = canvas.saveLayer(0f, 0f, width, height, mPaint)
// 蒙层背景
mPaint.color = 0x66000000
canvas.drawRect(0f, 0f, width, height, mPaint)
// 中心透明区域
mPaint.xfermode = clearMode
mPaint.color = 0x00000000
canvas.drawCircle(
width / 2, height / 2, circleRadius,
mPaint
)
canvas.restoreToCount(layerID)
mPaint.reset()
// 提示文字
canvas.drawText(
tipText,
(width / 2).toFloat(),
height / 2 + circleRadius + 32.dp,
mTextPaint
)
//圆形边框
canvas.drawBitmap(
circleBitmap,
circleDrawLeft,
circleDrawTop,
mPaint
)
//铺开该行代码可检查自己制作的 形状,与下面的scanBitmap扫描的相交
// canvas.drawBitmap(
// clipBitmap, circleDrawLeft,
// circleDrawTop - scanBitmap.height, mPaint
// )
//再画上结果
if (needCanvasScan) {
val layerScanId =
canvas.saveLayer(0f, 0f, width, height, null, Canvas.ALL_SAVE_FLAG)
mScanLineRectF.set(
circleDrawLeft, circleDrawTop + mScanLineTop - scanBitmap.height,
circleDrawLeft + clipBitmap.width,
circleDrawTop + mScanLineTop
)
//制作第一层,方针图画,在基层
canvas.drawBitmap(
scanBitmap,
null,
mScanLineRectF,
mPaint
)
mPaint.xfermode = clipScanClearMode
//在圆形上面制作一个画布,避免扫描线bitmap透出显现,源图画,第二层盖在了方针图画上
canvas.drawBitmap(
clipBitmap, circleDrawLeft,
circleDrawTop - scanBitmap.height, mPaint
)
mPaint.xfermode = null
canvas.restoreToCount(layerScanId)
moveScanLine()
}
}
/**
* 移动扫描线
*/
private fun moveScanLine() {
mScanLineTop += 2.dp
if (mScanLineTop > circleBitmap.height) {
mScanLineTop = 0f
}
postInvalidateDelayed(16, 0, 0, measuredWidth, measuredHeight)
}
private fun createCircularBitmap(): Bitmap {
// 创立一个长方形的 Bitmap
val size = 190.dp
// 设置 Bitmap 巨细
val bitmap = Bitmap.createBitmap(size, size + scanBitmap.height, Bitmap.Config.ARGB_8888)
// 创立一个 Canvas 目标,将 bitmap 作为制作方针
val canvas = Canvas(bitmap)
// 创立一个 Paint 目标,用于设置制作特点
val paint = Paint().apply {
color = Color.RED // 设置圆形区域的色彩,必须要有色彩不然组成的时候无效
}
// 创立一个圆形途径
val centerX = size / 2f
val centerY = size / 2f
val radius = size / 2f
//制作一个圆形,因为只想要圆形区域,所以只制作圆形区域
val path = Path().apply {
addCircle(centerX, centerY + scanBitmap.height, radius, Path.Direction.CW)
}
//在圆形上面制作一个长方形,避免扫描线bitmap透出显现
//铺开该行代码可检查自己制作的 长方形形状,与下面的scanBitmap扫描的相交
// path.addRect(0f, 0f, size.toFloat(), scanBitmap.height.toFloat(), Path.Direction.CW)
// 在 Canvas 上制作圆形途径
canvas.drawPath(path, paint)
return bitmap
}
override fun onVisibilityChanged(changedView: View, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
needCanvasScan = visibility == View.VISIBLE
if (needCanvasScan) {
mScanLineTop = 0f
}
}
}
参阅:www.jianshu.com/p/134cd2dbb…