今天在看视频的时候感觉视频底部的SeekBar样式还不错,所以就亲手实践改了一下系统默认的样式。
我们只要修改android:progressDrawable和android:thumb这个就可以达到自己想要的效果,所以将这两个属性Copy出来,在我们自己的styles.xml中创建如下style即可:
接下来开启照猫画虎模式
my_seekbar_style这个drawable的写法我们去参靠系统的abc_seekbar_track_material.xml,直接一个copy在我们的drawable目录下,然后改成我们自己的文件名字,如:my_seekbar_style.xml;具体如下:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background"
android:drawable="@drawable/seekbar_bg"/>
<item android:id="@android:id/secondaryProgress">
<scale android:scaleWidth="100%">
<selector>
<item android:state_enabled="false">
<color android:color="@android:color/transparent"/>
item>
<item android:drawable="@drawable/seekbar_progress_2"/>
selector>
scale>
item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<selector>
<item android:state_enabled="false">
<color android:color="@android:color/transparent"/>
item>
<item android:drawable="@drawable/seekbar_progress"/>
selector>
scale>
item>
layer-list>
其实这些代码都是google工程师写好的,我们只要把对应的drawable换成我们自己的即可。在layout应用的时候直接给Seekbar设置style属性即可;
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="140dp"
style="@style/jrt_seekbar_style"
/>
我们不用在styles.xml中设置属性,将my_seekbar_style.xml和seekbar_thumb两个资源准备好了直接在layout中使用。如下:
"@+id/myseekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:progress="30"
android:thumb="@drawable/seekbar_thumb"
android:progressDrawable="@drawable/my_seekbar_style"
/>
做过开发的都知道这两种使用方式的要根据具体的场景而定,主要取决与你的SeekBar使用频率,如果只用一次就用方式二,如果多次,并且全局都使用同样的Seekbar那就用方式一,这样更体现了封装抽取,便于项目的维护和管理。其实应该还有方式三,在java代码中设置其属性。接下来说说踩多的坑,程序员都是踩着坑成长的。
my_seekbar_style.xml在写的时候一定要按系统android:id的顺序来,不要随便改变item的顺序,必须是background->secondaryProgress->progress;我一开始没有copy系统代码,直接手写就掉坑里了把id顺序写成background->-progress>secondaryProgress;结果就成这样了,如图:
蓝色本来是progress应该随拖动的按钮变化而变化的,结果成了secondaryProgress缓冲进度了,刚好两个交换位置了,层级关系乱了。如果顺序写成secondaryProgress->progress->background那就更抽象了,直接把背景放到最顶层了,其他都被这挡了,如下图:
这个错误犯得好,让我明白了SeekBar是完全按照background->secondaryProgress->progress这个从低往高的层级关系显示UI的
由上面的类关系集成图可以看出,SeekBar继承 AbsSeekBar,所以SeekBar本身没做什么工作,我们用到的就是它的一个内部接口回调OnSeekBarChangeListener,通过这个回调操作SeekBar不同状态下的操作。
public interface OnSeekBarChangeListener {
/**
* Notification that the progress level has changed. Clients can use the fromUser parameter
* to distinguish user-initiated changes from those that occurred programmatically.
*
* @param seekBar The SeekBar whose progress has changed
* @param progress The current progress level. This will be in the range 0..max where max
* was set by {@link ProgressBar#setMax(int)}. (The default value for max is 100.)
* @param fromUser True if the progress change was initiated by the user.
*/
void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser);
/**
* Notification that the user has started a touch gesture. Clients may want to use this
* to disable advancing the seekbar.
* @param seekBar The SeekBar in which the touch gesture began
*/
void onStartTrackingTouch(SeekBar seekBar);
/**
* Notification that the user has finished a touch gesture. Clients may want to use this
* to re-enable advancing the seekbar.
* @param seekBar The SeekBar in which the touch gesture began
*/
void onStopTrackingTouch(SeekBar seekBar);
}
再看看AbsSeekBar继承于ProgressBar,通过下面AbsSeekBar源码中drawThumb(canvas)可以看出AbsSeekBar是在ProgressBar基础上多绘制了一个thumb,也就多了一个手指拖动进度的功能。所以SeekBar本身没有参与UI样式的修改工作,我们修改的progressDrawable是ProgressBar的,修改的thumb是AbsSeekBar的。还有一个就是SeekBar的子类AppCompatSeekBar,AppCompatSeekBar内部封装了一个AppCompatSeekBarHelper就是便于我们操作的,具体自己看源码。
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawThumb(canvas);
}
@Override
void drawTrack(Canvas canvas) {
final Drawable thumbDrawable = mThumb;
if (thumbDrawable != null && mSplitTrack) {
final Insets insets = thumbDrawable.getOpticalInsets();
final Rect tempRect = mTempRect;
thumbDrawable.copyBounds(tempRect);
tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop);
tempRect.left += insets.left;
tempRect.right -= insets.right;
final int saveCount = canvas.save();
canvas.clipRect(tempRect, Op.DIFFERENCE);
super.drawTrack(canvas);
drawTickMarks(canvas);
canvas.restoreToCount(saveCount);
} else {
super.drawTrack(canvas);
drawTickMarks(canvas);
}
}
最后那部分不是在做源码解析,我只是发表个人观点,如果有什么不对的可以,在下面留言,相互学习。知识在于探索,更在于创新。还是老话,举一反三。