英文:
Android RecyclerView doesn't recycle items
问题
我有一个recyclerView
,其中包含多个recyclerView
,可以动态膨胀。服务器返回的响应是一个包含对象的列表,每个对象都包含一个列表和用于recyclerView
类型(Vertical
或Horizontal
)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个TextView
和RecyclerView
)
<?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>
Home
的RecyclerView
的onBindViewHolder
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
<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 layout (contains of 2 TextView
and RecyclerView
)
<?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:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/titleTV" />
</androidx.constraintlayout.widget.ConstraintLayout>
onBindViewHolder
of Home RecyclerView
.
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 -> { //here is the problem when Orintation is vertical.
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()
}
}
}
答案1
得分: 5
以下是翻译好的部分:
由于您的内部RV
的高度设置为wrap_content
,因此无法为该情况启用回收。当它是水平的时候,回收是有效的,因为它的width
是match_parent
,而屏幕宽度是固定的。只有在定义了高度的情况下才能进行回收!
然而,正如我所说,有优化的空间:
现在,您是在父RV
的onBindViewHolder()
方法内创建内部RV
的适配器,从而迫使内部RV
在父RV
决定重用其先前的ViewHolders
时每次都要膨胀ViewHolders
以重新绑定新值。
您可以在父RV
的onCreateViewHolder()
方法内为内部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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论