自定義中間向左右滑動的seekbar

seekbar的樣式層出不窮,下面這篇文章也只是講述了其中的一種,自定義view實現從中間向左右滑動的seekbar。如圖



從上面的圖片看得出該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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章