如何在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) } } }
关键改动说明
- 绘制顺序调整:将事件条绘制逻辑从
dispatchDraw移到onDraw,并在super.onDraw(canvas)之后执行。原生进度条先绘制,事件条覆盖在进度条上层,拖动柄作为子视图会在dispatchDraw阶段绘制在事件条上方,符合预期层级。 - 位置对齐优化:使用
thumbOffset(拖动柄的偏移量)计算进度条的上下边界,让事件条和原生进度条高度对齐,避免位置偏移。
内容的提问来源于stack exchange,提问作者Tedy Ramdani
相关产品推荐
相关产品推荐

