Nested Recyclerview makes scroll laggy

360 Views Asked by At

I am using recyclerview with multi view type and using nested recyclerviews:

enter image description here

But its scrolling very slow. I also handled the child recyclerviews scroll position in onViewRecycled function:

override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
    super.onViewRecycled(holder)
    //save horizontal scroll state
    val key = getSectionID(holder.layoutPosition)

    if (holder is HomeSectionsViewHolder) {
        scrollStates[key] =
            holder
                .itemView
                .findViewById<RecyclerView>(R.id.itemsList)
                .layoutManager?.onSaveInstanceState()
    }
}

And in the onBindViewHolder

val state = scrollStates[key]
        if (state != null) {
            itemBinding.itemsList.layoutManager?.onRestoreInstanceState(state)
        } else {
            itemBinding.itemsList.layoutManager?.scrollToPosition(0)
        }

I also tried:

binding.rvHome.recycledViewPool.setMaxRecycledViews(0, 0);

I am using ListAdapter with DiffUtils. I also tried to set adapter to child recyclerviews in the onCreateViewHolder and submit the list from onBindViewHolder and also tried setting adapter from onBindViewHolder. But in all cases main recycler view recycling the views and lagging. I dont want to disable the recycling of the recyclerview.

Can anyone suggest me what i am doing wrong here.

Code:

class MainAdapter(
    private val context: Context,
    private val currentLocale: String,
    private val currentCountry: String,
    private val homeRepository: HomeRepository
) :
    ListAdapter<Section, RecyclerView.ViewHolder>(Companion) {

    var selectedCondition: List<Int?>? = emptyList()
    var selectedBrand: MutableList<Int?>? = arrayListOf()

    private var carousalListBinded: Boolean = false

    private var carousalList: List<CarouselItem>? = null
    private var banners: List<Banner>? = null

    private var carousel: ImageCarousel? = null
    private var COVIDMsg: String? = null
    private var COVIDMsgColor: String? = null
    private var tvCovidMessage: TextView? = null

    private var categoryAdapter = CategoryAdapter(context)



    companion object : DiffUtil.ItemCallback<Section>() {
        override fun areItemsTheSame(oldItem: Section, newItem: Section): Boolean =
            oldItem == newItem

        override fun areContentsTheSame(oldItem: Section, newItem: Section): Boolean =
            oldItem == newItem

        const val VIEW_TYPE_CATEGORY = 0
        const val VIEW_TYPE_ITEM_SECTION = 1
        const val VIEW_TYPE_BRAND = 2
        const val VIEW_TYPE_IMAGE = 3
        const val VIEW_TYPE_ADVANCE_IMAGE = 4
        const val VIEW_TYPE_ADVANCE_CATEGORY = 5
        const val VIEW_TYPE_BANNER = 6
        const val VIEW_TYPE_HOME_BOTTOM_SECTION = 7

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        when (viewType) {
            VIEW_TYPE_CATEGORY -> {

                val categoryViewHolder = CategoryViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_categories_item,
                        parent,
                        false
                    )
                )

                categoryViewHolder.initRecView()

                return categoryViewHolder

            }
            VIEW_TYPE_ITEM_SECTION -> {

                val homeSectionsViewHolder = HomeSectionsViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_item_section,
                        parent,
                        false
                    )
                )

                homeSectionsViewHolder.initRecView()

                return homeSectionsViewHolder
            }
            VIEW_TYPE_BRAND -> {

                val brandsViewHolder = BrandsViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_brands_item,
                        parent,
                        false
                    )
                )

                brandsViewHolder.initRecView()

                return brandsViewHolder
            }
            VIEW_TYPE_IMAGE -> {

                val imagesViewHolder = ImagesViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_brands_item,
                        parent,
                        false
                    )
                )

                imagesViewHolder.initRecView()

                return imagesViewHolder

            }
            VIEW_TYPE_ADVANCE_IMAGE -> {

                val advanceImagesViewHolder = AdvanceImagesViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_brands_item,
                        parent,
                        false
                    )
                )

                advanceImagesViewHolder.initRecView()

                return advanceImagesViewHolder
            }
            VIEW_TYPE_ADVANCE_CATEGORY -> {

                val categorySectionViewHolder = CategorySectionViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_item_section,
                        parent,
                        false
                    )
                )

                categorySectionViewHolder.initRecView()

                return categorySectionViewHolder

            }
            VIEW_TYPE_BANNER -> {

                val binding: HomeBannerItemBinding = DataBindingUtil.inflate(
                    LayoutInflater.from(parent.context),
                    R.layout.home_banner_item,
                    parent,
                    false
                )


                binding.carousel.post {
                    val numberItemsInColumn = 1
                    val collectionViewWidth = getWidth(context)
                    val widthPerCell = collectionViewWidth / numberItemsInColumn
                    val widthPerItem = widthPerCell
                    val cellHeight = getCellHeight(widthPerItem)

                    binding.carousel.layoutParams.width = widthPerItem
                    binding.carousel.layoutParams.height = cellHeight

                    binding.carousel.requestLayout()
                }

                return HomeBannerViewHolder(
                    binding
                )

            }
            VIEW_TYPE_HOME_BOTTOM_SECTION -> {
                return HomeBottomLayViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_screen_bottom_layout,
                        parent,
                        false
                    )
                )
            }
            else -> {

                val categoryViewHolder = CategoryViewHolder(
                    DataBindingUtil.inflate(
                        LayoutInflater.from(parent.context),
                        R.layout.home_categories_item,
                        parent,
                        false
                    )
                )

                categoryViewHolder.initRecView()

                return categoryViewHolder
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (getItemViewType(position)) {
            VIEW_TYPE_CATEGORY -> {

                (holder as CategoryViewHolder).bind(position)
            }
            VIEW_TYPE_ITEM_SECTION -> {

                (holder as HomeSectionsViewHolder).bind(position)

            }
            VIEW_TYPE_BRAND -> {
                (holder as BrandsViewHolder).bind(position)
            }
//
            VIEW_TYPE_IMAGE -> {
                (holder as ImagesViewHolder).bind(position)
            }
//
            VIEW_TYPE_ADVANCE_IMAGE -> {
                (holder as AdvanceImagesViewHolder).bind(position)
            }

            VIEW_TYPE_ADVANCE_CATEGORY -> {
                (holder as CategorySectionViewHolder).bind(position)
            }

            VIEW_TYPE_BANNER -> {
//                (holder as HomeBannerViewHolder).setIsRecyclable(false)
                (holder as HomeBannerViewHolder).bind()
            }

            VIEW_TYPE_HOME_BOTTOM_SECTION -> {
                (holder as HomeBottomLayViewHolder)
            }
        }

//        holder.itemView.setFadeAnimation()

    }

    override fun getItemViewType(position: Int): Int {

        return when (currentList[position].sectionType) {
            SectionType.items -> {
                VIEW_TYPE_ITEM_SECTION
            }
            SectionType.brands -> {
                VIEW_TYPE_BRAND
            }
            SectionType.images -> {
                VIEW_TYPE_IMAGE
            }
            SectionType.advanceimages -> {
                VIEW_TYPE_ADVANCE_IMAGE
            }
            SectionType.category -> {
                VIEW_TYPE_ADVANCE_CATEGORY
            }
            SectionType.banners -> {
                VIEW_TYPE_BANNER
            }
            SectionType.homeCategories -> {
                VIEW_TYPE_CATEGORY
            }
            SectionType.homeBottomView -> {
                VIEW_TYPE_HOME_BOTTOM_SECTION
            }
            else -> {
                VIEW_TYPE_CATEGORY
            }
        }

    }

    inner class CategoryViewHolder(private val itemBinding: HomeCategoriesItemBinding) :
        RecyclerView.ViewHolder(
            itemBinding.root
        ) {

        fun initRecView() {
            itemBinding.rvCategoryItem.adapter = categoryAdapter
        }

        fun bind(position: Int) {

//            val newItem = (currentList[position] as CategorySection).items
//            categoryAdapter.submitList(newItem)


        }
    }

    inner class HomeSectionsViewHolder(private val itemBinding: HomeItemSectionBinding) :
        RecyclerView.ViewHolder(
            itemBinding.root
        ) {

        private var sectionsAdapter = ItemSectionsAdapter(context)

        fun initRecView() {
            itemBinding.itemsList.adapter = sectionsAdapter
        }

        fun bind(position: Int) {

            val section = getItem(position)

            itemBinding.section = section
            itemBinding.executePendingBindings()

            sectionsAdapter.submitList(null)

            val newItem = (currentList[position] as ItemSection).items
            sectionsAdapter.submitList(newItem)

//            val state = scrollStates[key]
//            if (state != null) {
//                itemBinding.rvProducts.layoutManager?.onRestoreInstanceState(state)
//            } else {
//                itemBinding.rvProducts.layoutManager?.scrollToPosition(0)
//            }

        }

    }

    inner class BrandsViewHolder(private val itemBinding: HomeBrandsItemBinding) :
        RecyclerView.ViewHolder(itemBinding.root) {

        private val brandsAdapter = BrandsAdapter()

        fun initRecView() {
            itemBinding.itemsList.adapter = brandsAdapter
        }

        fun bind(position: Int) {

            itemBinding.sectionBtnViewAll.visibility = View.INVISIBLE

            val section = getItem(position)

            itemBinding.section = section
            itemBinding.executePendingBindings()

            brandsAdapter.submitList((currentList[position] as BrandSection).items)


        }

    }

    inner class ImagesViewHolder(private val itemBinding: HomeBrandsItemBinding) :
        RecyclerView.ViewHolder(itemBinding.root) {

        private val imagesAdapter = ImagesAdapter(context)
        val layoutManager = GridLayoutManager(context, 3)

        fun initRecView() {

            itemBinding.itemsList.let {

                it.layoutManager = layoutManager

                it.setHasFixedSize(true)
                it.adapter = imagesAdapter
            }

        }

        fun bind(position: Int) {

            val numberOfColumns = getNumberOfColumns((currentList[position] as ImageSection).items)

            itemBinding.itemsList.layoutManager = GridLayoutManager(context, numberOfColumns)

//            layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
//                override fun getSpanSize(position: Int): Int {
//                    return numberOfColumns
//                }
//            }

            itemBinding.sectionBtnViewAll.visibility = View.INVISIBLE

            val section = getItem(position)

            itemBinding.section = section
            itemBinding.executePendingBindings()


            imagesAdapter.submitList((currentList[position] as ImageSection).items)


        }


    }

    inner class AdvanceImagesViewHolder(private val itemBinding: HomeBrandsItemBinding) :
        RecyclerView.ViewHolder(itemBinding.root) {

        private var imagesAdapter = AdvanceImagesAdapter(context, null, null)

        fun initRecView() {

            itemBinding.itemsList.let {

//                val layoutManager = GridLayoutManager(context, getNumberOfColumns((currentList[position] as AdvanceImageSection).items))
                val layoutManager = GridLayoutManager(context, 3)

                it.layoutManager = layoutManager

                it.setHasFixedSize(true)
                it.adapter = imagesAdapter
            }

        }

        fun bind(position: Int) {

            val layoutManager = GridLayoutManager(
                context,
                getNumberOfColumns((currentList[position] as AdvanceImageSection).items)
            )

            itemBinding.itemsList.layoutManager = layoutManager

            val section = getItem(position)

//            var imagesAdapter = AdvanceImagesAdapter(context, section.columns, section.aspectRatio)
//
//            itemBinding.itemsList.adapter = imagesAdapter

//            val imagesAdapter = AdvanceImagesAdapter(section,context)
            itemBinding.sectionBtnViewAll.visibility = View.INVISIBLE

            itemBinding.section = section
            itemBinding.executePendingBindings()


            imagesAdapter.submitList((currentList[position] as AdvanceImageSection).items)
//
//
//
        }

    }

    inner class CategorySectionViewHolder(private val itemBinding: HomeItemSectionBinding) :
        RecyclerView.ViewHolder(itemBinding.root) {

        private val categoryAdapter = HomeCategorySectionAdapter(context)

        fun initRecView() {
            itemBinding.itemsList.let {

                val linearLayoutManager = LinearLayoutManager(
                    context,
                    LinearLayoutManager.HORIZONTAL,
                    false
                )


                it.layoutManager = linearLayoutManager

//                it.setHasFixedSize(true)
                it.adapter = categoryAdapter
            }
        }

        fun bind(position: Int) {

//            (currentList[position] as CategorySection)



            val section = getItem(position)
            itemBinding.section = section
            itemBinding.executePendingBindings()

            if (categoryAdapter.itemCount <= 0) {

                itemBinding.pb.show()

                GlobalScope.launch(Dispatchers.Main) {
                    val items = async(Dispatchers.IO) {
                        homeRepository.getSolarSearch(
                            getSolarSearchRequest(section)
                        )
                    }


                    when (items.await()) {
                        is Resource.Success -> {

                            itemBinding.pb.hide()
                            categoryAdapter.submitList((items.await() as Resource.Success<SolarSearchResponse>).value.items)
                        }
                        is Resource.Failure -> {
                            itemBinding.pb.hide()
                        }
                        is Resource.Loading -> {
                        }
                    }
                }

            }

//            itemBinding.sectionBtnViewAll.setOnClickListener {
//                homeSectionListener.onCatSectionViewAllClicked(section.categoryID.toString(),section)
//            }


        }

        private fun getSolarSearchRequest(section: Section): SolarSearchRequest {


            val result = section.categoryID!! % 100
            if (result == 0) {

                return SolarSearchRequest(
                    0,
                    null,
                    null,
                    25,
                    "popularity",
                    arrayListOf(section.categoryID),
                    emptyList<Int>(),
                    selectedCondition,
                    selectedBrand,
                    "1",
                    "15000"
                )


                //                    putIntegerArrayListExtra(CATEGORY_ID, arrayListOf(id))
            } else {
                //                putIntegerArrayListExtra(SUB_CAT_ID, arrayListOf(id))

                return SolarSearchRequest(
                    OFFSET,
                    null,
                    null,
                    PAGE_SIZE_LIMIT,
                    "popularity",
                    emptyList<Int>(),
                    arrayListOf(section.categoryID),
                    selectedCondition,
                    selectedBrand,
                    "1",
                    "15000"
                )

            }
        }
    }

    inner class HomeBannerViewHolder(private val itemBinding: HomeBannerItemBinding) :
        RecyclerView.ViewHolder(itemBinding.root) {


//        private val handler: Handler? = null
//        private val delay = 5000 //milliseconds
//        private var page = 0


        fun bind() {

//            if (carousalListBinded) {
//                return
//            }

            itemBinding.carousel.start()

            carousalList?.let {
                itemBinding.carousel.addData(it)
            }

            if (COVIDMsg.isNullOrEmpty()) {
                itemBinding.covidMessage.visibility = View.GONE
            } else {
                itemBinding.covidMessage.visibility = View.VISIBLE
            }

            itemBinding.covidMessage.text = COVIDMsg

            if (COVIDMsgColor != null) {
                (itemBinding.covidMessage.background as GradientDrawable).setColor(
                    Color.parseColor(
                        COVIDMsgColor
                    )
                )
            } else {
                (itemBinding.covidMessage.background as GradientDrawable).setColor(
                    context.getColor(
                        R.color.colorAccent
                    )
                )
            }

            carousel = itemBinding.carousel
            tvCovidMessage = itemBinding.covidMessage

            if (carousalList != null) {
                carousalListBinded = true
            }

          

        }

        fun openNewTabWindow(urls: String, context: Context) {
            val uris = Uri.parse(urls)
            val intents = Intent(Intent.ACTION_VIEW, uris)
            val b = Bundle()
            b.putBoolean("new_window", true)
            intents.putExtras(b)
            context.startActivity(intents)
        }

    }

    inner class HomeBottomLayViewHolder(private val itemBinding: HomeScreenBottomLayoutBinding) :
        RecyclerView.ViewHolder(
            itemBinding.root
        ) {
    }


    fun getNumberOfColumns(items: List<Image>): Int {

        var numberItemsInColumn: Int = 3

        if ((items.size) % 3 == 0) {
            numberItemsInColumn = 3
        } else if ((items.size) % 2 == 0) {
            numberItemsInColumn = 2
        } else if (items.size == 1) {
            numberItemsInColumn = 1
        } else {
            numberItemsInColumn = 3
        }
        return numberItemsInColumn
    }

    @RequiresApi(Build.VERSION_CODES.M)
    fun setBanners(
        carousalList: List<CarouselItem>?,
        bannersList: List<Banner>?,
        COVIDMsg: String?,
        COVIDMsgColor: String?
    ) {

        this.carousalList = carousalList
        this.banners = bannersList

        this.COVIDMsg = COVIDMsg
        this.COVIDMsgColor = COVIDMsgColor

        carousalList?.let { carousel?.addData(it) }

        if (COVIDMsg.isNullOrEmpty()) {
            tvCovidMessage?.visibility = View.GONE
        } else {
            tvCovidMessage?.visibility = View.VISIBLE
        }

        tvCovidMessage?.text = COVIDMsg

        if (COVIDMsgColor != null && tvCovidMessage != null) {

            (tvCovidMessage?.background as GradientDrawable).setColor(Color.parseColor(COVIDMsgColor))
        } else {
            if (tvCovidMessage != null) {
                (tvCovidMessage?.background as GradientDrawable).setColor(context.getColor(R.color.colorAccent))
            }
        }

    }

    fun getWidth(context: Context): Int {
        var width: Int = 0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val displayMetrics = DisplayMetrics()
            val display: Display? = context.display
            display!!.getRealMetrics(displayMetrics)
            return displayMetrics.widthPixels
        } else {
            val displayMetrics = DisplayMetrics()
            (context as Activity).windowManager.defaultDisplay.getMetrics(displayMetrics)
            width = displayMetrics.widthPixels
            return width
        }
    }

    fun getCellHeight(width: Int): Int {

        var height = width

        val ratio = 0.37
        val height1 = height * ratio
        height = height1.roundToInt()

        return height
    }

    fun setCategories(categoriesList: List<Category>?) {
        categoryAdapter.submitList(categoriesList)
    }

}
0

There are 0 best solutions below