【发布时间】:2021-03-06 17:16:35
【问题描述】:
所以我有这个 recyclerview,它将包含多种类型的持有者,其中一种可能是边缘到边缘图像的可滚动水平列表,它们会自动滚动并具有当前项目指示器。所以为此我使用了一个viewholder,它本身将包含另一个recyclerview和一个点指示器(它本身是另一个recycler view,所以基本上recyclerview = vh列表,其中一个vh = 2水平recyclerview)。
title
[A,B,C,D...]
[+ ---]
title
[A,B,C,D...]
[+ --]
title
[A,B,C,D...]
[+ --]
title
[A,B,C,D...]
[+ --]
我最里面的水平图像循环视图是这样创建的:
class ImageAdapter : RecyclerView.Adapter<ImageVH>() {
var imageResList = mutableListOf<Int>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ImageVH(parent, viewType)
override fun onBindViewHolder(holder: ImageVH, pos: Int)
= holder.bindData(imageResList[pos % imageResList.size])
override fun getItemCount() = Int.MAX_VALUE
}
class ImageVH(v: View) : RecyclerView.ViewHolder(v) {
constructor(parent: ViewGroup, viewtype: Int) : this(
LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false)
)
fun bindData(imageRes: Int) {
Glide.with(itemView.context).load("").error(imageRes).into(itemView.ivImage)
}
}
这基本上是在愚弄适配器,以为我有一百万张图片,但实际上只有几张图片。这会产生一种循环滚动的印象。
接下来我需要更改第二个 recyclerview 的点指示器。为此,我进入了这个 recyclerview 的父级并附加了一个 onScrollListener 。 onScrollListener 给了我 2 个函数:onScrolled 和 onScrollStateChanged。
- 使用
onScrolled,我确定何时更改下一个点recyclerview 的状态以显示新点。我通过线性布局管理器做到这一点。当它给出findFirstCompletelyVisibleItemPosition为正数时。 - 使用
onScrollStateChanged(),我运行一种递归,每当我获得状态为 SCROLL_STATE_IDLE 时,我发布一个处理程序以在 2 秒后将回收器视图滚动到下一个项目。 2 秒后,它会自动平滑滚动并再次触发相同的事件,导致处理程序再次触发相同的操作。
所以代码看起来像这样:
data class Rails(val title: String, val images: MutableList<Int>,val autoscroll:Boolean =false)
class RailsAdapter : RecyclerView.Adapter<RailVH>() {
var railsList = mutableListOf<Rails>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = RailVH(parent, viewType)
override fun onBindViewHolder(holder: RailVH, pos: Int) = holder.bindData(railsList[pos])
override fun getItemCount() = railsList.size
}
class RailVH(v: View) : RecyclerView.ViewHolder(v) {
constructor(parent: ViewGroup, viewtype: Int) : this(
LayoutInflater.from(parent.context).inflate(R.layout.item_rails, parent, false)
)
private var autoscrollImages = false
fun bindData(rails: Rails) {
autoscrollImages = rails.autoscroll
with(itemView) {
tvTitle?.text = rails.title
rvImagers?.apply {
adapter = ImageAdapter().also {
it.imageResList = rails.images
it.notifyDataSetChanged()
}
PagerSnapHelper().attachToRecyclerView(this)
isNestedScrollingEnabled = false
onFlingListener = null
addOnScrollListener(onScrollListener)
}
}
if(autoscrollImages){
bannerChangerHandler.postDelayed(bannerChangerRunnable,bannerChangerDelayMilllis)
}
}
private val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
//super.onScrolled(recyclerView, dx, dy)
val bannerLLManager = itemView.rvImagers?.layoutManager as? LinearLayoutManager
bannerLLManager?.let { linearLayoutManager ->
val bannerCurrentPos = linearLayoutManager.findFirstCompletelyVisibleItemPosition()
if (bannerCurrentPos >= 0) {
val rvDotsDataListSize = 5
val positionInRange = bannerCurrentPos % rvDotsDataListSize
Toast.makeText(
itemView.context,
"highlight dot #$positionInRange",
Toast.LENGTH_SHORT
).show()
}
}
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
//super.onScrollStateChanged(recyclerView, newState)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> {
if(autoscrollImages){
Log.e(">>a>>", "RecyclerView.SCROLL_STATE_IDLE!")
bannerChangerHandler.postDelayed(bannerChangerRunnable, bannerChangerDelayMilllis
)
}
}
RecyclerView.SCROLL_STATE_DRAGGING -> {
Log.e(">>a>>", "RecyclerView.SCROLL_STATE_DRAGGING!")
bannerChangerHandler.removeCallbacks(bannerChangerRunnable)
}
else -> {
}
}
}
}
private val bannerChangerHandler: Handler = Handler()
private val bannerChangerRunnable = Runnable {
itemView.rvImagers?.apply {
val bannerManager = layoutManager as? LinearLayoutManager
bannerManager?.let {
val bannerCurrentPos = it.findFirstCompletelyVisibleItemPosition()
smoothScrollToPosition(bannerCurrentPos + 1)
}
}
}
private var bannerChangerDelayMilllis = 2000L
}
为简洁起见,假设每当 toast 发生时,它都会滚动第二个点指示器 recyclerview。
这一切似乎在原则上有效,但有时处理程序似乎会触发两次或三次,导致用户体验不佳。有时它甚至会发疯,停止显示任何日志或任何内容,只是让 Rails 运行得非常快,就像处理程序每毫秒触发一个自动滚动运行器一样。
那么有什么帮助吗?我假设在实现级别出现了问题,例如可以更好地处理触发处理程序事件?
更新: 感谢@ADM,我得到了这个工作。我根据我的要求对其进行了调整,并且不得不放弃对反向循环滚动的支持,但给定的解决方案足以回答我的问题。谢谢!
【问题讨论】:
-
您一次又一次地发布相同的
Runnable即bannerChangerRunnable这可能会造成麻烦,因为bindViewHolder在正常情况下可以在该时间段内多次调用,而且您正在使用和延迟处理程序这也是为什么。解决方案是因为每个内部RecyclerView都拥有一个单独的ImageAdapter实例,您应该将处理程序实现移动到ImageAdapter。您可以传入一个 lambda 以在run内部调用。 -
@ADM 但是将处理程序传递给
ImageAdapter有什么用?目前,它的MainActivity<--RailsRecycler(+RailsAdapter)<--ImageRecycler(+ImageAdapter)railsAdpater 将为 ImageRecycler 创建一个视图,该视图将有权更改图像回收器的位置。这种权力如何转移到内部图像回收器?您能否发布一个答案并提供更多解释,因为我似乎无法想象
标签: android kotlin android-recyclerview