實現一個可定製化的TabFlowLayout(四) -- 與ViewPager 結合,實現炫酷效果

效果圖

在這裏插入圖片描述

FlowHelper工程源碼

最後就來實現 跟着 viewpager 的效果,如下:
在這裏插入圖片描述
可以看到 ,上面實現了幾個效果:
1、子控件的背景跟着自身大小自動變化
2、背景跟着viewpager的滾動自動滑動
3、當移動到中間,如果後面有多餘的數據,則讓背景保持在中間,內容移動

一、自定義背景

首先,實現一個紅色背景框框;首先,思考一下,在 viewgroup 實現 canvas , 是在 onDraw(Canvas canvas) 繪製,還是在 dispatchDraw(Canvas canvas) 呢?

答案肯定是 dispatchDraw 繪製了,爲什麼呢?這裏解釋幾個概念:

  1. onDraw 繪製內容
    onDraw 爲實際要關心的東西,即所有繪製都在這裏。

  2. dispatchDraw 只對ViewGroup有意義
    dispatchDraw 通常來講,可以解釋成繪製 子 View
    View 繼承drawable,view 組件的繪製會先調用 draw(Canvas canvas) 方法,然後先繪製 Drawable背景,接着纔是調用 onDraw ,然後調用 dispatchDraw方法。dispatchDraw 會分發給組件去繪製。
    不過 View 是沒有子 view 的,所以dispatchDraw對它來說沒意義。

所以,當自定義 ViewGroup 時,加入 ViewGroup 沒有背景,是不會回調 onDraw 方法的,只會回調dispatchDraw,有背景纔會走正常順序。(不信? 你可以把你的 tabflowlayout 背景去掉,在 onDraw 繪製,看看有沒有用)

這樣,當在自定義 ViewGroup 時,比如添加一下斑點,如果不想添加完子 view 之後,斑點消失,只需要在 子 view 的 onDraw 執行完,再執行 dispatchDraw 即可,就是在 ViewGroup的 dispatchDraw 繪製即可。

這樣,我們先拿到,第一個子 view 的大小,確定 rect:

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        View child = getChildAt(0);
        if (child != null) {
            //拿到第一個數據
            MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
            mRect.set(getPaddingLeft()+params.leftMargin,
                    getPaddingTop()+params.topMargin,
                    child.getMeasuredWidth()-params.rightMargin,
                    child.getMeasuredHeight() - params.bottomMargin);
        }
    }

接着在 dispatchDraw 中繪製圓角矩形:

    @Override
    protected void dispatchDraw(Canvas canvas) {
        //繪製一個矩形
        canvas.drawRoundRect(mRect, 10, 10, mPaint);
        super.dispatchDraw(canvas);
    }

效果如下:
在這裏插入圖片描述
接着,怎麼讓這個背景跟着 viewpager 移動呢?

可以從 viewpager 的頁面監聽中拿到 onPageScrolled 方法:

/**
 * This method will be invoked when the current page is scrolled, either as part
 * of a programmatically initiated smooth scroll or a user initiated touch scroll.
 *
 * @param position Position index of the first page currently being displayed.
 *                 Page position+1 will be visible if positionOffset is nonzero.
 * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
 * @param positionOffsetPixels Value in pixels indicating the offset from position.
 */
@UnsupportedAppUsage
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);

三個參數解釋如下:

  • position :當前第一頁的索引,比較有意思的是,當右滑時,position 表示當前頁面,當左滑時,爲當前頁面減1;
  • positionOffset:當前頁面移動的百分比,[0,1]之間;右滑0-1,左滑 1-0;
  • positionOffsetPixels:當前頁面移動的像素

從上面可以看到,我們只需要 position 和 positionOffset 即可,即上一個 左邊爲要移動的偏移量,加上 子 view 的寬度變化即可:
在這裏插入圖片描述

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            /**
             * position 當前第一頁的索引,比較有意思的是,當右滑時,position 表示當前頁面,當左滑時,爲當前頁面減1;
             * positionOffset 當前頁面移動的百分比
             * positionOffsetPixels 當前頁面移動的像素
             */
            if (position < getChildCount() - 1) {
                //上一個view
                final View lastView = getChildAt(position);
                //當前view
                final View curView = getChildAt(position + 1);
                //左邊偏移量
                float left = lastView.getLeft() + positionOffset * (curView.getLeft() - lastView.getLeft());
                //右邊表示寬度變化
                float right = lastView.getRight() + positionOffset * (curView.getRight() - lastView.getRight());
                mRect.left = left;
                mRect.right = right;
                postInvalidate();
          }
}

但移動發現,左邊和右邊都會被滑出,中間也沒滾動,所以,最後代碼改爲:

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            /**
             * position 當前第一頁的索引,比較有意思的是,當右滑時,position 表示當前頁面,當左滑時,爲當前頁面減1;
             * positionOffset 當前頁面移動的百分比
             * positionOffsetPixels 當前頁面移動的像素
             */
            //居中代碼
            float offset = getChildAt(position).getWidth() * positionOffset;
            int scrollX = (int) (getChildAt(position).getLeft() + offset);
            if (position < getChildCount() - 1) {
                //上一個view
                final View lastView = getChildAt(position);
                //當前view
                final View curView = getChildAt(position + 1);
                //左邊偏移量
                float left = lastView.getLeft() + positionOffset * (curView.getLeft() - lastView.getLeft());
                //右邊表示寬度變化
                float right = lastView.getRight() + positionOffset * (curView.getRight() - lastView.getRight());
                mRect.left = left;
                mRect.right = right;
                postInvalidate();

                //超過中間了,讓父控件也跟着移動
                if (scrollX > mScreenWidth / 2 - getPaddingLeft() ) {
                    scrollX -= mScreenWidth / 2 - getPaddingLeft();
                    //有邊界提醒
                    if (scrollX  < mRightBound - mScreenWidth) {
                        scrollTo(scrollX, 0);
                    }
                }else{
                    scrollTo(0,0);

                }
            }

        }

看看效果;
在這裏插入圖片描述
這樣,我們就實現了效果,至於最後要改成什麼樣,就靠大家的自定義想象了;

在子 view 的點擊事件那裏,只需要切 mViewPager.setCurrentItem(position); 就可以了;

二、擴展

上面實現了viewpager 的功能,但我沒有 viewpager 怎麼辦?那肯定也要一個效果呀。
從上面可以看到,只需要子控件的,左邊座標和 寬度變化即可。
那麼,可以使用 ValueAnimator,但 ofFloat 方法只能放一個參數呀;那這個時候,就可以用ObjectAnimator.ofObject() 方法了,然後再繼承 TypeEvaluator 實現一個x,y 都是勻速的插值器就可以了。

好了,思路給你了;如果沒實現出來,可以看看下面這個代碼:

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