首页 > 解决方案 > 在另一个 RecyclerView 内滚动 RecyclerView 自动无法正常工作

问题描述

所以我有这个rec​​yclerview,它将包含多种类型的持有者,其中一种可能是边缘到边缘图像的可滚动水平列表,这些图像正在自动滚动并具有当前项目指示器。所以为此我使用了一个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。

所以代码看起来像这样:

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,我得到了这个工作。我根据我的要求对其进行了调整,并且不得不放弃对反向的圆形滚​​动支持,但给定的解决方案足以回答我的查询。谢谢!

标签: androidkotlinandroid-recyclerview

解决方案


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

这应该照顾自动更改页面。您不必使用scrollListenerin Adapter。试试看。


推荐阅读