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

作者: 付十一v | 来源:发表于2019-04-28 19:53 被阅读37次

    seekbar的样式层出不穷,下面这篇文章也只是讲述了其中的一种,自定义view实现从中间向左右滑动的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.

    相关文章

      网友评论

        本文标题:自定义中间向左右滑动的seekbar

        本文链接:https://www.haomeiwen.com/subject/szdzgqtx.html