唯熟尔之仿锤子SwitchView

325 阅读3分钟

前面的话

自定义 View 这个东西得写熟练了,世上事情多大无捷径可寻,唯熟练尔。

主要是根据这篇博客的思路来进行编写的,建议是先撸一遍博主的文章,清楚在绘制中的一些重点和知识盲点,然后自行思考去如何实现该控件。最好是不要先去看已经写好的源码,可以等仿写完成之后再去看,和自身做一个对比各取精华。

效果展示

原博文效果

再再仿制效果

再再仿制的效果基本上都做到了,比如:触摸按钮的外阴影效果、椭圆背景的内阴影效果以及触摸点是否位于圆形按钮上的判断。

要点讲述

做东西先关其总体,寻找关键点,预先解决一些重要点的攻破,这样才能事半功倍。

  1. 椭圆背景内阴影

    这个采取的方法是先绘制椭圆路径,给椭圆路径添加阴影,最后使用 canvas 裁剪外面的阴影保留内。

  2. 阴影效果的变化

    这个采用的是使用属性动画,在用户按钮下按钮和抬起手指的时候触发。

  3. 关于圆环按钮阴影扩散处理

    我们在绘制阴影扩散的时候,通常会遇到应为阴影被限制在控件大小内的情况,就好似阴影被控件轮廓直接裁剪掉了。

    这个采取办法是控件大小不变,依据外阴影的大小减少实际可用大小。

    比如:

    • 控件长宽是 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()
}

源码地址

github.com/bukeCN/Simp…