Android RecycleView 如何禁用手动滚动但允许单击项目

Android RecycleView how to disable manual scroll but allow item clicked

我正在研究一个想法,让 RecyclerView 自动滚动,但允许用户在不停止滚动的情况下单击项目。

首先,我创建了一个自定义 LayoutManager 来禁用手动滚动,同时更改滚动到某个位置的速度

class CustomLayoutManager(context: Context, countOfColumns: Int) :
GridLayoutManager(context, countOfColumns) {

// Custom smooth scroller
private val smoothScroller = object : LinearSmoothScroller(context) {
    override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float =
        500f / displayMetrics.densityDpi
}

// Disable manual scroll
override fun canScrollVertically(): Boolean = false

// Using custom smooth scroller to control the duration of smooth scroll to a certain position
override fun smoothScrollToPosition(
    recyclerView: RecyclerView,
    state: RecyclerView.State?,
    position: Int
) {
    smoothScroller.targetPosition = position
    startSmoothScroll(smoothScroller)
}
}

然后我为 RecyclerView 做初始工作,并在 1 秒后开始平滑滚动

viewBinding.list.apply {
        // initial recycler view
        setHasFixedSize(true)
        customLayoutManager = CustomLayoutManager(context = context, countOfColumns = 2)
        layoutManager = customLayoutManager

        // data list
        val dataList = mutableListOf<TestModel>()
        repeat(times = 100) { dataList.add(TestModel(position = it, clicked = false)) }

        // adapter
        testAdapter =
            TestAdapter(clickListener = { testAdapter.changeVhColorByPosition(position = it) })
        adapter = testAdapter
        testAdapter.submitList(dataList)

        // automatically scroll after 1 sec
        postDelayed({ smoothScrollToPosition(dataList.lastIndex) }, 1000)
    }

一切都按照我的预期进行,直到我发现当我点击 RecycelerView 上的任何项目时自动滚动停止,clickListener 触发时的功能只是更改 [=15= 中视图持有者的背景颜色]

fun changeVhColor(position: Int) {
    position
        .takeIf { it in 0..itemCount }
        ?.also { getItem(it).clicked = true }
        ?.also { notifyItemChanged(it) }
}

这里是录屏screen recording

我遇到的问题

  1. 当我点击 ReycelerView 上的任何项目时自动滚动停止
  2. 第一次点击停止滚动,第二次点击触发 clickListener,但我希望通过一次点击触发 clickListener

谁能告诉我如何解决这个问题?提前致谢。

这里发生了很多事情。您应该怀疑 RecyclerView 的触摸处理以及对 notifyItemChanged(it) 的调用,但我相信 RecyclerView 正在运行正确。您可以考虑覆盖 RecyclerView 中的触摸代码以使其执行您想要的操作 - 假设您可以访问并覆盖它。

另一种方法是用另一个透明视图覆盖 RecyclerView 并捕获透明视图上的所有触摸。然后,您可以为与 RecyclerView 交互的透明视图编写代码,以满足您的目标。这也很棘手,您将不得不对 RecyclerView 进行更改,因为它会在滚动发生时不断布局视图。由于您有自己的布局管理器,如果您在滚动发生时将更改排队pre-layout,这可能会更容易。

尝试了几种方法后,发现让recycler视图自动滚动的关键是override onInterceptTouchEvent

示例

class MyRecyclerView @JvmOverloads constructor(
     context: Context,
     attrs: AttributeSet? = null,
     defStyle: Int = 0
 ) : RecyclerView(context, attrs, defStyle) {
     override fun onInterceptTouchEvent(e: MotionEvent?): Boolean = false
}

这将使自定义 RecyclerView 忽略所有触摸事件