如何在Android SeekBar中实现椭圆形状的自定义圆形拖动条
提问内容
我正在尝试开发自定义circular seekbar,但目前还没能实现下图中的效果:
问题主要出在长圆形样式的实现上,我此前已经完成过一款自定义circular seekbar,效果如下:
第二张图对应的实现代码如下:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="ring" android:thicknessRatio="16" android:useLevel="false"> <solid android:color="@color/black" /> </shape> </item> <item> <rotate android:fromDegrees="270" android:toDegrees="270"> <shape android:shape="ring" android:thicknessRatio="16" android:useLevel="true"> <solid android:color="@color/green" /> </shape> </rotate> </item> </layer-list>
我尝试调整尺寸参数,但得到的效果不符合预期,调整后效果如下:
请问有人知道该如何实现目标椭圆形状的circular seekbar吗?
解决方案
原生XMLshape标签的ring类型仅支持绘制正圆形环形,无法直接实现长圆形效果,这里提供两种可行实现方式:
方案1:自定义View绘制(推荐,适配性最高)
这是效果还原度最高的方案,核心逻辑如下:
- 长圆形轨道本质是左右两端为半圆的圆角矩形,轨道圆角半径等于控件整体高度的1/2
- 轨道绘制逻辑:
- 构造外轮廓路径:路径为圆角矩形,圆角半径设为控件高度/2
- 构造内轮廓路径:尺寸比外轮廓小2倍的轨道厚度,圆角半径为(控件高度/2 - 轨道厚度)
- 两条路径做异或运算得到空心长圆形轨道路径
- 进度绘制逻辑:
- 以控件中心点为圆心,按照进度对应的角度生成扇形裁剪区域
- 将扇形裁剪区域和轨道路径做交集运算得到进度填充区域
- 填充进度颜色即可
核心实现示例:
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) val trackThickness = 40f // 可自定义轨道厚度 val radius = height / 2f // 外圆角矩形路径 val outerRect = RectF(0f, 0f, width.toFloat(), height.toFloat()) val outerPath = Path().apply { addRoundRect(outerRect, radius, radius, Path.Direction.CW) } // 内圆角矩形路径 val innerRect = RectF(trackThickness, trackThickness, width - trackThickness, height - trackThickness) val innerPath = Path().apply { addRoundRect(innerRect, radius - trackThickness, radius - trackThickness, Path.Direction.CW) } // 得到空心轨道路径 val trackPath = Path().apply { op(outerPath, innerPath, Path.Op.XOR) } // 绘制背景轨道 canvas.drawPath(trackPath, backgroundPaint) // 计算进度对应角度 val progressAngle = 360 * currentProgress / maxProgress // 进度裁剪扇形路径 val progressClipPath = Path().apply { moveTo(width/2f, height/2f) arcTo(outerRect, -90f, progressAngle, true) close() } // 取轨道和扇形的交集作为进度区域 val progressPath = Path().apply { op(trackPath, progressClipPath, Path.Op.INTERSECT) } canvas.drawPath(progressPath, progressPaint) }
方案2:矢量Drawable实现(无需自定义View)
不想写自定义View的场景下,可以用矢量图定义长圆形轨道,配合动态裁剪实现进度效果,适合固定尺寸的使用场景:
<!-- 长圆形轨道矢量图 --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="200dp" android:height="100dp" android:viewportWidth="200" android:viewportHeight="100"> <!-- 背景空心轨道 --> <path android:fillColor="@color/black" android:fillType="evenOdd" android:pathData="M50,0 L150,0 A50,50 0 0,1 150,100 L50,100 A50,50 0 0,1 50,0 Z M60,10 L140,10 A40,40 0 0,1 140,90 L60,90 A40,40 0 0,1 60,10 Z"/> </vector>
进度部分可以在代码中动态裁剪Drawable或者配合RotateDrawable实现进度展示。
内容的提问来源于stack exchange,提问作者MadMax
相关产品推荐
相关产品推荐

