如何获得一个垂直的SeekBar

小记

在开发”上帝模式”的一个新功能中偶然需要用到一个垂直的SeekBar,一开始想得比较简单系统应该有提供这样垂直的SeekBar但是翻了一下SDK并没有于是上网搜索,查找下来无非这两种:

  1. 使用原生的SeekBar旋转90度
  2. 自定义SeekBaronDraw方法和onMeasure方法中旋转画布并重新计算控件大小

那为什么有两种呢让我们来分别分析一下这两个方案的优缺点:

  • 第一种使用原生的SeekBar最简单但是旋转后的宽高就不对了,实际上的宽是旋转后的高这也导致如果我们要增加SeekBar的高度就需要修改宽度但是会导致布局错乱以下是旋转前后对比:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <SeekBar
    android:id="@+id/seekbar1"
    android:layout_width="120dp"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:clickable="true"
    android:layoutDirection="rtl"/>

    <SeekBar
    android:id="@+id/seekbar2"
    android:layout_width="120dp"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:clickable="true"
    android:layoutDirection="rtl"
    android:rotation="90"/>

可以看到旋转后的SeekBar实际上控件位置并未改变,这会导致布局有问题, 因此有了第二种方案

  • 第二种自定义SeekBar这种方式就是为了解决第一种方式会导致控件实际布局大小不一致的情况,核心的逻辑也是旋转画布并且重新计算控件大小

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    public class VerticalSeekBar extends SeekBar {

    public VerticalSeekBar(Context context) {
    super(context);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(h, w, oldh, oldw);
    }

    @Override
    public synchronized void setProgress(int progress) // it is necessary for calling setProgress on click of a button
    {
    super.setProgress(progress);
    onSizeChanged(getWidth(), getHeight(), 0, 0);
    }
    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(heightMeasureSpec, widthMeasureSpec);
    setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
    c.rotate(-90);
    c.translate(-getHeight(), 0);

    super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) {
    return false;
    }

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
    case MotionEvent.ACTION_MOVE:
    case MotionEvent.ACTION_UP:
    setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
    onSizeChanged(getWidth(), getHeight(), 0, 0);
    break;

    case MotionEvent.ACTION_CANCEL:
    break;
    }
    return true;
    }
    }

    问题至此看起来已解决,不过我还是不想自定义SeekBar因为看起来比较麻烦但是有什么办法可以解决原生SeekBar大小的问题呢?此时我想起来系统的音量调节面板很好奇它是怎么实现的于是开始源码的探索之路

    我们要找到音量面板得先找到它的的代码因为平时调节音量这个窗口都是浮动在其他窗口上的因此可以断定是个悬浮窗,那我们先用dumpsyswindow信息拿出来看一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $ adb shell dumpsys window
    ...
    WINDOW MANAGER ANIMATOR STATE (dumpsys window animator)
    DisplayContentsAnimator #0:
    Window #0: WindowStateAnimator{6d30f com.android.systemui.ImageWallpaper}
    Window #1: WindowStateAnimator{400acfc com.android.settings/com.android.settings.applications.InstalledAppDetails}
    Window #2: WindowStateAnimator{fe69f85 com.android.chrome/com.google.android.apps.chrome.Main}
    Window #3: WindowStateAnimator{8607f21 com.android.launcher3/com.android.a1launcher.AndroidOneLauncher}
    Window #4: WindowStateAnimator{faaa1da com.android.launcher3/com.android.a1launcher.AndroidOneLauncher}
    Window #5: WindowStateAnimator{193860b com.mjar.test/com.mjar.test.MainActivity}
    Window #6: WindowStateAnimator{6299ce8 com.iapgame.ia2048/com.example.jrsen.testapp.MainActivity}
    Window #7: WindowStateAnimator{f6af01 DockedStackDivider}
    Window #8: WindowStateAnimator{e2101a6 AssistPreviewPanel}
    Window #9: WindowStateAnimator{f2b29d2 StatusBar}
    Window #10: WindowStateAnimator{ef277e7 VolumeDialogImpl}
    Window #11: WindowStateAnimator{68afaa3 PointerLocation}
    ...

    可以看到有一个很可疑的窗口 Window #10: WindowStateAnimator{ef277e7 VolumeDialogImpl}名字看上去跟音量相关应该就是它没跑了,拿着这个名字到Android源码中搜索在frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java 中找到了它,跟了一下代码找到了加载布局的相关代码片段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private void initRow(final VolumeRow row, final int stream, int iconRes, int iconMuteRes,
    boolean important, boolean defaultStream) {
    row.stream = stream;
    row.iconRes = iconRes;
    row.iconMuteRes = iconMuteRes;
    row.important = important;
    row.defaultStream = defaultStream;
    row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
    ...
    }

    找到frameworks/base/packages/SystemUI/res/layout/volume_dialog_row.xml对应的布局文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ...
    <FrameLayout
    android:id="@+id/volume_row_slider_frame"
    android:layout_width="match_parent"
    android:layout_marginTop="@dimen/volume_dialog_slider_margin_top"
    android:layout_marginBottom="@dimen/volume_dialog_slider_margin_bottom"
    android:layoutDirection="rtl"
    android:layout_height="@dimen/volume_dialog_slider_height">
    <SeekBar
    android:id="@+id/volume_row_slider"
    android:clickable="true"
    android:layout_width="@dimen/volume_dialog_slider_height"
    android:layout_height="match_parent"
    android:layoutDirection="rtl"
    android:layout_gravity="center"
    android:rotation="90" />
    ...

    到这里问题就找到答案了系统也是用的原生的SeekBar只不过在外层包裹了一个FrameLayout用于裁切SeekBar的实际大小,实在是秒啊!

总结

求知若渴遇到不懂的多研究研究源码总能在里面找到惊喜

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2015-2024 Kaisar
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信