水平垂直都可滑動的HorizontalVerticalViewPager實現(xiàn)

水平垂直都可滑動的HorizontalVerticalViewPager實現(xiàn)

自定義一個可以在垂直方向上滑動的ViewPager,同時支持水平和垂直方向手動切換和自動切換(根據(jù)手勢判斷),垂直方向的實現(xiàn)通過PageTransformer來完成,自動切換功能則涉及到手勢的相關(guān)內(nèi)容。以下是內(nèi)容簡介:

  • 垂直滑動的ViewPager實現(xiàn)思路

  • PageTransformer的使用

  • 手勢事件的分發(fā)和處理

垂直滑動的ViewPager實現(xiàn)思路

Android的兼容包support-v4提供的ViewPager默認(rèn)是水平滑動切換的,但是,想要實現(xiàn)垂直滑動,可行的方案還是不少的,下面舉幾個例子:

  1. 直接拷貝ViewPager源碼,把所有涉及到的水平控制的參數(shù),調(diào)整為垂直;
  2. 旋轉(zhuǎn)ViewPager的item的角度;
  3. 設(shè)置頁面切換動畫PageTransformer;

當(dāng)然,還有很多其他的方式來實現(xiàn),我這里使用的就是比較簡單的PageTransformer,簡單,是因為只需要給水平的ViewPager設(shè)置一個PageTransformer就能實現(xiàn),不用自定義ViewPager。

PageTransformer的使用

關(guān)于PageTransformer首先要知道的是,這是一個控制ViewPager頁面滑動動畫的一個類,要給頁面設(shè)置各種滑入和滑出的動畫,需要實現(xiàn)PageTransformer接口,實現(xiàn)里面的transformPage(View page,int position)方法,做動畫相關(guān)的操作。

先來看看官方的Demo:

DepthPageTransformer

對應(yīng)的代碼如下:

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);
        }
    }
}
ZoomOutPageTransformer

對應(yīng)的PageTransformer實現(xiàn):

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);
        }
    }
}

關(guān)于Demo的具體解讀,參見官網(wǎng)示例。

下面是垂直滑動效果:

手指水平滑動-頁面垂直滾動

對應(yīng)的PageTransformer實現(xiàn):

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);  
            }  
        }  
    } 

到這里其實就已經(jīng)可以上下滑動了,但是手勢卻還是左右滑動,還需要再做一點改動。把左右滑動的手勢變換為上下滑動。

   /**
    * 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);
        }
    }

效果如下:

手指垂直滑動-頁面垂直滾動

到這里,一個垂直滾動的ViewPager已經(jīng)實現(xiàn)了。

記住,別忘了把實現(xiàn)的PageTransformer設(shè)置給ViewPager:

setPageTransformer(true, new HorizontalVerticalPageTransformer());

關(guān)于PageTransformer更多的內(nèi)容,還可以看看CSDN博客專家們的解讀:

巧用ViewPager 打造不一樣的廣告輪播切換效果

PageTransformer實現(xiàn)個性的ViewPager切換動畫

手勢事件的分發(fā)和處理

現(xiàn)在,我要做的是,既可以水平滑動又可以垂直滑動,完全根據(jù)手指滑動的方向自動的切換滑動方向,通過按鈕手動切換的這里就不細(xì)說了,相信看完自動切換的細(xì)節(jié),手動的也就不是什么問題了。

這里的手勢,監(jiān)聽的是ViewPager的item里面的ImageView控件的,所以,實現(xiàn)一個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;
        }
    }

設(shè)置給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;
            }
        });

當(dāng)然,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);
                }
            }
        }
    }

再看看效果是怎么樣的吧:

雙向自動切換

只是一個簡單的Demo,還有一些瑕疵,有空再整理,這里暫時奉上不完善的源碼:

源碼地址

運行后,點擊主頁右下角FloatingActionButton即可。

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

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

  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,156評論 22 665
  • 走呀走,走呀走 迷路的蜘蛛依然尋不著歸家的路 滴答滴,滴答滴 珍藏的秘密已經(jīng)被雨水帶去無蹤 今夜的河流寂靜一片 莊...
    知諸閱讀 282評論 0 1
  • 前面我們提到的關(guān)于石頭街的賺錢技巧,更多的關(guān)于石頭街本地風(fēng)貌,這里我們就不用介紹了。我們接著談?wù)劥蟀⒉牡艿堋?/div>
    時以芩閱讀 180評論 0 0
  • 今天,我剛剛醒來,發(fā)現(xiàn)其他人都沒醒!我看了一會兒書,等待其他人醒來。過了一會兒其他人被鬧鐘吵醒了,胡鄭豪還迷迷糊糊...
    candy2008閱讀 721評論 0 0
  • 圖片發(fā)自簡書App月光破窗而入 周童 凌晨三點 月亮破窗而入 一如破窗而入的你 一個撒了窗下一地的青輝 疑為初雪降...
    周童的小屋閱讀 432評論 5 2

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