天天看點

Android基礎控件——SeekBar的自定義,超短代碼模仿抖音帶有數字拖拽進度條

前言

在開發中,經常會遇到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>