HorizontalVerticalViewPager實現水平垂直都可滑動
水平垂直都可滑動的HorizontalVerticalViewPager實現
自定義一個可以在垂直方向上滑動的ViewPager,同時支援水平和垂直方向手動切換和自動切換(根據手勢判斷),垂直方向的實現通過PageTransformer來完成,自動切換功能則涉及到手勢的相關內容。以下是內容簡介:
-
垂直滑動的ViewPager實現思路
-
PageTransformer的使用
-
手勢事件的分發和處理
垂直滑動的ViewPager實現思路
Android的相容包support-v4提供的ViewPager預設是水平滑動切換的,但是,想要實現垂直滑動,可行的方案還是不少的,下面舉幾個例子:
-
直接拷貝ViewPager原始碼,把所有涉及到的水平控制的引數,調整為垂直;
-
旋轉ViewPager的item的角度;
-
設定頁面切換動畫PageTransformer;
當然,還有很多其他的方式來實現,我這裡使用的就是比較簡單的PageTransformer,簡單,是因為只需要給水平的ViewPager設定一個PageTransformer就能實現,不用自定義ViewPager。
PageTransformer的使用
關於PageTransformer首先要知道的是,這是一個控制ViewPager頁面滑動動畫的一個類,要給頁面設定各種滑入和滑出的動畫,需要實現PageTransformer介面,實現裡面的transformPage(View page,int position)方法,做動畫相關的操作。
先來看看官方的Demo:

Picture
對應的程式碼如下:
public class DepthPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.75f; public void transformPage(View view, float position) { int pageWidth = view.getWidth(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 0) { // [-1,0] // Use the default slide transition when moving to the left page view.setAlpha(1); view.setTranslationX(0); view.setScaleX(1); view.setScaleY(1); } else if (position <= 1) { // (0,1] // Fade the page out. view.setAlpha(1 - position); // Counteract the default slide transition view.setTranslationX(pageWidth * -position); // Scale the page down (between MIN_SCALE and 1) float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)); view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } } }

Picture
對應的PageTransformer實現:
public class ZoomOutPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.85f; private static final float MIN_ALPHA = 0.5f; public void transformPage(View view, float position) { int pageWidth = view.getWidth(); int pageHeight = view.getHeight(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 1) { // [-1,1] // Modify the default slide transition to shrink the page as well float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); float vertMargin = pageHeight * (1 - scaleFactor) / 2; float horzMargin = pageWidth * (1 - scaleFactor) / 2; if (position < 0) { view.setTranslationX(horzMargin - vertMargin / 2); } else { view.setTranslationX(-horzMargin + vertMargin / 2); } // Scale the page down (between MIN_SCALE and 1) view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); // Fade the page relative to its size. view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA)); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } } }
關於Demo的具體解讀,參見官網示例。
下面是垂直滑動效果:

Picture
對應的PageTransformer實現:
private class VerticalPageTransformer implements ViewPager.PageTransformer { @Override public void transformPage(View view, float position) { if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 1) { // [-1,1] view.setAlpha(1); // Counteract the default slide transition view.setTranslationX(view.getWidth() * -position); //set Y position to swipe in from top float yPosition = position * view.getHeight(); view.setTranslationY(yPosition); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } } }
到這裡其實就已經可以上下滑動了,但是手勢卻還是左右滑動,還需要再做一點改動。把左右滑動的手勢變換為上下滑動。
/** * Swaps the X and Y coordinates of your touch event. */ private MotionEvent swapXY(MotionEvent ev) { float width = getWidth(); float height = getHeight(); float newX = (ev.getY() / height) * width; float newY = (ev.getX() / width) * height; ev.setLocation(newX, newY); return ev; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (isVertical) { boolean intercepted = super.onInterceptTouchEvent(swapXY(ev)); swapXY(ev); // return touch coordinates to original reference frame for any child views return intercepted; } else { return super.onInterceptTouchEvent(ev); } } @Override public boolean onTouchEvent(MotionEvent ev) { if (isVertical) { return super.onTouchEvent(swapXY(ev)); } else { return super.onTouchEvent(ev); } }
效果如下:

Picture
到這裡,一個垂直滾動的ViewPager已經實現了。
記住,別忘了把實現的PageTransformer設定給ViewPager:
setPageTransformer(true, new HorizontalVerticalPageTransformer());
關於PageTransformer更多的內容,還可以看看CSDN部落格專家們的解讀:
手勢事件的分發和處理
現在,我要做的是,既可以水平滑動又可以垂直滑動,完全根據手指滑動的方向自動的切換滑動方向,通過按鈕手動切換的這裡就不細說了,相信看完自動切換的細節,手動的也就不是什麼問題了。
這裡的手勢,監聽的是ViewPager的item裡面的ImageView控制元件的,所以,實現一個OnGestureListener,然後,在ImageView的touch事件交給GestureDetector來處理,看看OnGestureListener的程式碼:
private class GestureListener implements GestureDetector.OnGestureListener { @Override public boolean onDown(MotionEvent e) { Log.i(getClass().getName(), "onDown-----" + getActionName(e.getAction())); return false; } @Override public void onShowPress(MotionEvent e) { Log.i(getClass().getName(), "onShowPress-----" + getActionName(e.getAction())); } @Override public boolean onSingleTapUp(MotionEvent e) { Log.i(getClass().getName(), "onSingleTapUp-----" + getActionName(e.getAction())); return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.i(getClass().getName(), "onScroll-----" + getActionName(e2.getAction()) + ",(" + e1.getX() + "," + e1.getY() + ") ,(" + e2.getX() + "," + e2.getY() + ")"); return false; } @Override public void onLongPress(MotionEvent e) { Log.i(getClass().getName(), "onLongPress-----" + getActionName(e.getAction())); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.i(getClass().getName(), "onFling-----" + getActionName(e2.getAction()) + ",(" + e1.getX() + "," + e1.getY() + ") ,(" + e2.getX() + "," + e2.getY() + ")"); float detly_x = e1.getX() - e2.getX(); float detly_y = e1.getY() - e2.getY(); if (Math.abs(detly_x) > Math.abs(detly_y)) {// 水平方向滑動 int offsetPosition = 0; if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE) { offsetPosition = 1; Log.i(getClass().getName(), "onFling----- 向左滑動"); } else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE) { offsetPosition = -1; Log.i(getClass().getName(), "onFling----- 向右滑動"); } mOnTouchListener.onHorizontalFling(offsetPosition); } else {// 垂直方向滑動 int offsetPosition = 0; if (e1.getY() - e2.getY() > FLING_MIN_DISTANCE) { offsetPosition = 1; Log.i(getClass().getName(), "onFling----- 向上滑動"); } else if (e2.getY() - e1.getY() > FLING_MIN_DISTANCE) { offsetPosition = -1; Log.i(getClass().getName(), "onFling----- 向下滑動"); } mOnTouchListener.onVerticalFling(offsetPosition); } return false; } private String getActionName(int action) { String name = ""; switch (action) { case MotionEvent.ACTION_DOWN: { name = "ACTION_DOWN"; break; } case MotionEvent.ACTION_MOVE: { name = "ACTION_MOVE"; break; } case MotionEvent.ACTION_UP: { name = "ACTION_UP"; break; } default: break; } return name; } }
設定給ImageView:
final GestureDetector detector = new GestureDetector(context, new GestureListener()); iv.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { detector.onTouchEvent(event); return true; } });
當然,PageTransformer也要進行改造:
private class HorizontalVerticalPageTransformer implements PageTransformer { private static final float MIN_SCALE = 0.75f; @Override public void transformPage(View page, float position) { if (isVertical) { if (position < -1) { page.setAlpha(0); } else if (position <= 1) { page.setAlpha(1); // Counteract the default slide transition float xPosition = page.getWidth() * -position; page.setTranslationX(xPosition); //set Y position to swipe in from top float yPosition = position * page.getHeight(); page.setTranslationY(yPosition); } else { page.setAlpha(0); } } else { int pageWidth = page.getWidth(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. page.setAlpha(0); } else if (position <= 0) { // [-1,0] // Use the default slide transition when moving to the left page page.setAlpha(1); page.setTranslationX(0); page.setScaleX(1); page.setScaleY(1); } else if (position <= 1) { // (0,1] // Fade the page out. page.setAlpha(1 - position); // Counteract the default slide transition page.setTranslationX(pageWidth * -position); page.setTranslationY(0); // Scale the page down (between MIN_SCALE and 1) float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)); page.setScaleX(scaleFactor); page.setScaleY(scaleFactor); } else { // (1,+Infinity] // This page is way off-screen to the right. page.setAlpha(0); } } } }
再看看效果是怎麼樣的吧:

Picture