实现效果
Github传送门: https://github.com/drkingwater/TextSeekBar
实现功能
- 基本跟原生SeekBar一致
- 可以自定义Thumb大小
- 添加顶部文本显示
实现方法
- TextView + SeekBar组合
大概就是更新SeekBar的时候计算当前进度百分比 * 宽度得到偏移量,设置TextView距离左边的偏移量。可以实现(话说一开始就是这样做的),就是要调来调去代码总有自己的想法,而且效果不是那么令人满意。
- 自定义View
这不是废话吗~
实现思路
SeekBar可以通过progressDrawable来设置background、progress、secondProgress,就想要尽量跟原生SeekBar保持用法上一致,即自定义的SeekBar也要可以设置progressDrawable,大概看了SeekBar源码,发现SeekBar是在ProgressBar的基础上多画了个Thumb~~
那直接继承ProgressBar,再多画个Thumb和文本就行了
实际上没搞成~
——————————
那就只能自定义了,又粗略看了下ProgressBar源码,主要是看如何去画background、progress这些,于是发现了这个
难道Drawable也可以直接draw(原谅我基础薄弱),果断百度了一波:先用drawable.setBounds()设置大小
然后drawable.draw(canvas)
还有这种操作。。。这下background知道如何操作了,脑子里马上就冒出progress操作法:当前进度 / 总进度获取百分比,动态设置drawable.setBounds()
想想不是这么个事:progress是按进度慢慢显示完,也就是可以控制显示多少,而不是控制整个progress的长度,于是继续翻~
发现了这个:d = (LayerDrawable) d.findDrawableByLayerId(id)
感觉有什么东西要出来了~,又发现到自定义drawable里
background和progress的drawable类型是不同的(之前拷贝粘贴没注意过),progress多了个<clip>可不就ClipDrawable吗!那不是可以这样:(ClipDrawable) d.findDrawableByLayerId(android.id.progress)
————————
综上:
- 画background: Drawable.setBounds()设置位置大小,然后draw(canvas)
- 画progress : ClipDrawable.setLevel()
知道了这些之后就简单了
具体实现
实际实现的时候应该要为两边的预留空间来处理Thumb和Text,即实际的控件范围应该是这样的
于是background的范围应该是这样计算(progress同样)
thumb的初始范围是这样:
当然Thumb要动起来,left还要加上进度占比
总体流程如下:
最后处理手势:
- 判断手势落点是否在Thumb范围内
- 滑动时用x坐标实时计算占总宽度的百分比 * maxProgress来设置progress
/**
* 判断落点是否在thumb范围内
*
* @param event
* @return
*/
private boolean inThumbBound(MotionEvent event) {
//(当前进度+bgLeft) +- thumbWidth / 2
float center = mBackgroundLeft + getFraction(mProgress) * mBackgroundWidth;
float x = event.getX();
return x >= center - mThumbWith / 2 && x <= center + mThumbWith / 2;
}
/**
* 判断滑动是否在SeekBar范围内
*
* @param event
* @return
*/
private boolean checkProgressBound(MotionEvent event) {
float x = event.getX();
return x >= (mBackgroundLeft - mThumbLeft) && x <= mBackgroundLeft + mBackgroundWidth + mThumbRight;
}
private int getProgress(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float availableWidth = mBackgroundWidth;
float fraction = (x - mBackgroundLeft) / availableWidth;
fraction = Math.max(0, fraction);
fraction = Math.min(fraction, 1);
return (int) (fraction * getMax());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (inThumbBound(event)) {
mIsDragging = true;
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsDragging && checkProgressBound(event)) {
setProgress(getProgress(event));
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mOnSeekBarChangeListener != null && mIsDragging) {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
mIsDragging = false;
break;
}
return super.onTouchEvent(event);
}
———————— Over ——————————