Dealing With RecyclerView, NestedScrollView, and CardView
M

2

7

I'm going to achieve this UI in my App: UI

Well, Some ways that I tried before:

1. Using CollapsingToolbarLayout I put my CardView insid of CollapsingToolbarLayout and put them all in 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: I removed unrelated codes, don't mention me

This Way works correctly but!!! When CardView height goes taller than screen height, it's content igonered by AppBarLayout and does not show to user

2. Using NestedScrollView I put CardView and RecyclerView inside NestedScrollView. But the problem is When User Reached To end of RecyclerView and then scroll back to top, fling goes laggy and buggy and stop some where ans user have to scroll more and more to get to top!

<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>

How can fix this issue?! I don't want use adapters that make header for recyclerview, I got performance related issue from some of them.

Answer

Put RecyclerView inside NestedScrollView as you can see in 2 and apply .setNestedScrollingEnabled and set it to false

Magdalenemagdalenian answered 25/5, 2016 at 12:28 Comment(4)
If I ignore performance issue for a while, you want to fix CardView on top or simply scroll like other items in RecyclerView?Glance
@Glance Yep, I want this exactlyMagdalenemagdalenian
I actually asked you which one you actually want. Assuming you want the second one (i.e. CardView to scroll like other items in RecyclerView) Can you post the second layout in which you put CardView and RecyclerView inside NestedScrollView?Glance
@Glance Oops! my bad, Sorry. What i want is this: this is a post activity that cardView hold post related information (like image, description,..) and in recyclerview under it, we are going to shoe related posts. I want when user scroll the recyclerview, cardview scrolled too and goes off of screenMagdalenemagdalenian
G
10

You should use getItemViewType. It is easy and won't create any performance overhead. Change the code in your Adapter like this:

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);
    }
}


UPDATE 1

If you don't want to update the adapter, you can use

<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 is available in API Level 21 through xml.
For lower APIs use java method i.e. recyclerView.setNestedScrollingEnabled(false); or ViewCompat.setNestedScrollingEnabled(recyclerView, false);

NOTE (12-12-2016)

When using nested scroll. Be aware of the known issue of RecyclerView not recycling views as asked here and here (apparent reason answered by Arpit Ratan and me 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.


UPDATE 2

You can use setFullSpan. If the orientation of your StaggeredGridLayoutManager is vertical, you can use the following code to span it's width to full screen:

public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
    if (position == 0) {
        StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) viewHolder.itemView.getLayoutParams();
        layoutParams.setFullSpan(true);
    }
}
Glance answered 25/5, 2016 at 13:27 Comment(11)
It seems you forgot StaggeredLayoutManager!! for first child as you mention we should force view to fille screen by setting LayoutParams... we test this way before...Magdalenemagdalenian
@Magdalenemagdalenian Yes you'll have to set LayoutParams to make first view full screen (as I mentioned in Update 2). But if you don't want to do it this way, you can still use NestedScrollView (as in Update 1). It should work for you!Glance
As I said in my question, we tried both ways. lets talk about NestedScrollView, it is reliable, but when user reached to end of recyclerview, and scroll back to top! fling goes buggy and laggy! What's your solution for this?Magdalenemagdalenian
@Magdalenemagdalenian Well I haven't personally experience such issue when using NestedScrollView with RecyclerView so I don't have any solution for it. But yes, the lag can be because of your Adapter, as we know if there is a lot of code in Adapter, it can cause lag while scrolling!Glance
No it's not adapter performance related! I think it's related to BehaviorMagdalenemagdalenian
@Magdalenemagdalenian Yes, behaviour can be the issue. Try adding app:layout_behavior="@string/appbar_scrolling_view_behavior" in NestedScrollView, CardView and RecyclerView as well. May be it can help you.Glance
@Magdalenemagdalenian In addition to that, try setting android:nestedScrollingEnabled to true in RecyclerViewGlance
android:nestedScrollingEnabled is available in API 21 and higher!! my minimum api is 10!Magdalenemagdalenian
@Magdalenemagdalenian Oops. You might need to try something different then. I hope someone else might help you get over this issue. Good Luck :)Glance
Hey dude, nestedScrollingEnabled was answer! i've set this in my code to recyclerview ViewCompat.setNestedScrollingEnabled(recyclerView, false). Post as Answer and I'll accept it. So many thank ;)Magdalenemagdalenian
@Magdalenemagdalenian Great. Here you go :)Glance
B
4

the problem in 2.using

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

it can be deal by add android:descendantFocusability="blocksDescendants in the first layout under the NestedScrollView like this:

<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" >

it work for me. NestedScrollView scrolls to top on Recyclerview resized

Barbell answered 2/5, 2017 at 9:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.