最近的需求中结榄,需要用到一個橫向、豎向同時可滾動的 ViewPager囤捻,記住臼朗,是橫向、豎向同時滾動蝎土,不是橫豎切換视哑。我想了想,難點在于豎向誊涯。對于豎向的 ViewPager挡毅,我似乎記得 Jake Wharton 大神寫過一個叫 DirectionalViewPager 的框架,它基本上算是在 ViewPager 源碼上改的暴构,但效果欠佳且早已沒人維護跪呈,所以不予采用。
過了幾秒丹壕,不知怎么的庆械,腦子里突然閃現(xiàn)了一個想法:要使得 ViewPager 豎向滑動,把 Touch 事件交換一下不就得了菌赖,也就是將 MotionEvent 的 x 坐標轉換成 y 坐標缭乘,將 y 坐標轉換成 x 坐標。這樣,從下往上滑就轉換成了從右往左滑堕绩。而從右往左滑時策幼, ViewPager 會切換到下一頁。此時奴紧,只要給 ViewPager 設置一個豎向切換的 PageTransfromer特姐,就成了一個豎向的 ViewPager 了。
我激動了一小會兒黍氮,正躍躍欲試唐含,但懶癌又犯了,心想沫浆,“我能想到捷枯,估計別人早想到了”,于是還是打算先在 GitHub 上找找专执,看有沒有基于同樣思路的代碼淮捆。果不其然,有個日本伙計 10 個月前就搞出來了本股,代碼很短(我做了精簡)攀痊,大家膜拜下吧:
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
private MotionEvent swapTouchEvent(MotionEvent event) {
float width = getWidth();
float height = getHeight();
event.setLocation((event.getY() / height) * width, (event.getX() / width) * height);
return event;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return super.onInterceptTouchEvent(swapTouchEvent(MotionEvent.obtain(event)));
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapTouchEvent(MotionEvent.obtain(ev)));
}
}
嗯,實在是妙啊拄显。不過還需要在外部設置一下 overScrollMode 和 PageTransfromer苟径,以免看出破綻:
mVerticalViewPager.setPageTransformer(true, new VerticalTransformer());
mVerticalViewPager.setOverScrollMode(OVER_SCROLL_NEVER);
GitHub 地址 -> VerticalViewPager
雙向的 ViewPager
這里的雙向不是指一個 ViewPager 既可以橫向切換,也可以豎向切換(如果你想要凿叠,把上面的代碼稍作修改即可)涩笤,而是一個橫向的 ViewPager 里,每一頁都是一個 VerticalViewPager盒件,你可以理解為:外面的 ViewPager 用于切換分類蹬碧,里面的 ViewPager 用于切換分類中的 Item。
如果你簡單的使用 ViewPager 嵌套 VerticalViewPager炒刁,實際的效果是恩沽,里面的 ViewPager 可豎向切換,但外面的 ViewPager 不能橫向切換翔始。原因是里面的 ViewPager 將事件消耗掉了罗心,即便里面的 ViewPager 沒有可橫向滾動的控件(HorizontalScrollView、橫向 RecyclerView 等)城瞎。解決辦法是重寫外面的 ViewPager 的 onInterceptTouchEvent 方法渤闷,如果檢測到橫向滾動,則將事件攔截脖镀。代碼如下:
public class HorizontalViewPager extends ViewPager {
private float mDownX;
private float mDownY;
private float mTouchSlop;
public HorizontalViewPager(Context context) {
super(context);
init(context);
}
public HorizontalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = super.onInterceptTouchEvent(event);
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = x;
mDownY = y;
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - mDownX);
float dy = Math.abs(y - mDownY);
if (!intercept && dx > mTouchSlop && dx * 0.5f > dy) {
intercept = true;
}
break;
default:
break;
}
return intercept;
}
}
當然飒箭,這段代碼只適用于里面的 ViewPager 不含可橫向滾動的控件的情況,如果有,則處理起來就相對麻煩一些弦蹂,大致的思路是在 onInterceptTouchEvent 里肩碟,先將 move 事件 dispatch 給當前頁。再根據(jù) (!disallowIntercept && mTouchSlop && dx * 0.5f > dy) 決定是否攔截事件凸椿。有興趣的同學可以試一下削祈。
最后,附上效果圖:
好了脑漫,就分享這些髓抑。
推廣:一鍵直達我的最新開源項目 MagicIndicator。