简体   繁体   English

Recyclerview - 为每个列表项调用 onCreateViewHolder

[英]Recyclerview - onCreateViewHolder called for each list item

I have implemented a simple adapter but it is causing RecyclerView not to recycler views and calls onCreateViewHolder() for every list item when scrolled.我已经实现了一个简单的适配器,但它导致 RecyclerView 不回收视图并在滚动时为每个列表项调用onCreateViewHolder() This causes jank whenever I scroll the list.每当我滚动列表时,这都会导致卡顿。 Few points listed below are not related to excessive calls of onCreateViewHolder() , but I tried them to improve scroll performance and avoid jank.下面列出的几点与onCreateViewHolder()的过度调用无关,但我尝试使用它们来提高滚动性能并避免卡顿。 Things I have tried so far:到目前为止我尝试过的事情:

  1. recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
  2. recyclerView.recycledViewPool.setMaxRecycledViews(1, 10) with recyclerView.setItemViewCacheSize(10) recyclerView.recycledViewPool.setMaxRecycledViews(1, 10) 和 recyclerView.setItemViewCacheSize(10)
  3. recyclerView.setDrawingCacheEnabled(true) with recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH) recyclerView.setDrawingCacheEnabled(true) 与 recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH)
  4. setting RecyclerView height to "match_parent"将 RecyclerView 高度设置为“match_parent”
  5. Was previously using Kotlin's synthetic, now moved to Android's ViewBinding之前是用Kotlin的synthetic,现在移到Android的ViewBinding
  6. Rewrite complex nested layouts to Constraint Layout将复杂的嵌套布局重写为约束布局
  7. override onFailedToRecycleView() to see if it is called, but it was never called覆盖onFailedToRecycleView()以查看它是否被调用,但它从未被调用

Here is my adapter:这是我的适配器:

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.suppstore.R
import com.example.suppstore.common.Models.Brand
import com.example.suppstore.databinding.LiBrandBinding
import com.google.firebase.perf.metrics.AddTrace

class BrandsAdapter(list: ArrayList<Brand>, var listener: BrandClickListener?) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private val VIEW_TYPE_LOADING = 0
    private val VIEW_TYPE_NORMAL = 1
    private var brandsList: ArrayList<Brand> = list

    @AddTrace(name = "Brands - onCreateViewHolder", enabled = true)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return if (viewType == VIEW_TYPE_NORMAL) {
            ViewHolder(
                LiBrandBinding.inflate(
                    LayoutInflater.from(parent.context),
                    parent, false
                )
            )

        } else {
            LoaderHolder(
                LayoutInflater.from(parent.context)
                    .inflate(R.layout.li_loader, parent, false)
            )
        }
    }

    @AddTrace(name = "Brands - onBindViewHolder", enabled = true)
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is ViewHolder)
            holder.setData(brandsList[position], listener!!)
    }

    class ViewHolder(itemView: LiBrandBinding) : RecyclerView.ViewHolder(itemView.root) {
        private val binding: LiBrandBinding = itemView

        @AddTrace(name = "Brands - ViewHolder-setData", enabled = true)
        fun setData(brand: Brand, listener: BrandClickListener) {
            binding.cardItem.setOnClickListener { listener.onItemClick(brand) }
            binding.tvBrandName.text = brand.name
            binding.tvCount.text = brand.count.toString() + " Products"
        }
    }

    class LoaderHolder(itemView: View) : RecyclerView.ViewHolder(itemView.rootView) {

    }

    @AddTrace(name = "Brands - addLoader", enabled = true)
    fun addLoader() {
        brandsList.add(Brand())
        notifyItemInserted(brandsList.size - 1)

    }

    @AddTrace(name = "Brands - setData", enabled = true)
    fun setData(newList: ArrayList<Brand>) {
        this.brandsList = newList
        notifyDataSetChanged()

    }

    @AddTrace(name = "Brands - removeLoader", enabled = true)
    fun removeLoader() {
        if (brandsList.size == 0)
            return
        val pos = brandsList.size - 1
        brandsList.removeAt(pos)
        notifyItemRemoved(pos)

    }

    override fun getItemViewType(position: Int): Int {
        return if (brandsList.get(position).count == -1) {
            VIEW_TYPE_LOADING
        } else
            VIEW_TYPE_NORMAL

    }

    interface BrandClickListener {
        fun onItemClick(brand: Brand)
    }

    override fun getItemCount(): Int {
        return brandsList.size
    }
}

Here is the list item (li_brand):这是列表项(li_brand):

<?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"
    android:id="@+id/cardItem"
    android:layout_width="match_parent"
    android:layout_height="85dp"
    android:background="@color/app_black">

    <TextView
        android:id="@+id/tvBrandName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:textColor="@color/app_yellow"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@id/tvCount"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />

    <TextView
        android:id="@+id/tvCount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:layout_marginTop="2dp"
        android:textColor="@color/app_grey"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvBrandName" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="15dp"
        android:src="@drawable/ic_baseline_arrow_forward_ios_24"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:layout_width="match_parent"
        android:layout_height="3dp"
        android:background="@color/app_bg"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Here are related functions in Fragment以下是Fragment中的相关函数

class BrandsFragment : Fragment() {
    private val adapter = BrandsAdapter(ArrayList(), brandClickListener())
    fun brandClickListener(): BrandsAdapter.BrandClickListener {
        return object : BrandsAdapter.BrandClickListener {
            override fun onItemClick(brand: Brand) {
                activityViewModel?.setSelectedBrand(brand)
            }
        }
    }

    fun setupRecyclerView() {
        val llManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
        binding.recyclerView.layoutManager = llManager
        binding.recyclerView.setHasFixedSize(true)
        binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                if (dy > 0) { //check for scroll down
                    val visibleItemCount = llManager.childCount
                    val totalItemCount = llManager.itemCount
                    val firstVisibleItemPos = llManager.findFirstVisibleItemPosition()
                    if (loadWhenScrolled
                        && visibleItemCount + firstVisibleItemPos >= totalItemCount
                        && firstVisibleItemPos >= 0
                    ) {
                        //ensures that last item was visible, so fetch next page
                        loadWhenScrolled = false
                        viewModel.nextPage()
                    }
                }
            }
        })
        binding.recyclerView.adapter = adapter
    }
}

And here is the fragment xml:这是片段 xml:

<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/app_black"
    android:focusableInTouchMode="true"
    android:orientation="vertical"
    tools:context=".Brands.BrandsFragment">

    <androidx.appcompat.widget.SearchView
        android:id="@+id/searchView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:background="@drawable/bottom_line_yellow"
        android:theme="@style/SearchViewTheme"
        app:closeIcon="@drawable/ic_baseline_close_24"
        app:iconifiedByDefault="false"
        app:queryBackground="@android:color/transparent"
        app:queryHint="Atleast 3 characters to search"
        app:searchIcon="@drawable/ic_baseline_search_24" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

Have you tried RecyclerView Diffutil class?您是否尝试过 RecyclerView Diffutil class? Hope it will resolve smooth scrolling issue and overwhelm recreation of items.希望它将解决平滑滚动问题并压倒项目的娱乐。

https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil

"DiffUtil is a utility class that calculates the difference between two lists and outputs a list of update operations that converts the first list into the second one." “DiffUtil 是一个实用程序 class,它计算两个列表之间的差异并输出将第一个列表转换为第二个列表的更新操作列表。”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM