RecyclerView does not Recycling Views when use it inside NestedScrollView
Asked Answered
T

2

69

I'm using RecyclerView inside NestedScrollView. Also i set setNestedScrollingEnabled to false for recyclerview

to support lower API

ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);

Now! When user scrolled the view every thing seems okay, but!!! views in recyclerview does not recycled!!! and Heap size grows swiftly!!

Update: RecyclerView layout manager is StaggeredLayoutManager

fragment_profile.xml:

<android.support.design.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:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

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

        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/profileSwipeRefreshLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

                <!-- RecyclerView and NestedScrollView -->
                <include layout="@layout/fragment_profile_details" />

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

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

fragment_profile_details.xml:

<LinearLayout
    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:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:orientation="vertical" >

        <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:fillViewport="true"
            android:scrollbars="none" >

                <LinearLayout
                    android:id="@+id/nested_scrollbar_linear"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:descendantFocusability="blocksDescendants"
                    android:orientation="vertical" >

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

                            <!-- Profile related stuff like avatar and etc. --->

                        </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"
                            android:clipToPadding="false" />

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

ProfileFragment.java:

mAdapter        = new MainAdapter(getActivity(), glide, Data);

listView        = (RecyclerView) view.findViewById(R.id.list_view);

ViewCompat.setNestedScrollingEnabled(listView, false);  
listView.setAdapter(mAdapter);

mStaggeredLM    = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mStaggeredLM.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);

listView.setLayoutManager(mStaggeredLM);

mScroll.setOnScrollChangeListener(new OnScrollChangeListener() {

        @Override
        public void onScrollChange(NestedScrollView arg0, int arg1, int arg2, int arg3, int arg4) {

            View view   = (View) mScroll.getChildAt(mScroll.getChildCount() - 1);
            int diff    = (view.getBottom() - ( mScroll.getHeight() + mScroll.getScrollY()));

            if(diff == 0){

                int visibleItemCount            = mStaggeredLM.getChildCount();
                int totalItemCount              = mStaggeredLM.getItemCount();

                int[] lastVisibleItemPositions  = mStaggeredLM.findLastVisibleItemPositions(null);
                int lastVisibleItemPos  = getLastVisibleItem(lastVisibleItemPositions);

                Log.e("getChildCount", String.valueOf(visibleItemCount));
                Log.e("getItemCount", String.valueOf(totalItemCount));
                Log.e("lastVisibleItemPos", String.valueOf(lastVisibleItemPos));

                if ((visibleItemCount + 5) >= totalItemCount) {

                    mLoadMore.setVisibility(View.VISIBLE);
                    Log.e("LOG", "Last Item Reached!");
                }

                mMore = true;
                mFresh = false;
                mRefresh = false;
                getPosts();
            }

        }

    });

P.s : I've set load more to scroll view, because recyclerview do it continuously and none stoppable!

Any help is appreciated

Toon answered 2/6, 2016 at 11:33 Comment(15)
Can you explain the reason why did you add RecyclerView inside NestedScrollView?Dogger
@rom4ek #37437661Toon
@Toon update your code and xmlStrawn
@Strawn done, code and xml was addedToon
what version of recyclerview you have compiled in your gradle?Commentary
@Toon have you tried solution suggested by Rehan for #37437661 ?Hominoid
@Hominoid I don't want update my adapterToon
NestedScrollView draws all child element, So if RecyclerView has been used inside NestedScrollView all the element of adapter will be loaded at first time itself. A better approach will be to use RecyclerView only and use getItmeViewType to return cardView if(position==0) in adapter. as suggested by Rehan.Hominoid
@Hominoid I've tried this before and I got some performance issue, this approach could be okay when using LinearLayoutManager but in my case with StaggeredGridLayoutManager, it could not help so.Toon
I normally go with @Pr38y. what performance issue are you facing?Vacillatory
Side note: don’t use ViewCompat , use RecyclerView#setNestedScrollingEnabled. RecyclerView comes from support libraries and is already backporting its functionality to older APIs.Sloop
@Toon did you find a solution?Nik
@Kevinrob Nope yet!! :(Toon
Hi @MAY3AM. Got any solution? I have multiple horizontal scrolling RecyclerView's inside NestedScrollView. Have added- setNestedScrollingEnabled(true) and facing the same issue (recyclerview is not recycling)Pneumothorax
@WijaySharma Hi there, unfortunately, I've got nothing :(Toon
R
10

This is because we have a recycler view which has scroll behaviour inside a scroll view. (scroll inside a scroll)

I think the best way to resolve this issue is to your profileCardview as a header in your recycler view and then remove the nested scroll view.

If it were a listview then it was as simple as listView.addHeaderView(profileCardView) but for the Recycler view there is no addheadview equivalent. Hence you could refer the below link to change your implementation.

Is there an addHeaderView equivalent for RecyclerView?

Rozina answered 10/6, 2016 at 11:28 Comment(1)
What if the requirement of the design team is more complex than this? In my case, the "header" has a TabLayout (and some simple views above), and needs to be scrolled nicely so that the TabLayout alone would stay at the top of the screen when scrolling. Below the TabLayout, there is a ViewPager of 2 RecyclerViews as pages (belong to the TabLayout so it has 2 tabs). In this case, there is no single RecyclerView, and I'm not sure how to have the scrolling stop (pin at the top) for some Views of the RecyclerView even if I had just one.Mellen
O
4

For a RecyclerView or ListView the height should be constant, because if it will not a constant size then how it will manage the maximum number of visible rows in memory. Try by changing RecyclerView attribute android:layout_height="match_parent" or a fixed height(e.g. "300dp" - as needed), instead of "wrap_content". It should improve your memory management.

Ours answered 19/6, 2016 at 9:45 Comment(2)
@CliveJefferies Setting layout_height to a fixed size will reduce re-drawing when a sub-view is updated. You are kind of saying "don't worry - no need to re-draw this view if my subviews change because it has a fixed size". You could, in theory, say that it might reduce memory footprints, but mainly it will eliminate UI-blocking garbage collections which will make your app smoother.Gauhati
But what happens if all is supposed to scroll, so that some part above the RecyclerView should pin ? Would you need to keep setting the height of the RecyclerView based on scrolling listener ?Mellen

© 2022 - 2024 — McMap. All rights reserved.