android - 在另一个 RecyclerView 内滚动 RecyclerView 自动无法正常工作
问题描述
所以我有这个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...]
[+ --]
我最里面的水平图像recylerview是这样创建的:
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 秒后将 recyclerview 滚动到下一个项目。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
}
为简洁起见,假设每当吐司发生时,它都会滚动第二个点指示器 recyclerview 。
这一切似乎在原则上有效,但有时处理程序似乎会触发两次或三次,导致用户体验不佳。有时它甚至会发疯,停止显示任何日志或任何东西,只是让轨道运行得非常快,就像处理程序每毫秒触发一个自动滚动运行器一样。
那么有什么帮助吗?我假设在实现级别出现问题,例如可以更好地处理触发处理程序事件?
更新: 感谢@ADM,我得到了这个工作。我根据我的要求对其进行了调整,并且不得不放弃对反向的圆形滚动支持,但给定的解决方案足以回答我的查询。谢谢!
解决方案
Handler
在这里不是问题Runnable
。您Runnable
每次都在使用和发布相同的内容,这就是它堆积起来的原因。您无法删除先前的呼叫,因为您没有Tag
此延迟呼叫的或令牌。看看这些Handler's
方法sendMessageDelayed
可能会有所帮助。
经过一番思考,我认为您可以将自动滚动部分移至SnapHelper
. 不是一个完整的证明解决方案,但我认为它会起作用。您可能需要在 SnapHelper
. 试一试,让我知道。我还没有测试过。
class AutoPagedSnapHelper(private var autoScrollInterval: Long) : PagerSnapHelper() {
private var recyclerView: RecyclerView? = null
private var currentPage = 0
private var isHold = false
private val autoScrollRunnable = Runnable {
recyclerView?.let {
if (recyclerView?.scrollState != RecyclerView.SCROLL_STATE_DRAGGING && !isHold) {
if (it.adapter != null) {
val lastPageIndex = (recyclerView?.adapter!!.itemCount - 1)
var nextIndex: Int
nextIndex = currentPage + 1
if (currentPage == lastPageIndex) {
nextIndex = 0
}
it.post {
val linearSmoothScroller = object : LinearSmoothScroller(recyclerView?.context) {
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi
}
}
linearSmoothScroller.targetPosition = nextIndex
(recyclerView?.layoutManager as LinearLayoutManager).startSmoothScroll(linearSmoothScroller)
}
}
} else {
postNextPage()
}
}
}
override fun attachToRecyclerView(recyclerView: RecyclerView?) {
super.attachToRecyclerView(recyclerView)
if (this.recyclerView === recyclerView) {
return
}
if (autoScrollInterval != 0L) {
this.recyclerView = recyclerView
this.recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_SETTLING) {
val itemPosition = (recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
if (itemPosition != -1) {
currentPage = itemPosition
postNextPage()
}
}
}
})
postNextPage()
recyclerView?.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onInterceptTouchEvent(rv: RecyclerView, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
isHold = true
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
isHold = false
}
}
return false
}
override fun onTouchEvent(rv: RecyclerView, event: MotionEvent) {}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
})
}
}
fun postNextPage() {
recyclerView?.handler?.removeCallbacks(autoScrollRunnable)
recyclerView?.postDelayed(autoScrollRunnable, autoScrollInterval)
}
companion object {
private const val MILLISECONDS_PER_INCH = 75f //default is 25f (bigger = slower)
}
}
这应该照顾自动更改页面。您不必使用scrollListener
in Adapter
。试试看。
推荐阅读
- actions-on-google - 是否可以深度链接到谷歌助手应用程序?
- html - Kendo-Grid 截断没有空格的字符串
- c# - 如何获取 httpStatusCode 200 的描述?
- c# - 在 VSTO 中将工作簿存储并打开为“资源”
- flutter - 如何使用颤振代码在颤振中创建这个弹跳方块?
- javascript - React-Firebase 与 webpack 未捕获的 std::bad_alloc 类型的异常:std::bad_alloc
- sql - ADQL 条件搜索
- linux - 关于cronjob执行时间
- python - 在 for loop python selenium web-driver 中运行 3 个变量以打印所有数据,包括链接
- vue.js - Cypress 监视 Vue 组件