手摸手教你做一个分段式进度条组件

Eason最近遇到一个需求,需要去展示分段式的进度条,为了给这个进度条想要的外观和感觉,在构建用户界面 (UI) 时,大家通常会依赖 SDK 提供的可用工具并尝试通过调整SDK来适配当前这个UI需求;但悲伤的是,大多数情况下它基本不符合我们的预期。所以Eason决定自己绘制它。

创建自定义视图

在 Android 中要绘制自定义动图,大家需要使用Paint并根据Path对象引导绘制到画布上。

我们可以直接在画布Canvas中操作上面的所有对象View。更具体地说,所有图形的绘制都发生在onDraw()回调中。

class SegmentedProgressBar @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : View(context, attrs, defStyleAttr) {

      override fun onDraw(canvas: Canvas) {
          // Draw something onto the canvas
      }
  }

回到进度条,让我们从开始对整个进度条的实现进行分解。

整体思路是: 先绘制有一组显示不同角度的四边形,它们彼此间隔开并且具有没有空间的填充状态。最后,我们有一个波浪动画与其填充进度同步。

在尝试满足上述所有这些要求之前,我们可以从一个更简单的版本开始。不过不用担心。我们会从基础的开始并逐步深入浅出的!

绘制单段进度条

第一步是绘制其最基本的版本:单段进度条。

暂时抛开角度、间距和动画等复杂元素。这个自定义动画整体来说只需要绘制一个矩形。我们从分配 aPath和一个Paint对象开始。

private val segmentPath: Path = Path()
private val segmentPaint: Paint = Paint( Paint.ANTI_ALIAS_FLAG )

尽量不在onDraw()方法内部分配对象。这两个Path和Paint对象必须在其范围之内创建。在View很多时候调用这个onDraw回调时将导致你内存逐渐减少。编译器中的 lint 消息也会警告大家不要这样做。

要实现绘图部分,我们可能要选择Path的drawRect()方法。因为我们将在接下来的步骤中绘制更复杂的形状,所以更倾向于逐点绘制。

moveTo():将画笔放置到特定坐标。

lineTo(): 在两个坐标之间画一条线。

这两种方法都接受Float值作为参数。

从左上角开始,然后将光标移动到其他坐标。

下图表示将绘制的矩形,给定一定的宽度 ( w ) 和高度 ( h )。

在Android中,绘制时,Y轴是倒置的。在这里,我们从上到下计算。

绘制这样的形状意味着将光标定位在左上角,然后在右上角画一条线。

path.moveTo(0f, 0f)

path.lineTo(w, 0f)

在右下角和左下角重复这个过程。

path.lineTo(w, h)

path.lineTo(0f, h)

最后,关闭路径完成形状的绘制。

path.close()

计算阶段已经完成。是时候用paint给它涂上颜色了!

针对Paint对象的处理,大家可以使用颜色、Alpha 通道和其他选项。Paint.Style枚举决定形状是否将被填充(默认)、空心有边框或两者兼而有之。 在示例中,将绘制一个带有半透明灰色的填充矩形:

paint.color = color

paint.alpha = alpha.toAlphaPaint()

对于 alpha 属性,Paint需要Integer从 0 到 255。由于更习惯于Float从 0 到 1操作 a ,我创建了这个简单的转换器

fun Float.toAlphaPaint(): Int = (this * 255).toInt()

上面已准备好呈现我们的第一个分段进度条。我们只需要将我们的Paint按照计算出的x和y方向绘制在canvas上。

canvas.drawPath(path,paint)

下面是部分代码:

    class SegmentedProgressBar @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : View(context, attrs, defStyleAttr) {
        @get:ColorInt
        var segmentColor: Int = Color.WHITE
        var segmentAlpha: Float = 1f

        private val segmentPath: Path = Path()
        private val segmentPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)

        override fun onDraw(canvas: Canvas) {
            val w = width.toFloat()
            val h = height.toFloat()

            segmentPath.run {
                moveTo(0f, 0f)
                lineTo(w, 0f)
                lineTo(w, h)
                lineTo(0f, h)
                close()
            }

            segmentPaint.color = segmentColor
            segmentPaint.alpha = alpha.toAlphaPaint()
            canvas.drawPath(segmentPath, segmentPaint)
        }
    }

使用多段进度条前进

是不是感觉已经差不多快完成了呢?对的!已经完成了大部分自定义动画的工作。我们将为每个段创建一个实例,而不是操作唯一的Path和Paint对象。

var segmentCount: Int = 1 // Set wanted value here
private val segmentPaths: MutableList<Path> = mutabl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值