web-dev-qa-db-ja.com

RecyclerViewスクロールの問題がある垂直ViewPager2

垂直方向の2つのフラグメントを持つViewPager2を使用しています。ユーザーが2番目のフラグメントまで下にスワイプすると、コンテンツを同じ垂直方向にスクロールするRecyclerViewがあります。

問題は RecyclerViewのコンテンツをスクロールすると、ViewPager2がスクロールイベントをキャッチし、RecyclerViewがスクロールイベントをキャッチすることがあります。

欲しいユーザーがRecyclerViewの最上部にスクロールしているときに、ユーザーがRecyclerViewのコンテンツの最上部に到達したときにのみ、ViewPagerが最初のフラグメントまでスワイプして戻るようにします。

使用してみましたrecyclerView.isNestedScrollingEnabled = false運が悪い。また、RecyclerViewをNestedScrollViewに入れてみましたが、RecyclerViewがデータセットに必要なすべてのViewHolderを作成するため、これは推奨されません。これは明らかに効率的ではありません。

2
Calvin Ry

だから... documentation ????を読むだけでそれを理解することができました。答えをここに投稿して、同様の問題を抱えている他の人に役立つようにします。

NestedScrollViewとは異なり、ViewPager2はネストされたスクロールビューを十分にサポートしていないため、ネストされたスクロールビューを親のレイアウトでインターセプトしているタッチイベントとスワイプイベントを処理できるように、レイアウトのカスタムラッパーでラップする必要があります。この場合、子はRecyclerViewで、親はViewPager2になります。

あなたはラッパークラス ここ を見つけることができます。以下のように、プロジェクトに追加して、スクロール可能なビューをラップするだけです。

_    <NestedScrollableHost
            Android:layout_width="match_parent"
            Android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
                Android:id="@+id/my_recycler_view"
                Android:layout_width="match_parent"
                Android:layout_height="match_parent"
                Android:orientation="vertical" />

    </NestedScrollableHost>
_

ここで注意すべきことがいくつかあります。 documentation は、このソリューションは、ViewPager内の他のスクロール可能なビュー内にあるスクロール可能なビューでは機能しないことを示しています。このソリューションは、ViewPagerの即時スクロールビューでのみ機能します。

もう1つの注意点は、ラッパークラスがrequestDisallowInterceptTouchEvent()を使用して、子が代わりにスクロールする必要がある場合に、子のスクロール可能なビューが親にスクロールしないように指示することを確認することです。

5
Calvin Ry

私が手に入れるまでの最良の解決策は、recyclerView.addOnItemTouchListener(this)の内側でジェスチャー検出器。シンプルオンGestureListenerを使用することです。

ステップ-1:OnCreate()メソッドで

gestureDetector = new GestureDetector(getActivity(), new GestureListener());

ステップ-2:recyclerView addonitemtouchlistenrメソッドを実装する-

recyclerView.addOnItemTouchListener(this);

ステップ-3:GestureDetector.SimpleOnGestureListenerを拡張するGestureListenerクラスを作成します。

public class GestureListener extends GestureDetector.SimpleOnGestureListener {
    private final int Y_BUFFER = 10;

    @Override
    public boolean onDown(MotionEvent e) {
        // Prevent ViewPager from intercepting touch events as soon as a DOWN is detected.
        // If we don't do this the next MOVE event may trigger the ViewPager to switch
        // tabs before this view can intercept the event.
        Log.d("vp", "true1");
        recyclerView.getParent().requestDisallowInterceptTouchEvent(true);
        return super.onDown(e);
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        if (Math.abs(distanceX) > Math.abs(distanceY)) {
            Log.d("vp2", "true");
            // Detected a horizontal scroll, allow the viewpager from switching tabs
            recyclerView.getParent().requestDisallowInterceptTouchEvent(false);
        } else if (Math.abs(distanceY) > Y_BUFFER) {
            // Detected a vertical scroll prevent the viewpager from switching tabs
            Log.d("vp3", "false");
            recyclerView.getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onScroll(e1, e2, distanceX, distanceY);
    }
}

ステップ4:onInterceptTouchEvent()からGestureDetector.onTouchEvent(e)を呼び出します。

@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
    gestureDetector.onTouchEvent(e);
    return false;
}
0
Prakash