自定義OverlapViewpager

寫在前面

很久之前寫的一個(gè)控件了,Overlap,顧名思義,是一個(gè)page可以重疊的的ViewPager,一共有左中右三頁,中間頁固定,左右兩邊的page分別可以劃出重疊到中間。話不多說,下面開始。

先看效果

向右劃
right_slide.gif
向左劃
left_slide.gif

實(shí)現(xiàn)思路

自定義一個(gè)ViewGroup,往其中添加3個(gè)child;按照左中右的順序,在onLayout中,設(shè)置好children的位置;在從左往右劃動(dòng)的時(shí)候,找到被drag的child,通過Scroller,根據(jù)手指的滑動(dòng)距離,把對(duì)應(yīng)的child移動(dòng)相應(yīng)的位置;有對(duì)滑動(dòng)速度的檢測(cè),超過閾值就觸發(fā)完全覆蓋,以及對(duì)松手時(shí)滑動(dòng)距離的閾值檢測(cè),判斷是還原還是覆蓋。見下圖:

layout.png

1.一些重要的常量

    public static final int PAGE_LEFT = -1; //left page 標(biāo)識(shí)
    
    public static final int PAGE_MIDDLE = 0;//middle page 標(biāo)識(shí)
    
    public static final int PAGE_RIGHT = 1;//right page 標(biāo)識(shí)
    
    private static final int PAGE_COUNT = 3;// page數(shù)量
    
    public static final int VIEW_INDEX_LEFT = 0;//left child 下標(biāo)
    
    public static final int VIEW_INDEX_MIDDLE = 1;//middle child 下標(biāo)
    
    public static final int VIEW_INDEX_RIGHT = 2;//middle child 下標(biāo)
    
    private int downX = 0;
    
    private int mCurrentFront = PAGE_MIDDLE;//默認(rèn)當(dāng)前頁為middle
    
    private int mCurrentMovedItemIndex = Integer.MIN_VALUE;

2.添加child

public void addViews(View[] views) {
    removeAllViews();
    this.views = views;
    int seq[] = { 1, 0, 2 };
    for (int i : seq) {
        if (views[i] != null) {
            addView(views[i], new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        }
    }
}

對(duì)應(yīng)在onLayout時(shí)注意順序,代碼如下:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int height = b - t;
    int width = r - l;
    //根據(jù)當(dāng)前頁不同,應(yīng)該按照不同順序layout
    if (mCurrentFront == PAGE_MIDDLE) {
        for (int i = 0; i < 3; ++i) {
            View viewItem = views[i];
            if (viewItem != null) {
                viewItem.layout(0 - width + i * width, 0, i * width, height);
            }
        }
    } else if (mCurrentFront == PAGE_LEFT) {
        for (int i = 0; i < PAGE_COUNT; i++) {
            View viewItem = views[i];
            if (viewItem != null) {
                if (i == VIEW_INDEX_MIDDLE) {
                    viewItem.layout(0, 0, width, height);
                } else if (i == VIEW_INDEX_RIGHT) {
                    viewItem.layout(width, 0, i * width, height);
                } else if (i == VIEW_INDEX_LEFT) {
                    viewItem.layout(0, 0, width, height);
                }
            }
        }
    } else if (mCurrentFront == PAGE_RIGHT) {
        for (int i = 0; i < PAGE_COUNT; i++) {
            View viewItem = views[i];
            if (viewItem != null) {
                if (i == VIEW_INDEX_MIDDLE) {
                    viewItem.layout(0, 0, width, height);
                } else if (i == VIEW_INDEX_RIGHT) {
                    viewItem.layout(0, 0, width, height);
                } else if (i == VIEW_INDEX_LEFT) {
                    viewItem.layout(0 - width, 0, 0, height);
                }
            }
        }
    }
}

3.滑動(dòng)

當(dāng)前頁是中間頁時(shí),在onTouchEvent中根據(jù)x軸移動(dòng)數(shù)據(jù)判斷滑動(dòng)方向、滑動(dòng)的child以及計(jì)算出offset,對(duì)應(yīng)的view再進(jìn)行移動(dòng)。

...
int deltaX = (int) event.getX() - downX;
int offset = (int) event.getX() - lastPointX;
lastPointX = (int) event.getX();
switch (mCurrentFront) {
    case PAGE_LEFT:
    ...
    case PAGE_MIDDLE:
        if (deltaX > 0) {
            mCurrentMovedItemIndex = VIEW_INDEX_LEFT;
        } else if (deltaX < 0) {
            mCurrentMovedItemIndex = VIEW_INDEX_RIGHT;
        } else {
            break;
        }
        dragLayout = getDragView(mCurrentMovedItemIndex);
        if (dragLayout != null) {
            if (mCurrentMovedItemIndex == VIEW_INDEX_LEFT) {
                if (dragLayout.getRight() >= 0 && dragLayout.getRight() <= getWidth()) {
                    dragLayout.offsetLeftAndRight(offset);
                    invalidate();
                }
            } else if (mCurrentMovedItemIndex == VIEW_INDEX_RIGHT) {
                if (dragLayout.getLeft() >= 0 && dragLayout.getLeft() <= getWidth()) {
                    dragLayout.offsetLeftAndRight(offset);
                    invalidate();
                }
            }
        }
        checkToResetLastMovedView(mCurrentMovedItemIndex);
        break;
    case PAGE_RIGHT:
        ...
}

寫到這,主要的內(nèi)容就完成了,可以對(duì)這個(gè)自定義的ViewPager中做很多拓展,在公司的一些功能開發(fā)中已用到,就不贅述了。

代碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容