自定义中间向左右滑动的seekbar

seekbar的样式层出不穷,下面这篇文章也只是讲述了其中的一种,自定义view实现从中间向左右滑动的seekbar。如图

《自定义中间向左右滑动的seekbar》 seekbar.png

从上面的图片看得出该seekbar的构成分为底部默认的背景条,开始滑动的红色背景条和中间的圆形thumb。那么就按照该结构开始编写代码。
首先我们来看一下所需要的全局变量,以便后面代码的阅读。

 //    中间的拖动bar
    private Drawable mThumb;
    //    默认的背景
    private Drawable mDefaultBack;
    //    滑动后的背景
    private Drawable mSlideBack;
    
    private int mSeekBarWidth;
    private int mSeekBarHeight;
    private int mThumbWidth;
    private int mThumbHeight;
    //     thumb的中心位置
    private int mThumbCenterPosition = 0;
    //     能滑动的总长度
    private int mSlideTotalDistance = 0;

代码中也都注释了,不多说明。我们接着往下看。进行初始化操作。

private void init() {
        mThumb = getResources().getDrawable(R.drawable.thumb);
        mDefaultBack = getResources().getDrawable(R.drawable.back_default);
        mSlideBack = getResources().getDrawable(R.drawable.back_slide);

        mSeekBarHeight = mDefaultBack.getIntrinsicHeight();
        mSeekBarWidth = mDefaultBack.getIntrinsicWidth();

        mThumbHeight = mThumb.getIntrinsicHeight();
        mThumbWidth = mThumb.getIntrinsicWidth();
    }

上述代码中,将中间圆形mThumb,默认背景mDefaultBack,滑动红色背景mSlideBack进行相应的初始化。为了后面mThumb的滑动以及位置的变化,这里需要以默认背景条来作为整条seekbar的宽度和高度,以及获取mThumb的宽度和高度。
对于view的绘制一般分为onMeasure,onLayout,onDraw三个步骤,
-onMeasure():测量自己的大小,为正式布局提供建议;
-onLayout():使用layout()函数对所有子控件布局;
-onDraw():根据布局的位置绘图;
那么先进入第一步对view进行测量。

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = measureWidth(widthMeasureSpec);
        mSeekBarWidth = width;
        mThumbCenterPosition = width / 2;
        mSlideTotalDistance = width - mThumbWidth;
        setMeasuredDimension(width, mThumbHeight);
    }

    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.AT_MOST) {

        } else if (specMode == MeasureSpec.EXACTLY) {

        }
        return specSize;
    }

上述代码中mThumbCenterPosition指的Thumb的圆心坐标,mSlideTotalDistance为能滑动的总长度。这里最为重要的地方为圆心坐标的值mThumbCenterPosition = width / 2,一般thumb的位置位于seekbar的起始位置,那么这里就是将thumb设置到seekbar的中点,从这里开始向左向右滑动。
上述是做好了view 的测量,一般来说测量只是你建议的值,真正布局是在onLayout中,在这里呢我把onLayout省略掉了,在onMeasure中做好了处理。那么就接着看看onDraw的重头戏。
按照思路,把绘制的过程分为三个部分,第一部分:绘制默认的背景条,这个很简单

mDefaultBack.setBounds(mThumbWidth / 2, 0, mSeekBarWidth - mThumbWidth / 2, mSeekBarHeight);
        mDefaultBack.draw(canvas);

从第一个thumb的中心点到最后一个thumb的中心点绘制默认的背景条。
第二部分:绘制thumb,按照thumb的宽度进行绘制即可。

mThumb.setBounds(mThumbCenterPosition - mThumbWidth / 2, 0, mThumbCenterPosition + mThumbWidth / 2, mThumbHeight);
        mThumb.draw(canvas);

第三部分:开始随着手势绘制滑动的进度条


        if (mThumbCenterPosition > mSeekBarWidth / 2) {
            mSlideBack.setBounds(mSeekBarWidth / 2, 0, mThumbCenterPosition, mSeekBarHeight);

        } else if (mThumbCenterPosition < mSeekBarWidth / 2) {
            mSlideBack.setBounds(mThumbCenterPosition, 0, mSeekBarWidth / 2, mSeekBarHeight);
        } else {
            mSlideBack.setBounds(mThumbCenterPosition, 0, mSeekBarWidth / 2, mSeekBarHeight);
        }
        mSlideBack.draw(canvas);

咱们来分析下这个代码,因为今天所做的seekbar分为左右两部分,所以根据不同情况做不同处理。首先如果小圆球的中心位于有半部分,那么将绘制的范围设置为(mSeekBarWidth / 2到mThumbCenterPosition)即seekbar中心点到小圆球的中心位置,小圆球的中心位置是随着手势变化的,所以就就形成了动态绘制的效果。
理解了上面的第一段代码,那么接下来的两段就很好理解了。和上面一样,如果小圆球处于左半部分,做相同处理,只不过范围变了罢了。最后一种情况是位于中心位置,就不多说了。最后就将上述不同的情况进行绘制了。
从上面的分析,我们知道了图形是怎么绘制的,我们也知道能动态绘制的重点在于小圆球的中心点,也就是mThumbCenterPosition,那么mThumbCenterPosition是如何随手势获取的呢?
我们带着这个疑问,来看下面的代码:

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mSeekBarChangeListener != null) {

                }
                mFlag = getAreaFlag(event);
                if (mFlag == CLICK_ON_PRESS) {
                    mThumb.setState(STATE_PRESSED);
                    mSeekBarChangeListener.onProgressBefore();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mFlag == CLICK_ON_PRESS) {
                    if (event.getX() < 0 || event.getX() <= mThumbWidth / 2) {
                        mThumbCenterPosition = mThumbWidth / 2;
                    } else if (event.getX() >= mSeekBarWidth - mThumbWidth / 2) {
                        mThumbCenterPosition = mSlideTotalDistance + mThumbWidth / 2;
                    } else {
                        mThumbCenterPosition = (int) event.getX();
                    }
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                mThumb.setState(STATE_NORMAL);
                if (mSeekBarChangeListener != null) {
                    mSeekBarChangeListener.onProgressAfter();
                }
                break;
            default:
                break;
        }
        return true;
    }

从知道随手势变化来动态的获取小圆球的中心值,就能想到要用Touch事件了,上面代码分为down,move和up,我们现在只看move部分,首先获取手势在x方向的值,然后判断是否超出了seekbar的范围,超过左边就将设置为小圆球的半径的长度位置,如果超过了右边就设置为seekbar长度减小圆球半径的位置,这里如果有不清楚的可以在纸上自己画一下图。最后就是处于seekbar范围内,直接就随着手势获取手势的位置,即 mThumbCenterPosition = (int) event.getX();到这里,基本的布局和绘制也就结束了。
如果你想使用seekbar的change事件,可以自己定义一个changeListener,如下:

public interface OnSeekBarChangeListener {
        void onProgressBefore();

        void onProgressChanged(MidThumbSeekBar seekBar, double progress);

        void onProgressAfter();
    }

好了,从中间向左右滑动的seekbar全部绘制完了,最后我们来调用 并运行
xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.fusy.midthumbseekbar.MidThumbSeekBar
        android:id="@+id/seekBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        />

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity implements MidThumbSeekBar.OnSeekBarChangeListener {
    private MidThumbSeekBar mBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mBar = findViewById(R.id.seekBar);
        mBar.setOnSeekBarChangeListener(this);
    }

    @Override
    public void onProgressBefore() {

    }

    @Override
    public void onProgressChanged(MidThumbSeekBar seekBar, double progress) {

    }

    @Override
    public void onProgressAfter() {

    }
}

End.

    原文作者:二气乖张
    原文地址: https://www.jianshu.com/p/a039b48b2930
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞