SeekBar的样式修改及分析

今天在看视频的时候感觉视频底部的SeekBar样式还不错,所以就亲手实践改了一下系统默认的样式。

这里我先做了一个SeekBar的结构分析图:


这里写图片描述


先看一下效果,不是很炫酷,上面的是我的,下面的是系统原生的样式,没有人家的那么迷你,只想让你看的更清楚


这里写图片描述

这里写图片描述

首先我去系统的values.xml搜索SeekBar,搜索结果如下


<style name="Base.Widget.AppCompat.SeekBar" parent="android:Widget">
        <item name="android:indeterminateOnly">false</item>
        <item name="android:progressDrawable">@drawable/abc_seekbar_track_material</item>
        <item name="android:indeterminateDrawable">@drawable/abc_seekbar_track_material</item>
        <item name="android:thumb">@drawable/abc_seekbar_thumb_material</item>
        <item name="android:focusable">true</item>
        <item name="android:paddingLeft">16dip</item>
        <item name="android:paddingRight">16dip</item>
    </style>

方式一


我们只要修改android:progressDrawable和android:thumb这个就可以达到自己想要的效果,所以将这两个属性Copy出来,在我们自己的styles.xml中创建如下style即可:

<style name="jrt_seekbar_style" parent="Base.Widget.AppCompat.SeekBar">
        <item name="android:progressDrawable">@drawable/my_seekbar_style</item>
        <item name="android:thumb">@drawable/seekbar_thumb</item>
    </style>

接下来开启照猫画虎模式


my_seekbar_style这个drawable的写法我们去参靠系统的abc_seekbar_track_material.xml,直接一个copy在我们的drawable目录下,然后改成我们自己的文件名字,如:my_seekbar_style.xml;具体如下:

<?xml version="1.0" encoding="utf-8"?>
<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中使用。如下:

<SeekBar
        android:id="@+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的源码


这里写图片描述

由上面的类关系集成图可以看出,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);
        }
    }

总结

最后那部分不是在做源码解析,我只是发表个人观点,如果有什么不对的可以,在下面留言,相互学习。知识在于探索,更在于创新。还是老话,举一反三。

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值