前面的话
自定义 View 这个东西得写熟练了,世上事情多大无捷径可寻,唯熟练尔。
主要是根据这篇博客的思路来进行编写的,建议是先撸一遍博主的文章,清楚在绘制中的一些重点和知识盲点,然后自行思考去如何实现该控件。最好是不要先去看已经写好的源码,可以等仿写完成之后再去看,和自身做一个对比各取精华。
效果展示
原博文效果
再再仿制效果
再再仿制的效果基本上都做到了,比如:触摸按钮的外阴影效果、椭圆背景的内阴影效果以及触摸点是否位于圆形按钮上的判断。
要点讲述
做东西先关其总体,寻找关键点,预先解决一些重要点的攻破,这样才能事半功倍。
-
椭圆背景内阴影
这个采取的方法是先绘制椭圆路径,给椭圆路径添加阴影,最后使用
canvas裁剪外面的阴影保留内。 -
阴影效果的变化
这个采用的是使用属性动画,在用户按钮下按钮和抬起手指的时候触发。
-
关于圆环按钮阴影扩散处理
我们在绘制阴影扩散的时候,通常会遇到应为阴影被限制在控件大小内的情况,就好似阴影被控件轮廓直接裁剪掉了。
这个采取办法是控件大小不变,依据外阴影的大小减少实际可用大小。
比如:
-
控件长宽是 100dp * 60dp
-
按钮外阴影最大范围是 4dp 最小是 2dp(因为我们的按钮有阴影大小变化),同时阴影要在 y 轴上下移 2dp。
-
那么控件的 left / top / right / buttom 应该留出 4dp / 2dp / 4dp 6dp 的距离以便显示阴影,示意图是这样的。具体的方式是直接移动
canvas.translate()
-
绘制两个圆点指示器
这一步要注意的就是要计算好两个圆点之间的距离。
/**
* 绘制两个圆点指示器
* @param canvas
*/
private fun drawFlag(canvas: Canvas) {
canvas.save()
canvas.clipPath(shadowPath)
dotFlagPaint.color = dotFlagColors[0]
canvas.drawCircle(dotFlagDistance - (1 - touchButtonMoveRate)*switchFlagBetweenDistance, reallyUseHeight / 2f, dotFlagRadius, dotFlagPaint)
dotFlagPaint.color = dotFlagColors[1]
canvas.drawCircle(
reallyUseWidth - dotFlagDistance + touchButtonMoveRate*switchFlagBetweenDistance,
reallyUseHeight / 2f,
dotFlagRadius,
dotFlagPaint
)
canvas.restore()
}
绘制椭圆背景
/**
* 绘制开关背景
* @param canvas
*/
private fun drawSwitchBackground(canvas: Canvas) {
canvas.save()
commnPaint.color = Color.parseColor("#e1e1e1")
commnPaint.style = Paint.Style.STROKE
commnPaint.strokeWidth = Utils.dp2px(1)
// 绘制内阴影?先给外框绘制阴影,然后再裁切掉外部阴影即可。
commnPaint.setShadowLayer(cornerRadius / 3, 0f, 0f, Color.parseColor("#ffc1c1c1"))
// 裁切
canvas.clipPath(shadowPath)
// 然后再进行绘制
canvas.drawPath(shadowPath, commnPaint)
commnPaint.clearShadowLayer()
canvas.restore()
}
绘制圆形按钮
/**
* 绘制触摸按钮
* @param canvas
*/
private fun drawTouchButton(canvas: Canvas) {
canvas.save()
dotFlagPaint.style = Paint.Style.FILL_AND_STROKE
dotFlagPaint.color = Color.WHITE
val finalShadowSpace = touchButtonShadowSpaceMin + (1 - touchPushRate) * (touchButtonShadowSpaceMax - touchButtonShadowSpaceMin)
dotFlagPaint.setShadowLayer(finalShadowSpace,0f,touchButtonShadowSpaceYOffse,Color.parseColor("#d1d1d1"))
// 计算圆形按钮 x 位置
touchButtonX = touchButtonXinit + touchButtonFreeSpace*touchButtonMoveRate
canvas.drawCircle(
touchButtonX, reallyUseHeight / 2f,
reallyUseHeight / 2f ,
dotFlagPaint
)
canvas.restore()
dotFlagPaint.clearShadowLayer()
}
判断用户手指是否按在圆形按钮上
/**
* 绘制触摸按钮
* @param canvas
*/
private fun drawTouchButton(canvas: Canvas) {
canvas.save()
dotFlagPaint.style = Paint.Style.FILL_AND_STROKE
dotFlagPaint.color = Color.WHITE
val finalShadowSpace = touchButtonShadowSpaceMin + (1 - touchPushRate) * (touchButtonShadowSpaceMax - touchButtonShadowSpaceMin)
dotFlagPaint.setShadowLayer(finalShadowSpace,0f,touchButtonShadowSpaceYOffse,Color.parseColor("#d1d1d1"))
// 计算圆形按钮 x 位置
touchButtonX = touchButtonXinit + touchButtonFreeSpace*touchButtonMoveRate
canvas.drawCircle(
touchButtonX, reallyUseHeight / 2f,
reallyUseHeight / 2f ,
dotFlagPaint
)
canvas.restore()
dotFlagPaint.clearShadowLayer()
}