寫在前面
很久之前寫的一個(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ā)中已用到,就不贅述了。