当在NestedScrollView中使用RecyclerView时,RecyclerView无法回收视图

69

我正在NestedScrollView中使用RecyclerView。同时,我将RecyclerView的setNestedScrollingEnabled设置为false。

为了支持较低的API版本

ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);

现在!当用户滚动视图时,一切似乎都很好,但!!! RecyclerView 中的视图没有被回收!!并且堆大小迅速增长!!

更新:RecyclerView布局管理器是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();
            }

        }

    });

顺便说一下:我已将“加载更多”设置为滚动视图,因为recyclerview会持续执行且无法停止!

非常感谢任何帮助!


@MAY3AM,请更新您的代码和XML。 - appukrb
@appukrb 完成,代码和xml已添加。 - MAY3AM
14
如果在NestedScrollView中使用RecyclerView,则NestedScrollView会绘制所有子元素,因此适配器的所有元素都将一次性加载。更好的方法是仅使用RecyclerView,并在适配器中使用getItemViewType返回cardView,如果position==0,正如Rehan建议的那样。 - Pr38y
2
我通常跟随@Pr38y。你遇到了什么性能问题? - Tin Tran
@MAY3AM,你找到解决方案了吗? - Kevin Robatel
显示剩余10条评论
2个回答

10

这是因为我们有一个在滚动视图中具有滚动行为的Recycler View(即内嵌滚动)。

我认为解决此问题的最佳方法是将您的profileCardView作为Recycler View中的标题,然后删除嵌套的滚动视图。

如果它是一个ListView,那么只需使用listView.addHeaderView(profileCardView)就可以了,但对于Recycler View来说,没有addheadview等效的方法。 因此,您可以参考下面的链接来更改您的实现方式。

RecyclerView是否有addHeaderView等效方法?


如果设计团队的要求比这个更复杂怎么办?在我的情况下,“header”有一个TabLayout(和一些简单的视图),并且需要能够平滑地滚动,以便当滚动时,TabLayout仍然停留在屏幕顶部。在TabLayout下面,有一个包含两个RecyclerView作为页面的ViewPager(属于TabLayout,所以有2个选项卡)。在这种情况下,没有单独的RecyclerView,我不确定如何使某些RecyclerView的视图停止滚动(固定在顶部),即使只有一个RecyclerView。 - android developer

4

对于RecyclerView或ListView,高度应该是固定的,因为如果它不是一个常量大小,那么它将如何管理内存中可见行的最大数量。尝试通过更改RecyclerView属性android:layout_height="match_parent"或固定高度(例如"300dp" - 根据需要)来代替"wrap_content"。这应该有助于改善您的内存管理。


3
将layout_height设置为固定大小可减少在子视图更新时重新绘制的次数。这种做法可以理解为“不用担心,如果我的子视图更改了,这个视图不需要重新绘制,因为它有一个固定的大小”。你可以在理论上说这可能会减少内存占用,但主要是消除UI阻塞垃圾回收,使您的应用程序更加流畅。 - Arturs Vancans
但是如果所有内容都应该滚动,那么上面的一部分应该固定在RecyclerView上方会怎样?您需要根据滚动监听器不断设置RecyclerView的高度吗? - android developer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接