使用 PagerSnapHelper 在 RecyclerView 中添加 space

Add space in RecyclerView with PagerSnapHelper

我正在寻找一种使用 RecyclerView and the PagerSnapHelper 在我的项目之间添加 space 的方法。

为了证明我在这里的意思是一个 gif:

如您所见,两张图片之间有一点黑色 space。但是这些 space 仅在滚动 上可用 。如果 scrollState is in SCROLL_STATE_IDLE 图像居中对齐并且 "full width"。

这就是我想要实现的。

我的想法是添加一个 new "placeholder" viewType 到我的 RecyclerView.Adapter 并将 PagerSnapHelper 自定义为 "jump over" 占位符viewType(每秒跳过View也是可能的)。 但不知何故,这是行不通的。我无法更改 PagerSnaperHelper(或带有 PagerSnapHelper 中大量重复代码的 SnapHelper)以按我想要的方式工作。更进一步,在实施它时感觉有点不对劲。感觉就像 "this is not the way it should be".

那么 - 有什么建议或想法可以解决这个问题吗? 我完全愿意接受所有建议。

免责声明:
我知道我可以为此使用旧的 ViewPager。但出于特殊原因,我要求实现 RecyclerView 。所以请不要在这里使用ViewPager,谢谢。

你可以试试这个: 创建两个 viewType,第一个是你的图像,第二个是黑色缩进。然后在SCROLL_STATE_IDLESCROLL_STATE_SETTLING调用smoothScrollToPosition(position)到想要的位置。

val pagerSnapHelper = PagerSnapHelper()
val spacePagerSnapHelper = SpacePagerSnapHelper(pagerSnapHelper)
pagerSnapHelper.attachToRecyclerView(recyclerView)
spacePagerSnapHelper.attachToRecyclerView(recyclerView)

这是 SpacePagerSnapHelper:

/**
 * This is an [RecyclerView.OnScrollListener] which will scrolls
 * the [RecyclerView] to the next closes position if the
 * position of the snapped View (calculated by the given [snapHelper] in [SnapHelper.findSnapView])
 * is odd. Because each odd position is a space/placeholder where we want to jump over.
 *
 * See also [this SO question](
 */
class SpacePagerSnapHelper(
        private val snapHelper: SnapHelper
) : RecyclerView.OnScrollListener() {

    private enum class ScrollDirection {
        UNKNOWN, LEFT, RIGHT
    }

    private var scrollDirection: ScrollDirection = UNKNOWN

    fun attachToRecyclerView(recyclerView: RecyclerView) {
        recyclerView.addOnScrollListener(this)
    }

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        scrollDirection = if (dx > 0) RIGHT else LEFT
    }

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        when (newState) {
            RecyclerView.SCROLL_STATE_IDLE -> onPageChanged(recyclerView)
            RecyclerView.SCROLL_STATE_SETTLING -> onPageChanged(recyclerView)
        }
    }

    private fun onPageChanged(recyclerView: RecyclerView) {
        val layoutManager = recyclerView.layoutManager
        val viewToSnap = snapHelper.findSnapView(layoutManager)
        viewToSnap?.let {
            val position = layoutManager.getPosition(it)
            // Only "jump over" each second item because it is a space/placeholder
            // see also MediaContentAdapter.
            if (position % 2 != 0) {
                when (scrollDirection) {
                    LEFT -> {
                        recyclerView.smoothScrollToPosition(position - 1)
                    }
                    RIGHT -> {
                        recyclerView.smoothScrollToPosition(position + 1)
                    }
                    UNKNOWN -> {
                        Timber.i("Unknown scrollDirection... Don't know where to go")
                    }
                }
            }
        }
    }
}

UgAr0FF 的解决方案通常有效,但它也会导致不需要的滚动行为:

how it shouldn't look

如您所见,PagerSnapHelper 首先尝试关注分隔线,然后决定移至下一项。我修复了滚动行为,使其看起来像这样:

how it should look

代码如下:

import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_SETTLING


class PagerSnapAndSkipDividerHelper : PagerSnapHelper() {

    private var dx = 0

    override fun attachToRecyclerView(recyclerView: RecyclerView?) {
        super.attachToRecyclerView(recyclerView)
        recyclerView?.addOnScrollListener(onScrollListener)
    }

    override fun findTargetSnapPosition(
        layoutManager: RecyclerView.LayoutManager?,
        velocityX: Int,
        velocityY: Int
    ): Int = getNextNonDividerPosition(
        super.findTargetSnapPosition(layoutManager, velocityX, velocityY)
    )

    private fun getNextNonDividerPosition(position: Int) =
        if (position % 2 == 0) position else if (dx > 0) position + 1 else position - 1

    private val onScrollListener = object : RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            this@PagerSnapAndSkipDividerHelper.dx = dx
        }

        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            if (listOf(SCROLL_STATE_IDLE, SCROLL_STATE_SETTLING).contains(newState)) {
                val viewToSnapTo = findSnapView(recyclerView.layoutManager) ?: return
                val position = recyclerView.layoutManager?.getPosition(viewToSnapTo) ?: return
                val nonDividerPosition = getNextNonDividerPosition(position)
                if (position != nonDividerPosition) {
                    recyclerView.smoothScrollToPosition(nonDividerPosition)
                }
            }
        }
    }
}

这样设置:

PagerSnapAndSkipDividerHelper().attachToRecyclerView(recyclerView)