Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView inside NestedScrollView causes RecyclerView to inflate all elements

I'm having an issue with placing a RecyclerView inside a NestedScrollView, which causes ALL elements of the RecyclerView's adapter to be rendered.

This is a rather large issue, as the lists that the RecyclerView is showing can contain several hundred elements.

This is at the moment causing quite a lot of lag (obviously) as it has to render all views at once, and can't reuse any already inflated views as the RecyclerView normally does.

This is my current XML (Removed some bloat to minimize it):

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
    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:fillViewport="true"
    android:overScrollMode="never">

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

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="90dp">

            <!-- Some content -->

        </RelativeLayout>

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

            <!-- Some more content -->

        </LinearLayout>

        <!-- Product list -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="12dp"/>

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:overScrollMode="never"/>

        </LinearLayout>

    </LinearLayout>

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

This is my onCreateView() from the Fragment that is inflating the view containing the NestedScrollView and RecyclerView:

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.category_content_fragment, container, false);
    ButterKnife.bind(this, root);

    List<Product> products = new ArrayList<>(); //This is populated by other means, not relevant to the issue

    productsRecyclerView.setNestedScrollingEnabled(false);
    productsRecyclerView.setHasFixedSize(true);
    productsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

    ProductsContentAdapter productsContentAdapter = new ProductsContentAdapter(products);
    productsRecyclerView.setAdapter(productsContentAdapter);

    return root;
}

I have seen this post about the issue:
How to put RecyclerView inside NestedScrollView?

But it doesn't mention a final fix to the issue sadly.

To clarify: The RecyclerView scrolls perfectly, it shows at the correct time, but the issue is that it renders ALL of its children instantly, meaning possible several hundreds of elements, even though the screen only shows 5-6 at a time at max.

Please feel free to ask questions if more information is needed.

------- EDIT -------
After many failed attempts of other solutions, i ended up using Jeeva Nandhan's solution.
Prior to asking this question i knew that was a possible solution, but i had 11 different possible views that needed to fit into the RecyclerView, so i would've liked to avoid it.
After using different ViewTypes, it worked perfectly. I was afraid it would be very inefficient due to the high amount of ViewTypes, but it's buttery smooth.

like image 833
Moonbloom Avatar asked Nov 19 '22 18:11

Moonbloom


1 Answers

I too have come across this issue... This is because both scrollview and RecyclerView are different in loading data, since the ScrollView acts as the parent in this case and we are using the below line in our code.

setNestedScrollingEnabled(false);

This will make the scroll slow and hang issue based on the Recyclerview data.

One way which I have used to solve this issue is adding header to the Recyclerview..

Here I'll explain it clearly.

lets assume this recyclerview is in our activity.

<android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                />

The adapter class will be like this, where we will add the header

public class SampleAdapter extends RecyclerView.Adapter {


    private final int BODY = 1;
    private final int HEADER = 2;

    private List<String> data = null;

    SampleAdapter(List<String> data) {
        this.data = data;
    }


    @Override
    public int getItemViewType(int position) {

        if (position == 0) {
            return HEADER;
        }

        return BODY;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        switch (viewType) {

            case HEADER:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.inflate_header_layout, parent, false);
                return new HeaderViewHolder(view);

            default:
                //Setting the Body view...
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.inflate_details, parent, false);
                return new BodyViewHolder(view);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        if (holder instanceof BodyViewHolder) {
            //Set the body content....
            if (data != null && data.size() > 0) {
                /** Since we have added one cell for header,
                 * we need to decrement the position and render the body view.
                 *
                 */
                int bodyPosition = position - 1;

            }
        } else if (holder instanceof HeaderViewHolder) {
            //Set the header content...
        }
    }

    @Override
    public int getItemCount() {
        //Sice we are going to add header, we are supposed increase the count by one...
        return data.size() + 1;
    }
}

by this there is no need for NestedScrollView and all the view will work in RecyclerView behavior...

Hope this is helpful :)

like image 63
Jeevanandhan Avatar answered Nov 21 '22 08:11

Jeevanandhan