安卓版面: Horizontal Recyclerview inside a Vertical Recyclerview inside a Viewpager with Scroll Behaviors

标签 android android-layout android-recyclerview android-coordinatorlayout android-appbarlayout

这是我正在尝试构建的应用程序,其中包含以下映射的所有元素:

Veggies app

一切正常,但是,我希望内部水平回收 View 不捕获任何垂直滚动。所有垂直滚动都必须朝向外部垂直回收 View ,而不是水平 ScrollView ,以便垂直滚动允许工具栏根据它的滚动标志退出 View 。

当我将手指放在 recyclerview 的“StrawBerry Plant”部分并向上滚动时,它会滚动出工具栏:

strawberry plant

如果我将手指放在水平 ScrollView 上并向上滚动,它根本不会滚动出工具栏。

以下是我目前为止的xml布局代码。

Activity xml 布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/fragment_container"
    android:clipChildren="false">

    <android.support.design.widget.CoordinatorLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/container"
        >

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:minHeight="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways">

        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
            android:id="@+id/sliding_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            style="@style/CustomTabLayout"
            />

    </android.support.design.widget.AppBarLayout>
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />

    </android.support.design.widget.CoordinatorLayout>

</FrameLayout>

“Fruits” fragment xml布局(这是 fragment 的代码- fragment 在上图中标记):

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

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"
        android:visibility="gone"
        android:layout_centerInParent="true"
        android:indeterminate="true"/>

<!--    <android.support.v7.widget.RecyclerView-->
    <com.example.simon.customshapes.VerticallyScrollRecyclerView
        android:id="@+id/main_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>

我使用了一个名为 VerticallyScrollRecyclerView 的自定义类,它遵循谷歌在 View 组中处理触摸事件的示例。它的目的是拦截和消耗所有垂直滚动事件,以便它可以滚动进/出工具栏:http://developer.android.com/training/gestures/viewgroup.html

VerticallyScrollRecyclerView的代码如下:

public class VerticallyScrollRecyclerView extends RecyclerView {

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

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

    public VerticallyScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    ViewConfiguration vc = ViewConfiguration.get(this.getContext());
    private int mTouchSlop = vc.getScaledTouchSlop();
    private boolean mIsScrolling;
    private float startY;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        // Always handle the case of the touch gesture being complete.
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            // Release the scroll.
            mIsScrolling = false;
            startY = ev.getY();
            return super.onInterceptTouchEvent(ev); // Do not intercept touch event, let the child handle it
        }
        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                Log.e("VRecView", "its moving");
                if (mIsScrolling) {
                    // We're currently scrolling, so yes, intercept the
                    // touch event!
                    return true;
                }
                // If the user has dragged her finger horizontally more than
                // the touch slop, start the scroll
                // left as an exercise for the reader
                final float yDiff = calculateDistanceY(ev.getY());
                Log.e("yDiff ", ""+yDiff);
                // Touch slop should be calculated using ViewConfiguration
                // constants.
                if (Math.abs(yDiff) > 5) {
                    // Start scrolling!
                    Log.e("Scroll", "we are scrolling vertically");
                    mIsScrolling = true;
                    return true;
                }
                break;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }


    private float calculateDistanceY(float endY) {
        return startY - endY;
    }

}

“收藏夹”布局,它是垂直回收 View 中的回收 View :

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@color/white"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Favourite"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="16dp"
        android:id="@+id/header_fav"/>

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_below="@+id/header_fav"
        android:id="@+id/recyclerview_fav">
    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

这已经困扰我一段时间了,我还没有想出一个解决方案。有谁知道如何解决这个问题?

5 分给 Griffindor 的正确答案,当然还有 SO 的声望分。

最佳答案

经过测试的解决方案:

您只需要在您的内部 RecyclerViews 上调用 mInnerRecycler.setNestedScrollingEnabled(false);


解释:

RecyclerView 通过实现 NestedScrollingChild 接口(interface)支持在 API 21 中引入的嵌套滚动。当您在另一个 View 内部有一个沿相同方向滚动的 ScrollView 并且您希望仅在聚焦时滚动内部 View 时,这是一个有值(value)的功能。

无论如何,RecyclerView在初始化时默认调用RecyclerView.setNestedScrollingEnabled(true);。现在,回到问题,因为您的两个 RecyclerView 都在具有 AppBarBehaviorCoordinateLayout 必须决定当您从内部 RecyclerView 滚动时响应哪个滚动;当您的内部 RecyclerView 的嵌套滚动启用时,它会获得滚动焦点,并且 CoordinateLayout 将选择响应其在外部 RecyclerView 上的滚动> 的滚动。问题是,由于您的内部 RecyclerView 不会垂直滚动,因此没有垂直滚动变化(从 CoordinateLayout 的角度来看),如果有没有变化,AppBarLayout 也没有变化。

在您的情况下,因为您的内部 RecyclerView 正在向不同的方向滚动,您可以禁用它,从而导致 CoordinateLayout 忽略其滚动并响应外部 RecyclerView 的滚动。


通知:

xml 属性 android:nestedScrollingEnabled="boolean" 不打算与 RecyclerView 一起使用,并尝试使用 android:nestedScrollingEnabled="false " 将导致 java.lang.NullPointerException 因此,至少目前,您必须在代码中执行此操作。

关于安卓版面: Horizontal Recyclerview inside a Vertical Recyclerview inside a Viewpager with Scroll Behaviors,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33456216/

相关文章:

android - 扩展 RelativeLayout,并覆盖 dispatchDraw() 以创建可缩放的 ViewGroup

android - 如何隐藏和显示一些 ImageButtons?

java - 如何根据给编辑 TextView 的计数值动态实现线性布局的代码

java - 无法在 RecyclerView 中显示项目

android - 在 Android 中将 notifyItemRemoved 或 notifyDataSetChanged 与 RecyclerView 一起使用

java - 无法通过 `CompileOptions.compilerArgs` 指定 -processorpath 或 --processor-path

android - 如何通知一个Activity另一个Activity刚刚启动?

android - 为什么新的 google places api 不与 build.gradle 同步?

java - Android Rss 图片问题

java - 未调用 RecyclerView 适配器方法