首页 > 解决方案 > 为 RecyclerView 中一定数量的可见项目调用 OnScrollListener

问题描述

我在回收站视图底部使用圆形页面指示器作为ItemDecoration ,并使用GridLayoutManager并将方向设置为 Horizo ​​ntal。目前,当用户从左向右移动项目时,指示器会改变其位置,反之亦然。但是当用户滚动时,当屏幕上的总可见项目(此处为 6)发生偏移时,我需要更改页面指示器。因此,指标的总数会减少。

这张图片试图解释这个问题。 在此处输入图像描述

似乎RecyclerView.OnScrollListener将被放置来处理屏幕偏移。

bindingView.listPaymentType.addOnScrollListener(object : RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            Log.i("listPaymentType","onScrolled")
        }

        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            Log.i("listPaymentType","onScrollStateChanged")
            super.onScrollStateChanged(recyclerView, newState)
        }
    })

我为这里的所有东西编写的代码。

itemDecor = CirclePagerIndicatorDecoration()
    //bindingView.listPaymentType.addItemDecoration(itemDecor)
    viewModel.paymentList.observe(this, Observer {

        var layoutManager = GridLayoutManager(activity, 2,
                GridLayoutManager.HORIZONTAL, false)

        bindingView.listPaymentType.removeItemDecorations()

        when {
            it.size == 4 -> {
                layoutManager.spanCount = 1
                bindingView.listPaymentType.addItemDecoration(itemDecor)
            }
            it.size > 4 -> {
                layoutManager.spanCount = 2
                bindingView.listPaymentType.addItemDecoration(itemDecor)
            }
            else -> {
                layoutManager.spanCount = 1
            }
        }

        bindingView.listPaymentType.layoutManager = layoutManager
        bindingView.listPaymentType.adapter = adapter
        adapter.update(it as ArrayList<PaymentMethod>)
        if (it.isNotEmpty()) {
            adapter.selectedItem(it[0])
        }
    })

CirclePagerIndicator装饰如下:

class CirclePagerIndicatorDecoration : RecyclerView.ItemDecoration() {

private val colorActive = 0xFF545455.toInt()
private val colorInactive = 0x33000000

/**
 * Height of the space the indicator takes up at the bottom of the view.
 */
private val mIndicatorHeight = (DP * 12).toInt()

/**
 * Indicator stroke width.
 */
private val mIndicatorStrokeWidth = DP * 4

/**
 * Indicator width.
 */
private val mIndicatorItemLength = DP * 2

/**
 * Padding between indicators.
 */
private val mIndicatorItemPadding = DP * 8

/**
 * Some more natural animation interpolation
 */
private val mInterpolator = AccelerateDecelerateInterpolator()

private val mPaint = Paint()

init {
    mPaint.apply {
        strokeWidth = mIndicatorStrokeWidth
        style = Paint.Style.STROKE
        isAntiAlias = true
    }
}

override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
    super.onDrawOver(c, parent, state)

    val itemCount = parent.adapter?.itemCount?.div(COUNT_DIVIDER)?.toInt() ?: 0

    // center horizontally, calculate width and subtract half from center
    val totalLength = mIndicatorItemLength * itemCount
    val paddingBetweenItems = Math.max(0, itemCount - 1) * mIndicatorItemPadding
    val indicatorTotalWidth = totalLength + paddingBetweenItems
    val indicatorStartX = (parent.width - indicatorTotalWidth) / 2f

    // center vertically in the allotted space
    val indicatorPosY = parent.height - mIndicatorHeight / 2f

    drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount)

    // find active page (which should be highlighted)
    (parent.layoutManager as? LinearLayoutManager)?.let { layoutManager ->
        val activePosition = layoutManager.findFirstVisibleItemPosition()
        if (activePosition == RecyclerView.NO_POSITION) {
            return
        }

        // find offset of active page (if the user is scrolling)
        layoutManager.findViewByPosition(activePosition)?.let { activeChild ->
            val left = activeChild.left
            val width = activeChild.width
            // on swipe the active item will be positioned from [-width, 0]
            // interpolate offset for smooth animation
            val progress = mInterpolator.getInterpolation(left * -1 / width.toFloat())

            drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress)
        }
    }
}

private fun drawInactiveIndicators(c: Canvas, indicatorStartX: Float, indicatorPosY: Float, itemCount: Int) {
    mPaint.color = colorInactive

    // width of item indicator including padding
    val itemWidth = mIndicatorItemLength + mIndicatorItemPadding

    var start = indicatorStartX
    for (i in 0 until itemCount) {
        c.drawCircle(start, indicatorPosY, mIndicatorItemLength / 2f, mPaint)
        start += itemWidth
    }
}

private fun drawHighlights(
        c: Canvas, indicatorStartX: Float, indicatorPosY: Float,
        highlightPosition: Int, progress: Float
) {
    mPaint.color = colorActive

    // width of item indicator including padding
    val itemWidth = mIndicatorItemLength + mIndicatorItemPadding
    val radius = mIndicatorItemLength / SCALE
    if (progress == 0f) {
        // no swipe, draw a normal indicator
        val highlightStart = indicatorStartX + itemWidth * highlightPosition

        c.drawCircle(highlightStart, indicatorPosY, radius, mPaint)

    } else {
        val highlightStart = indicatorStartX + itemWidth * highlightPosition
        // calculate partial highlight
        val partialLength = mIndicatorItemLength * progress + (mIndicatorItemPadding * progress)

        c.drawCircle(highlightStart + partialLength, indicatorPosY, radius, mPaint)
    }
}

override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
    super.getItemOffsets(outRect, view, parent, state)
    outRect.bottom = mIndicatorHeight
}

companion object {
    private val DP = Resources.getSystem().displayMetrics.density
    private const val COUNT_DIVIDER: Float = 1.4f
    private const val SCALE: Float = 1.1f
}

}

标签: androidandroid-recyclerview

解决方案


推荐阅读