欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android基础控件——SeekBar的自定义,超短代码模仿抖音带有数字拖拽进度条

程序员文章站 2022-04-11 15:33:39
...

前言

在开发中,经常会遇到SeekBar组件的开发,一个高效的自定义SeekBar显得尤为重要,笔者刚好也在项目中大量使用带有数字的拖拽进度条,在深思熟虑后,打算从继承源码形式上,把数字绘制在拖拽进度条上,让拖拽的时候时刻去更新数字。这种实现方式不到100行代码,代码极其精简,功能极其好用,另外,这种方案可以用于各种组件绘制在进度条上方,这块的实现就看需求的具体效果

本例子中高仿抖音的进度拖拽效果,实现效果如下

Android基础控件——SeekBar的自定义,超短代码模仿抖音带有数字拖拽进度条

实现思路

  • 继承SeekBar,自定义属性
  • 在onDraw方法增加数字的绘制和数字阴影的绘制

实现分析

1、快速使用

在xml直接使用,其监听进度变化同SeekBar组件

<com.baseui.seekbar.NumberSeekBar
    android:layout_width="match_parent"
    android:layout_height="54dp"
    android:max="100"
    android:paddingStart="24dp"
    android:paddingEnd="24dp"
    android:progressDrawable="@drawable/progress_seek_bar_beauty"
    android:thumb="@drawable/icon_beauty_seek_bar_thumb"
    app:textColor="@color/white"
    app:textMarginBottom="2dp"
    app:textShadowColor="#80000000"
    app:textShadowOffsetX="0dp"
    app:textShadowOffsetY="0dp"
    app:textShadowRadius="2dp"
    app:textSize="12sp"
    app:withText="true"
    app:withTextShadow="true"
    tools:progress="50"/>

2、自定义属性

在attr中增加自定义的属性

<resources>
    <declare-styleable name="InheritedSeekBar">
        <attr name="withText" format="boolean" />
        <attr name="textSize" format="dimension" />
        <attr name="textColor" format="color" />
        <attr name="textMarginBottom" format="dimension" />
        <attr name="withTextShadow" format="boolean" />
        <attr name="textShadowColor" format="color" />
        <attr name="textShadowOffsetX" format="dimension" />
        <attr name="textShadowOffsetY" format="dimension" />
        <attr name="textShadowRadius" format="dimension" />
    </declare-styleable>
</resources>
  • withText:是否带有进度值
  • textSize:进度值的大小
  • textColor:进度值的颜色
  • textMarginBottom:进度值底边距
  • withTextShadow:是否带有进度值的阴影
  • textShadowColor:进度值阴影的颜色
  • textShadowOffsetX:进度值阴影的x坐标偏移
  • textShadowOffsetY:进度值阴影的y坐标偏移
  • textShadowRadius:进度值阴影的圆角

3、获取属性

通过attributeSet获取自定义属性,同时获取进度的属性和进度阴影的属性

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

    private val textPaint = Paint()
    private var withText: Boolean = false
    private var textSize: Float = 0f
    private var textColor: Int = Color.BLACK
    private var textMarginBottom: Int = 0
    private var withTextShadow = false
    private var textShadowColor = Color.BLACK
    private var textShadowRadius = 0
    private var textShadowOffsetX = 0
    private var textShadowOffsetY = 0

    init {
        init(context, attrs)
    }

    private fun init(context: Context, attributeSet: AttributeSet?) {
        val a: TypedArray = context.obtainStyledAttributes(attributeSet, R.styleable.InheritedSeekBar)
        // 获取进度的属性
        withText = a.getBoolean(R.styleable.InheritedSeekBar_withText, false)
        if (withText) {
            textSize = a.getDimensionPixelSize(R.styleable.InheritedSeekBar_textSize,
                    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                            12f,
                            context.resources.displayMetrics).toInt()).toFloat()
            textColor = a.getColor(R.styleable.InheritedSeekBar_textColor, textColor)
            textMarginBottom = a.getDimensionPixelSize(R.styleable.InheritedSeekBar_textMarginBottom, 0)
        }
        // 获取进度阴影的属性
        withTextShadow = a.getBoolean(R.styleable.InheritedSeekBar_withTextShadow, false)
        if (withTextShadow) {
            textShadowColor = a.getColor(R.styleable.InheritedSeekBar_textShadowColor, textShadowColor)
            textShadowRadius = a.getDimensionPixelSize(R.styleable.InheritedSeekBar_textShadowRadius, 0)
            textShadowOffsetX = a.getDimensionPixelOffset(R.styleable.InheritedSeekBar_textShadowOffsetX, 0)
            textShadowOffsetY = a.getDimensionPixelOffset(R.styleable.InheritedSeekBar_textShadowOffsetY, 0)
        }
        
        a.recycle()

        // 对画笔设置值和阴影
        if (withText) {
            textPaint.textSize = textSize
            textPaint.color = textColor
            textPaint.textAlign = Paint.Align.CENTER
        }
        if (withTextShadow) {
            textPaint.setShadowLayer(textShadowRadius.toFloat(), textShadowOffsetX.toFloat(),
                    textShadowOffsetY.toFloat(), textShadowColor)
        }
    }
}

4、绘制数字和阴影

通过复写onDraw方法,经过计算偏移量后,在进度拖拽的上方绘制数字

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    if (withText) {
        drawText(canvas)
    }
}

private fun drawText(canvas: Canvas) {
    val saveCount = canvas.save()
    val thumbTopY = thumb.bounds.top
    // 计算拖拽组件的中点
    val thumbCenterX = paddingStart + progress.toFloat() / max * (width - paddingStart - paddingEnd)
    canvas.drawText(progress.toString(), thumbCenterX, thumbTopY.toFloat() - textMarginBottom, textPaint)
    canvas.restoreToCount(saveCount)
}

5、进度条颜色

对于进度条颜色是layer-list,可以设置进度的背景和进度颜色值,而进度拖拽组件,其实就是个圆形的Icon图片而已

android:progressDrawable="@drawable/progress_seek_bar"
android:thumb="@drawable/icon_beauty_seek_bar_thumb"

progress_seek_bar设置进度成黄色,背景成少许透明的灰黑色

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@android:id/background"
        android:gravity="center_vertical">
        <shape>
            <solid android:color="#29ffffff" />
            <corners android:radius="3dp" />
            <size android:height="3dp" />
        </shape>
    </item>

    <item
        android:id="@android:id/secondaryProgress"
        android:gravity="center_vertical">
        <clip>
            <shape>
                <solid android:color="#29ffffff" />
                <corners android:radius="3dp" />
                <size android:height="3dp" />
            </shape>
        </clip>
    </item>

    <item
        android:id="@android:id/progress"
        android:gravity="center_vertical">
        <clip>
            <shape>
                <solid android:color="#ffffe000" />
                <corners android:radius="3dp" />
                <size android:height="3dp" />
            </shape>
        </clip>
    </item>
</layer-list>