处理 RecyclerView、NestedScrollView 和 CardView

Dealing With RecyclerView, NestedScrollView, and CardView

我将在我的应用程序中实现此 UI:

好吧,我之前尝试过的一些方法:

1.使用 CollapsingToolbarLayout 我把我的 CardView 放在 CollapsingToolbarLayout 里面,然后把它们都放在 AppBarLAyout.

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|exitUntilCollapsed" >

            <CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                />
                  <!-- My Views Goes there -->
            </CardView

            <android.support.v7.widget.Toolbar
                android:id="@+id/flexible.example.toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@null"
                app:layout_collapseMode="pin"
                style="@style/ToolBarWithNavigationBack"
                />
        </android.support.design.widget.CollapsingToolbarLayout>

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

    <RecyclerView
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
    ></RecyclerView>

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

P.S:我去掉了不相关的代码,不要提我

这种方法可以正常工作但是!!!当 CardView 高度高于屏幕高度时,它的内容会被 AppBarLayout 忽略并且不会向用户显示

2。使用 NestedScrollView 我把 CardViewRecyclerView 放在 NestedScrollView 里面。但问题是当用户到达 RecyclerView 的末尾然后滚动回到顶部时,fling 变得迟缓和错误并停止一些用户必须滚动越来越多才能到达顶部的地方!

<android.support.v4.widget.NestedScrollView
    android:id="@+id/nested_scrollbar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="fill_vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:scrollbars="none" >
        <LinearLayout
            android:id="@+id/nested_scrollbar_linear"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

                <android.support.v7.widget.CardView
                    android:id="@+id/flexible.example.cardview"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:cardBackgroundColor="@color/post_card_backgroind"
                    app:cardCornerRadius="0dp"
                    app:cardElevation="0dp">

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

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/list_view"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="@dimen/four"
                    android:layout_marginEnd="@dimen/four"
                    android:layout_marginLeft="@dimen/four"
                    android:layout_marginRight="@dimen/four"
                    android:layout_marginStart="@dimen/four"
                    android:layout_marginTop="@dimen/four"
                    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    </LinearLayout>

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

如何解决这个问题?! 我不想使用为 recyclerview 制作 header 的适配器,我从其中一些那里遇到了与性能相关的问题。

回答

RecyclerView 放入 NestedScrollView 中,如您在 2 中所见,然后应用 .setNestedScrollingEnabled 并将其设置为 false

你应该使用 getItemViewType。这很容易,不会产生任何性能开销。像这样更改 Adapter 中的代码:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    class CardViewHolder extends RecyclerView.ViewHolder {
        ...
    }

    class ItemViewHolder extends RecyclerView.ViewHolder {
        ...
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return 0; // Card Type
        } else {
            return 1; // Item Type
        };
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case 0: 
                 // Card Type
                 return new CardViewHolder(...);
             case 1: 
                 // Item Type
                 return new ItemViewHolder(...);
         }
    }

    // Optional
    // If your data list does not contain CardView data
    // You may need to add extra count in adapter

    @Override
    public final int getItemCount() {
        // Add one count for CardView data
        return list.size() + 1;
    }

    @Override
    public T getItem(int position) {
        // As the position is change because of CardView at position 0
        // So you may have to decrement the corresponding index 
        return list.get(position - 1);
    }
}


更新 1

如果不想更新适配器,可以使用

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
              <!-- Views Goes there -->
        </CardView>
        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            // This is the key
            android:nestedScrollingEnabled="false"/>
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

android:nestedScrollingEnabled 在 API 级别 21 到 xml 中可用。
对于较低的 APIs 使用 java 方法,即 recyclerView.setNestedScrollingEnabled(false);ViewCompat.setNestedScrollingEnabled(recyclerView, false);

注意 (12-12-2016)

When using nested scroll. Be aware of the known issue of RecyclerView not recycling views as asked here and (apparent reason answered by Arpit Ratan and respectively). So I'll suggest to go with the first solution i.e. using getItemViewType. For more details, see complete and better answers like this or this.


更新 2

您可以使用 setFullSpan。如果 StaggeredGridLayoutManager 的方向是垂直的,您可以使用以下代码将其宽度扩展到全屏:

public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
    if (position == 0) {
        StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) viewHolder.itemView.getLayoutParams();
        layoutParams.setFullSpan(true);
    }
}

2.using

中的问题

But the problem is When User Reached To end of RecyclerView and then scroll back to top

可以在NestedScrollView下面的第一个布局中添加android:descendantFocusability="blocksDescendants来处理:

<android.support.v4.widget.NestedScrollView
android:id="@+id/nested_scrollbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:scrollbars="none" >
    <LinearLayout
        android:id="@+id/nested_scrollbar_linear"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:descendantFocusability="blocksDescendants" >

对我有用。 NestedScrollView 在 Recyclerview 调整大小后滚动到顶部