Android RecyclerView不会回收项目。

huangapple go评论52阅读模式
英文:

Android RecyclerView doesn't recycle items

问题

我有一个recyclerView,其中包含多个recyclerView,可以动态膨胀。服务器返回的响应是一个包含对象的列表,每个对象都包含一个列表和用于recyclerView类型(VerticalHorizontal)LinearLayout的属性。

一切都正常,只是当方向是垂直时,适配器会等待一次性加载所有视图(停止回收)。

我已经搜索了很多,但没有找到解决方案。

主要的HomeFragment内部的RecyclerView

<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.gt.gcook.ui.home.HomeFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:nestedScrollingEnabled="false"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/home_single_item" />
</androidx.constraintlayout.widget.ConstraintLayout>

home_single_item布局(包含2个TextViewRecyclerView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/seeAllTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="24dp"
        android:text="See all"
        android:textColor="@color/color_yellow"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/titleTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginTop="32dp"
        android:text="Catigories"
        android:textColor="#707070"
        android:textSize="22sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:nestedScrollingEnabled="false"
        app:reverseLayout="false"
        android:clipToPadding="false"
        app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
        app:layoutConstraintBottom_toBottomOf="parent"
        app:layoutConstraintEnd_toEndOf="parent"
        app:layoutConstraintStart_toStartOf="parent"
        app:layoutConstraintTop_toBottomOf="@+id/titleTV" />

</androidx.constraintlayout.widget.ConstraintLayout>

HomeRecyclerViewonBindViewHolder

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    when (position) {
        0 -> {
            holder.itemView.titleTV.text = "Categories"
            val mLayoutManager = LinearLayoutManager(holder.itemView.context)
            mLayoutManager.orientation = RecyclerView.HORIZONTAL
            holder.itemView.recyclerView.layoutManager = mLayoutManager
            holder.itemView.recyclerView.adapter = HomeCategoryAdapter()
        }
        1 -> { //这里是问题所在,当方向是垂直时。
            holder.itemView.titleTV.text = "Most rated recipes"
            val mLayoutManager = GridLayoutManager(holder.itemView.context, 2)
            mLayoutManager.orientation = RecyclerView.VERTICAL
            holder.itemView.recyclerView.layoutManager = mLayoutManager
            holder.itemView.recyclerView.adapter = MostRatedRecipeAdapter(clickListener)
        }
        else -> {
            holder.itemView.titleTV.text = "Favourite"
            val mLayoutManager = LinearLayoutManager(holder.itemView.context)
            mLayoutManager.orientation = RecyclerView.HORIZONTAL
            holder.itemView.recyclerView.layoutManager = mLayoutManager
            holder.itemView.recyclerView.adapter = HomeCategoryAdapter()
        }
    }
}
英文:

I have A recyclerView that have multiple recyclerView with a dynamic inflation, the response I

am getting from the server is a list that contains objects each one contains a list and attributes for the

recyclerView type (Vertical or Horizontal) LinearLayout.

everything works fine except that when the orienatation is Vertical the adapter wait to load all Views at once (stop recycling).

I have searched a lot to find a solution but nothing.

the Main RecyclerView inside HomeFragment

&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;com.gt.gcook.ui.home.HomeFragment&quot;&gt;

    &lt;androidx.recyclerview.widget.RecyclerView
        android:id=&quot;@+id/recyclerView&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        android:orientation=&quot;vertical&quot;
        android:nestedScrollingEnabled=&quot;false&quot;
        app:layoutManager=&quot;androidx.recyclerview.widget.LinearLayoutManager&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        tools:listitem=&quot;@layout/home_single_item&quot; /&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;

home_single_item layout (contains of 2 TextView and RecyclerView)

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;&gt;

    &lt;TextView
        android:id=&quot;@+id/seeAllTV&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_marginTop=&quot;32dp&quot;
        android:layout_marginEnd=&quot;24dp&quot;
        android:text=&quot;See all&quot;
        android:textColor=&quot;@color/color_yellow&quot;
        android:textSize=&quot;16sp&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;

    &lt;TextView
        android:id=&quot;@+id/titleTV&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_marginStart=&quot;24dp&quot;
        android:layout_marginTop=&quot;32dp&quot;
        android:text=&quot;Catigories&quot;
        android:textColor=&quot;#707070&quot;
        android:textSize=&quot;22sp&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;

    &lt;androidx.recyclerview.widget.RecyclerView
        android:id=&quot;@+id/recyclerView&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_marginTop=&quot;24dp&quot;
        android:nestedScrollingEnabled=&quot;false&quot;
        app:reverseLayout=&quot;false&quot;
        android:clipToPadding=&quot;false&quot;
        app:layoutManager=&quot;androidx.recyclerview.widget.GridLayoutManager&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/titleTV&quot; /&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;

onBindViewHolder of Home RecyclerView .

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    when (position) {
        0 -&gt; {
            holder.itemView.titleTV.text = &quot;Categories&quot;
            val mLayoutManager = LinearLayoutManager(holder.itemView.context)
            mLayoutManager.orientation = RecyclerView.HORIZONTAL
            holder.itemView.recyclerView.layoutManager = mLayoutManager
            holder.itemView.recyclerView.adapter = HomeCategoryAdapter()


        }
        1 -&gt; { //here is the problem when Orintation is vertical.
            holder.itemView.titleTV.text = &quot;Most rated recipes&quot;
            val mLayoutManager = GridLayoutManager(holder.itemView.context, 2)
            mLayoutManager.orientation = RecyclerView.VERTICAL
            holder.itemView.recyclerView.layoutManager = mLayoutManager
            holder.itemView.recyclerView.adapter = MostRatedRecipeAdapter(clickListener)


        }
        else -&gt; {
            holder.itemView.titleTV.text = &quot;Favourite&quot;
            val mLayoutManager = LinearLayoutManager(holder.itemView.context)
            mLayoutManager.orientation = RecyclerView.HORIZONTAL
            holder.itemView.recyclerView.layoutManager = mLayoutManager
            holder.itemView.recyclerView.adapter = HomeCategoryAdapter()
        }
    }
}

答案1

得分: 5

以下是翻译好的部分:

由于您的内部RV的高度设置为wrap_content,因此无法为该情况启用回收。当它是水平的时候,回收是有效的,因为它的widthmatch_parent,而屏幕宽度是固定的。只有在定义了高度的情况下才能进行回收!

然而,正如我所说,有优化的空间:

现在,您是在父RVonBindViewHolder()方法内创建内部RV的适配器,从而迫使内部RVRV决定重用其先前的ViewHolders时每次都要膨胀ViewHolders以重新绑定新值。

您可以在RVonCreateViewHolder()方法内为内部RV创建适配器,并在OnBindViewHolder()内提交新的列表给内部适配器(随后调用notifyDatasetChanged())。

上述方法应该能够在性能上进行改进,至少在后续滚动时:回收将在RV滚动时起作用,这意味着内部RV将保留其ViewHolders并在某种程度上重用它们。

不幸的是,我现在没有时间为您的情况提供精确的代码片段,但您可以查看我的另一个答案:
https://stackoverflow.com/questions/63045236/show-values-of-same-key-in-one-textview-in-recyclerview/63118092#63118092

英文:

Since your inner RV's height is set to wrap_content there is no way to enable recycling for that case. When its horizontal, recycling is working because it's width is match_parent and screen width is fixed. Recycling works only when there is defined height!

However, as i said there is room for optimization:

Right now, you are creating adapter for inner RV inside parent RV's onBindViewHolder() method, thus forcing inner RV to inflate ViewHolders every time parent RV decides to reuse it's previous ViewHolders via rebinding new values.

You can create adapters for inner RV inside onCreateVoewHolder() of parent RV and submit new list to inner adapter ( subsequently calling notifyDatasetChanged() ) inside OnBindViewHolder().

The above approach should certainly improve performance, at least on subsequent scrolling: recycling will work on parent RV scroll, meaning inner RV will retain its ViewHolders and reuse them to some degree.

Unfortunately, i don't have time to post snippet precisely for your scenario but you can have a look at my another answer:
https://stackoverflow.com/questions/63045236/show-values-of-same-key-in-one-textview-in-recyclerview/63118092#63118092

huangapple
  • 本文由 发表于 2020年7月28日 17:42:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/63131286.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定