Вложенный Recyclerview делает прокрутку медленной

#android #kotlin #android-recyclerview #nestedrecyclerview #multiviews

Вопрос:

Я использую recyclerview с несколькими типами просмотра и использую вложенные recyclerview:

введите описание изображения здесь

Но его прокрутка очень медленная. Я также обработал положение прокрутки дочерних переработанных просмотров в функции onViewRecycled:

 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()
    }
}
 

И в держателе onBindViewHolder

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

Я тоже пытался:

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

Я использую ListAdapter с DiffUtils. Я также попытался установить адаптер для дочерних переработчиков в onCreateViewHolder и отправить список из onBindViewHolder, а также попытался установить адаптер из onBindViewHolder. Но во всех случаях основной вид переработчика перерабатывает представления и отстает. Я не хочу отключать переработку просмотра вторсырья.

Может ли кто-нибудь подсказать мне, что я здесь делаю не так.

Код:

 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 amp;amp; 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)
    }

}
 

Комментарии:

1. можете ли вы поделиться кодом, который вы пытаетесь использовать? Является ли вложенный recyclerview с фиктивными элементами, которые не зависят от Интернета пользователя, также имеющим запаздывающее поведение? Недавно я реализовал вложенный просмотр вторсырья с сохранением правильного положения прокрутки вложенных просмотров вторсырья и хотел бы помочь вам, если вы предоставите более подробную информацию о своей проблеме. Может быть, вы можете начать разговор здесь или продолжить в комментариях 🙂

2. @mehulbisht пожалуйста, проверьте вопрос, я добавил некоторый код.

3. хорошо, я сообщу вам об этом 🙂

4. @WaqarVicky Вместо добавления частей кода лучше загрузить функциональный пример на Github, чтобы мы могли легко отлаживать.

5. Есть некоторые другие проблемы, которые могут сделать ваш список запаздывающим, например, изображения (используйте что-то вроде Пикассо для изменения размера изображений).