Android-VerticalViewPagerネストRecyclerViewスライド処理

6944 ワード

この会社に入る前に、垂直方向と水平方向のスライドページをめくる機能が必要だと製品が提案したが、垂直方向は次のページに引き上げるしかない.実はこの機能は以前の兄弟たちが実現したが、彼の実現方式が優雅ではないことを考慮すると、後期バージョンの反復に不利であることから、この機能を再実現した.以前の兄弟たちはRecyclerViewでViewPagerをネストする方式で処理していたが、RecyclerViewのitemは2番目から空白を表示していたが、具体的な原因は分からず、多重化に関係していると推定され、知っている友人が注意できる.ここではVerticalViewPager+RecyclerViewネスト方式を使用しており、UIの更新と再描画が容易です.外部のViewPagerとRecyclerViewは画面いっぱいでページをめくる効果があるからです.コードは多くなく、思想を理解し、イベントを明確に判断し、分割することが肝心です.
では、本題に入ります.gif図を使って、縦方向から下へスライドしてページをめくる+水平ページをめくる必要があることを明らかにしましょう.
NestViewPager.gif
次のようになります.
VerticalViewPagerを無効にする上から前のページのイベントへ
1)垂直方向のViewPagerはVerticalViewPagerで、下から上へページをめくることができます.VerticalViewPagerが上から下へスライドするジェスチャーが失効しても、dispatchTouchEventを書き換えることで、以下のように制御できます.
    private int xDispatchLast;
    private int yDispatchLast;
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "xLastDown:" + xInterceptLast);
                Log.d(TAG, "yLastDown:" + yInterceptLast);

                xDispatchLast = (int) ev.getX();
                yDispatchLast = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                final int curX = (int) ev.getX();
                final int curY = (int) ev.getY();
                Log.d(TAG, "curXMove:" + curX);
                Log.d(TAG, "curYMove:" + curY);
                int xDiff = curX - xDispatchLast;
                int yDiff = curY - yDispatchLast;
                int xAbsDiff = Math.abs(xDiff);
                int yAbsDiff = Math.abs(yDiff);
                Log.d(TAG, "xDiffMove:" + xDiff);
                Log.d(TAG, "yDiffMove:" + yDiff);
                if (yAbsDiff > xAbsDiff && yDiff > 0) {//            ,  ViewPager        ,              
                    Log.d(TAG, "dispatchTouchEvent");
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                xDispatchLast = (int) ev.getX();
                yDispatchLast = (int) ev.getY();
                Log.d(TAG, "xLastUp:" + xDispatchLast);
                Log.d(TAG, "yLastUp:" + yDispatchLast);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

2)VerticalViewPager+RecyclerViewでイベント競合が発生する場合は、水平方向にスライドするイベントをRecyclerView、すなわちRecyclerView内部に渡すだけでdispatchTouchEvent()メソッド内でgetParent()を通過する.requestDisallowInterceptTouchEvent(true)は、親コントロールが親コントロールをブロックするイベントを自分で処理するように制御します.垂直方向にスライドするイベントはVerticalViewPagerに渡されます.すなわち、onInterceptTouchEvent()メソッドで直接return trueを自分の処理に渡します.
VerticalViewPagerは、垂直方向イベントをサブViewにブロックし、自分で処理します.
    private int xInterceptLast;
    private int yInterceptLast;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "xLastDown:" + xInterceptLast);
                Log.d(TAG, "yLastDown:" + yInterceptLast);

                xInterceptLast = (int) ev.getX();
                yInterceptLast = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                final int curX = (int) ev.getX();
                final int curY = (int) ev.getY();
                Log.d(TAG, "curXMove:" + curX);
                Log.d(TAG, "curYMove:" + curY);
                int xDiff = curX - xInterceptLast;
                int yDiff = curY - yInterceptLast;
                int xAbsDiff = Math.abs(xDiff);
                int yAbsDiff = Math.abs(yDiff);
                Log.d(TAG, "xDiffMove:" + xDiff);
                Log.d(TAG, "yDiffMove:" + yDiff);
                if (yAbsDiff > xAbsDiff && yDiff <= 0) {//            ,  ViewPager        ,    ViewPager     
                    Log.d(TAG, "onInterceptTouchEvent");
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                xInterceptLast = (int) ev.getX();
                yInterceptLast = (int) ev.getY();
                Log.d(TAG, "xLastUp:" + xInterceptLast);
                Log.d(TAG, "yLastUp:" + yInterceptLast);
                break;
        }
        boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
        swapXY(ev); // return touch coordinates to original reference frame for any child views
        return intercepted;
    }

RecyclerView親コントロールの水平方向のイベントをサブコントロールに渡す処理を処理しない
3)このコードは私はRecyclerViewで処理することを選択していません.そのルートレイアウトに置いて、どの場所を置くかは必要に応じて決められます.以下のようにします.
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "xLastDown:" + xLast);
                Log.d(TAG, "yLastDown:" + yLast);

                xLast = (int) ev.getX();
                yLast = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                final int curX = (int) ev.getX();
                final int curY = (int) ev.getY();
                Log.d(TAG, "curXMove:" + curX);
                Log.d(TAG, "curYMove:" + curY);
                int xDiff = curX - xLast;
                int yDiff = curY - yLast;
                int xAbsDiff = Math.abs(xDiff);
                int yAbsDiff = Math.abs(yDiff);
                Log.d(TAG, "xDiffMove:" + xDiff);
                Log.d(TAG, "yDiffMove:" + yDiff);
                if (yAbsDiff < xAbsDiff || (yAbsDiff > xAbsDiff && yDiff > 0)) {//             
                    Log.d(TAG, "requestDisallowInterceptTouchEvent");
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                getParent().requestDisallowInterceptTouchEvent(false);
                xLast = (int) ev.getX();
                yLast = (int) ev.getY();
                Log.d(TAG, "xLastUp:" + xLast);
                Log.d(TAG, "yLastUp:" + yLast);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }


以上で目的の効果が得られるので、簡単そうですね.最後にイベント分析をまとめます:1.内部RecyclerViewでdispatchTouchEvent()で水平スライドイベントを取得します.  2.外部VerticalViewPagerでonInterceptTouchEvent()で垂直方向スライドイベントを取得します.  3.外部VerticalViewPagerでは、dispatchTouchEvent()では、前のページのジェスチャーイベントをドロップダウンして処理しません.
イベント競合を解決する鍵は、イベントの配布を分析し、いつどのコントロールがどのジェスチャーを処理するかを分析することが第一の前提です.もちろん、イベントの配布についても深く理解しなければなりません.
VerticalViewPagerソースコードstackoverflowコードからgithub:NestViewPagerにアップロードされました