#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. Есть некоторые другие проблемы, которые могут сделать ваш список запаздывающим, например, изображения (используйте что-то вроде Пикассо для изменения размера изображений).