You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android SeekBar滑块上正确添加事件标记彩色线条?

如何在SeekBar进度条上层、拖动柄下层绘制事件提示条

我尝试在SeekBar滑块上添加特定颜色的线条,用来提示用户视频对应时间点存在特定事件。期望这些彩色线条位于滑块线条上方、拖动柄(scrubber)下方,但实际实现效果不符合预期。

现有代码实现

Player 代码

val seekBar = findViewById<EventOverlayBar>(R.id.seek_bar)

val eventSections = listOf(
            Pair(10, 20),  // 事件1:10秒到20秒
            Pair(40, 50)   // 事件2:40秒到50秒
)
val duration = 100  // 示例视频总时长100秒

seekBar.setEventSections(eventSections, duration)

自定义SeekBar代码

class EventOverlayBar @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : androidx.appcompat.widget.AppCompatSeekBar(context, attrs) {

    private val eventPaint = Paint().apply {
        color = Color.BLUE
        style = Paint.Style.FILL
    }

    private var eventSections: List<Pair<Int, Int>> = listOf()
    private var duration: Int = 0

    fun setEventSections(adSections: List<Pair<Int, Int>>, duration: Int) {
        this.eventSections = adSections
        this.duration = duration
        invalidate()
    }

    override fun dispatchDraw(canvas: Canvas) {
        super.dispatchDraw(canvas)
        drawEventSections(canvas)
    }

    private fun drawEventSections(canvas: Canvas) {
        if (duration == 0) return

        val barWidth = width - paddingLeft - paddingRight
        val barHeight = height / 2

        for ((start, end) in eventSections) {
            val startX = paddingLeft + (start.toFloat() / duration) * barWidth
            val endX = paddingLeft + (end.toFloat() / duration) * barWidth
            canvas.drawRect(startX, barHeight.toFloat(), endX, (barHeight + 10).toFloat(), eventPaint)
        }
    }
}

布局代码

<com.example.EventOverlayBar
    android:id="@+id/exo_progress"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
/>

问题原因与解决方案

问题根源

当前代码在dispatchDraw中先调用super.dispatchDraw(canvas)(会绘制拖动柄等子视图),再绘制事件条,导致事件条被拖动柄遮挡;同时事件条的Y轴位置计算未和原生进度条对齐,位置偏移。

修复后的代码

class EventOverlayBar @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : androidx.appcompat.widget.AppCompatSeekBar(context, attrs) {

    private val eventPaint = Paint().apply {
        color = Color.BLUE
        style = Paint.Style.FILL
    }

    private var eventSections: List<Pair<Int, Int>> = listOf()
    private var duration: Int = 0

    fun setEventSections(adSections: List<Pair<Int, Int>>, duration: Int) {
        this.eventSections = adSections
        this.duration = duration
        invalidate()
    }

    override fun onDraw(canvas: Canvas) {
        // 先绘制原生SeekBar的进度条
        super.onDraw(canvas)
        // 再绘制事件提示条,此时事件条在进度条上层
        drawEventSections(canvas)
    }

    private fun drawEventSections(canvas: Canvas) {
        if (duration == 0) return

        val barWidth = width - paddingLeft - paddingRight
        // 基于拖动柄偏移量对齐原生进度条的上下边界
        val progressBarTop = thumbOffset - 5f
        val progressBarBottom = thumbOffset + 5f

        for ((start, end) in eventSections) {
            val startX = paddingLeft + (start.toFloat() / duration) * barWidth
            val endX = paddingLeft + (end.toFloat() / duration) * barWidth
            canvas.drawRect(startX, progressBarTop, endX, progressBarBottom, eventPaint)
        }
    }
}

关键改动说明

  1. 绘制顺序调整:将事件条绘制逻辑从dispatchDraw移到onDraw,并在super.onDraw(canvas)之后执行。原生进度条先绘制,事件条覆盖在进度条上层,拖动柄作为子视图会在dispatchDraw阶段绘制在事件条上方,符合预期层级。
  2. 位置对齐优化:使用thumbOffset(拖动柄的偏移量)计算进度条的上下边界,让事件条和原生进度条高度对齐,避免位置偏移。

内容的提问来源于stack exchange,提问作者Tedy Ramdani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 10:20:03