当滚动子项(RecyclerView)时停止父项(NestedScrollview)的滚动

12

你是否按照 medium 给出的答案实现了它,能否请分享一些代码? - Damodhar
6个回答

3

我已经实现了Marc Knaup的解决方案,并且即使NestedScrollView是父级,结果也可以正确显示,如下所示:

enter image description here

顺便说一句,我将我所做的放在下面了。

CustomRecycleView

package com.example.nested_scroll_test;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.core.view.NestedScrollingParent;
import androidx.recyclerview.widget.RecyclerView;

public class CustomRecycleView extends RecyclerView implements NestedScrollingParent {
    private View nestedScrollTarget = null;
    private boolean nestedScrollTargetIsBeingDragged = false;
    private boolean nestedScrollTargetWasUnableToScroll = false;
    private boolean skipsTouchInterception = false;


    public CustomRecycleView(Context context) {
        super(context);
    }

    public CustomRecycleView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomRecycleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean temporarilySkipsInterception = nestedScrollTarget != null;
        if (temporarilySkipsInterception) {
            // If a descendent view is scrolling we set a flag to temporarily skip our onInterceptTouchEvent implementation
            skipsTouchInterception = true;
        }

        // First dispatch, potentially skipping our onInterceptTouchEvent
        boolean handled = super.dispatchTouchEvent(ev);

        if (temporarilySkipsInterception) {
            skipsTouchInterception = false;

            // If the first dispatch yielded no result or we noticed that the descendent view is unable to scroll in the
            // direction the user is scrolling, we dispatch once more but without skipping our onInterceptTouchEvent.
            // Note that RecyclerView automatically cancels active touches of all its descendents once it starts scrolling
            // so we don't have to do that.
            if (!handled || nestedScrollTargetWasUnableToScroll) {
                handled = super.dispatchTouchEvent(ev);
            }
        }

        return handled;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        return !skipsTouchInterception && super.onInterceptTouchEvent(e);
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        if (dyConsumed != 0) {
            // The descendent was actually scrolled, so we won't bother it any longer.
            // It will receive all future events until it finished scrolling.
            nestedScrollTargetIsBeingDragged = true;
            nestedScrollTargetWasUnableToScroll = false;
        } else if (dyConsumed == 0 && dyUnconsumed != 0) {
            // The descendent tried scrolling in response to touch movements but was not able to do so.
            // We remember that in order to allow RecyclerView to take over scrolling.
            nestedScrollTargetWasUnableToScroll = true;
            if (target.getParent() != null)
                target.getParent().requestDisallowInterceptTouchEvent(false);
        }
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        if (axes != 0 && View.SCROLL_AXIS_VERTICAL != 0) {
            // A descendent started scrolling, so we'll observe it.
            nestedScrollTarget = target;
            nestedScrollTargetIsBeingDragged = false;
            nestedScrollTargetWasUnableToScroll = false;
        }

        super.onNestedScrollAccepted(child, target, axes);
    }

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return nestedScrollAxes != 0 && View.SCROLL_AXIS_VERTICAL != 0;
    }

    @Override
    public void onStopNestedScroll(View child) {
        nestedScrollTarget = null;
        nestedScrollTargetIsBeingDragged = false;
        nestedScrollTargetWasUnableToScroll = false;
    }
}

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">


    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="#FFFFFF"
                android:orientation="vertical">
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:textAlignment="center"
                    android:text="Top Section"/>
            </LinearLayout>

            <com.example.nested_scroll_test.CustomRecycleView
                android:id="@+id/rw"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:background="@color/colorPrimary"
                android:nestedScrollingEnabled="true"
                android:orientation="vertical">

            </com.example.nested_scroll_test.CustomRecycleView>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:background="#FFFFFF"
                android:orientation="vertical" >
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:textAlignment="center"
                    android:text="Bottom Section"/>
            </LinearLayout>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>


</androidx.coordinatorlayout.widget.CoordinatorLayout>

RecycleViewItem.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/nsw"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="2dp"
        android:background="#CCCC"
        android:gravity="center"
        android:nestedScrollingEnabled="false"
        android:orientation="vertical"
        android:padding="2dp"
        android:textColor="#FFFFFF" />
</androidx.core.widget.NestedScrollView>

0
通常简单的技巧对我很有效。当将OnTouchListener附加到子视图时,在onTouch()中只需禁用触摸中断即可。像这样:
        RecyclerView rv = findViewById(...);
        rv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
            ...
            v.getParent().requestDisallowInterceptTouchEvent(true);

0

这是你想查看的演示,在MainActivity.xml文件中:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="MainActivity">

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:focusableInTouchMode="true"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/top_seller"
                android:layout_width="match_parent"
                android:layout_height="200sp"
                android:background="@color/colorAccent"
                android:contentDescription="@string/app_name"
                android:adjustViewBounds="true"
                android:src="@drawable/background"
                android:scaleType="fitXY"/>
            <ImageView
                android:id="@+id/top_seller1"
                android:layout_width="match_parent"
                android:layout_height="200sp"
                android:background="@color/colorAccent"
                android:contentDescription="@string/app_name"
                android:adjustViewBounds="true"
                android:src="@drawable/background"
                android:scaleType="fitXY"/>
            <android.support.v7.widget.RecyclerView
                android:id="@+id/product_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                android:scrollbars="none" />
            <ImageView
                android:id="@+id/top_seller2"
                android:layout_width="match_parent"
                android:layout_height="200sp"
                android:background="@color/colorAccent"
                android:contentDescription="@string/app_name"
                android:adjustViewBounds="true"
                android:src="@drawable/background"
                android:scaleType="fitXY"/>
            <ImageView
                android:id="@+id/top_seller3"
                android:layout_width="match_parent"
                android:layout_height="200sp"
                android:background="@color/colorAccent"
                android:contentDescription="@string/app_name"
                android:adjustViewBounds="true"
                android:src="@drawable/background"
                android:scaleType="fitXY"/>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</LinearLayout>

在你的MainActivity.java类中的onCreate方法中:
   RecyclerView bestRecyclerView = findViewById(R.id.product_list);
   GridLayoutManager mGrid = new GridLayoutManager(this, 2);
   bestRecyclerView.setLayoutManager(mGrid);
   bestRecyclerView.setHasFixedSize(true);
   bestRecyclerView.setNestedScrollingEnabled(false);
   // Create ProductAdapter for  RecyclerView data
   ProductAdapter mAdapter = new ProductAdapter(MainActivity4.this,getProductTestData());
   bestRecyclerView.setAdapter(mAdapter);

enter image description here

希望这能帮到你...!


你找到了答案还是需要帮助? - Viral Patel
这对你有帮助吗? - Viral Patel

0

请将您已尝试的所有内容都发布出来。

您的 XML 应该是:

<android.support.v4.widget.NestedScrollView
            android:id="@+id/nestedScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:overScrollMode="never">


            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

              <View > <!-- upper content -->

              <!-- set recycler view with with wrap_content -->
              <android.support.v7.widget.RecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />

            </LinearLayout>
      </android.support.v4.widget.NestedScrollView>

在onCreateView() / onCreate()方法内设置滚动行为。需要API 21+。

    RecyclerView v = (RecyclerView) findViewById(...);
    v.setNestedScrollingEnabled(false);
                   or 
    android:nestedScrollingEnabled="false"  // inside recycler view in xml file

无法使用过度滚动模式实现在RecyclerView中嵌套Scrollable NestedScrollViews的滚动行为,详见https://medium.com/widgetlabs-engineering/scrollable-nestedscrollviews-inside-recyclerview-ca65050d828a。 - Rockin
1
这不是一个解决方案,因为android:nestedScrollingEnabled="false"会冻结RecycleView。 - A Farmanbar

0

@Rockin 使用overscrollmode兄弟:)

 <androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:fillViewport="true"
    android:overScrollMode="always"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/color_white"
        android:orientation="vertical">


       <androidx.recyclerview.widget.RecyclerView                
           android:id="@+id/activity_insight_recyclerview_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="@dimen/_20sdp"
          app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/raw_insight" />

无法通过使用overscroll模式来实现此https://medium.com/widgetlabs-engineering/scrollable-nestedscrollviews-inside-recyclerview-ca65050d828a中提到的滚动行为。 - Rockin
@Rockin,你需要给android:layout_height属性设置为"match_parent",这样肯定可以正常工作,我在我的代码中也是这么做的。 - hio

-1
你尝试过子类化RV并覆盖onInterceptTouchEvent吗?
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {

    when (event!!.action and MotionEvent.ACTION_MASK) {


        MotionEvent.ACTION_DOWN -> {

            // WE INTERACT WITH THIS RV. PREVENT PARENT TO INTERCEPT 
            parent.requestDisallowInterceptTouchEvent(true) 

        }


        MotionEvent.ACTION_MOVE, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {

            // THIS SEEMS LIKE IT WILL HAVE "DEFAULT" BEHAVIOUR BUT SINCE WE CURRENTLY DRAGGING THE RV THEN IT WONT SCROLL THE PARENT  
            parent.requestDisallowInterceptTouchEvent(false)

        }

    }

    return false
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接