使用贝塞尔曲线绘制的自定义滑动条,不多说,先看效果图
更新了一版,优化了滑动是数字动画,并添加“我的、推荐”标签
版本1:
版本2:
关键代码分析:
1.定义变量
//假设 控件高度为115,可以求得弧度的:开始点、结束点的坐标和控制点的坐标 private int bgColor = Color.WHITE;//控件背景色 private int viewHight = 200;//控件的高度 private int rSliderBlock = 40;//圆形滑块半径 private float drawX = 0;//滑块滑动时x坐标 private int index = 6;//滑块停止滑动后,当前选中的位置 1-9 private int preIndexPx = 150;//每个位置的间距(18个分隔) private int lineWidth = 5;//线背景的宽度 private int dengSize = 30;//等号字体大小 private int gaoDiSize = 30;//“高低”字体大小 private int zhiShuSize = 40;//“风险指数”字体大小 private boolean isMoving = false;//是否正在滑动 private int tmpIndex = 0;//用来保存滑块滑动超过index的preIndexPx的距离范围时,index值在tmpIndex中不变
2.绘制凸起的弧度
//将高阶贝塞尔曲线降阶到二阶:分成4段进行绘制
if (isMoving) {//移动中,使用滑动的X坐标进行计算
path.moveTo(drawX - preIndexPx * 2, viewHight * 0.5f);//绘制起点
path.quadTo(drawX - preIndexPx * 2 + preIndexPx * 0.5f, viewHight * 0.5f, drawX - preIndexPx * 2 + preIndexPx, viewHight * 0.5f - preIndexPx * 0.5f);
path.quadTo(drawX - preIndexPx * 0.5f, viewHight * 0.5f - preIndexPx, drawX, viewHight * 0.5f - preIndexPx);//viewHight * 0.5f-preIndexPx表示:弧度的顶点为一个preIndexPx的高度
path.quadTo(drawX + preIndexPx * 0.5f, viewHight * 0.5f - preIndexPx, drawX + preIndexPx, viewHight * 0.5f - preIndexPx * 0.5f);//viewHight * 0.5f-preIndexPx表示:弧度的顶点为一个preIndexPx的高度
path.quadTo(drawX + preIndexPx * 2 - preIndexPx * 0.5f, viewHight * 0.5f, drawX + preIndexPx * 2, viewHight * 0.5f);
} else {
path.moveTo(preIndexPx * (changeValue(index - 1)), viewHight * 0.5f);
path.quadTo(preIndexPx * (changeValue(index - 1)) + preIndexPx * 0.5f, viewHight * 0.5f, preIndexPx * (changeValue(index - 1)) + preIndexPx, viewHight * 0.5f - preIndexPx * 0.5f);
path.quadTo(preIndexPx * (changeValue(index)) - preIndexPx * 0.5f, viewHight * 0.5f - preIndexPx, preIndexPx * (changeValue(index)), viewHight * 0.5f - preIndexPx);//viewHight * 0.5f-preIndexPx表示:弧度的顶点为一个preIndexPx的高度
path.quadTo(preIndexPx * (changeValue(index)) + preIndexPx * 0.5f, viewHight * 0.5f - preIndexPx, preIndexPx * (changeValue(index)) + preIndexPx, viewHight * 0.5f - preIndexPx * 0.5f);//viewHight * 0.5f-preIndexPx表示:弧度的顶点为一个preIndexPx的高度
path.quadTo(preIndexPx * (changeValue(index + 1)) - preIndexPx * 0.5f, viewHight * 0.5f, preIndexPx * (changeValue(index + 1)), viewHight * 0.5f);
}
3.数字随曲线位置变化而上下浮动
/** * 用偏移量比例计算刻度的高度 * 将滑块的X的坐标值drawX,从index的位置增大到index+1的位置时的距离值的变化范围, * 映射成刻度线和数字的Y坐标,从viewHight * 0.5f到viewHight * 0.5f-preIndexPx 的变化 * 具体描述: * 滑动到index右一个2*preIndexPx距离侧时, * drawX变大,index位置的高度值变小,index+1位置的值变大。 * drawX变小,index位置的高度值变大,index+1位置的值变小 * 滑动在index左侧一个2*preIndexPx距离时 * drawX变小,index位置的高度值变小,index-1位置的值变大 * drawX变大,index位置的高度值变大,index-1位置的值变小。 * * @return float[] 0:index位置的高度变化(偏离index位置的绝对值变大则高度变小),1:index+1 或者index-1位置的高度变化(偏离index位置的绝对值变大则高度变大) */ private float[] getDistance() { float[] retValue = new float[]{viewHight * 0.5f, viewHight * 0.5f}; if (tmpIndex == 0) {//首次计算时 tmpIndex = index; } if (drawX == 0) { drawX = changeValue(tmpIndex) * preIndexPx; } if ((changeValue(tmpIndex) * preIndexPx - 2 * preIndexPx) >= drawX || drawX >= (changeValue(tmpIndex) * preIndexPx + 2 * preIndexPx)) { tmpIndex = index;//滑块滑动距离超过2*preIndexPx时对tmpIndex更新 } float b = Math.abs(drawX - changeValue(tmpIndex) * preIndexPx) / (preIndexPx * 2);//计算百分比 retValue[0] = viewHight * 0.5f - preIndexPx + preIndexPx * b;//index位置的距离变化值 retValue[1] = viewHight * 0.5f - preIndexPx * b;//index+1或者index-1位置的距离变化值 // Log.d(TAG, tmpIndex + "移动的结果:" + retValue[0] + ";" + retValue[1] + ";drawX:" + drawX + ";index和drawx的差值:" + Math.abs(drawX - changeValue(tmpIndex) * preIndexPx) + ";2格值:" + preIndexPx * 2 + ";百分比" + b + ";控件位置:" + viewHight * 0.5f); return retValue; }
4.小结
绘制贝塞尔曲线时,首先绘制开始点path.moveTo(),然后根据控制点x,y坐标,结束点的x,y坐标,绘制一段弧度。在根据下一段弧度的控制点x,y坐标,结束点的x,y坐标,绘制下一段弧度。以此类推,直到绘制完毕整条弧线。
这其中的难点在于每条弧度的控制点x,y坐标的确认,中途困扰了很久,甚至试图放弃贝塞尔使用画圆弧drawArc()来绘制整条弧线,但动态赋值后会出现弧度拼接错位错位效果,所以重新研究贝塞尔。
扯远了,言归正传,怎么确认每条贝塞尔的控制点呢,只能靠试,当然也不是盲目的试,先要对贝塞尔有个大概的轮廓,然后根据设计稿推断一个近似值,然后在多次调整,找到最佳值。
贝塞尔轮廓的建立参见:
https://blog.csdn.net/u013831257/article/details/51281136
https://juejin.im/entry/58b3b3431b69e60058b4db94
https://juejin.im/entry/58cf432bb123db3f6b43a6a7
具体代码参见我的gitHub seekBar 项目https://github.com/sougoucm/seekBar