使用PagerSnapHelper在RecyclerView中添加间距

3
我正在寻找一种方法,使用 RecyclerViewPagerSnapHelper 在我的元素之间添加间隔。
为了说明我的意思,这里有一个 gif: 如你所见,在两个图像之间有一个小黑色空间。但是,这些空间仅在滚动时可用。如果scrollState处于SCROLL_STATE_IDLE中,则图像会居中捕捉,并且“全宽”。
那就是我想要实现的。
我的想法是在我的RecyclerView.Adapter中添加一个新的“占位符”viewType,并自定义PagerSnapHelper以“跳过”占位符viewType(也可以跳过每第二个View)。 但是不知何故,这并没有起作用。我无法更改PagerSnaperHelper(或使用大量重复代码从PagerSnapHelper中复制SnapHelper)以按照我想要的方式工作。在实现时,甚至感觉有些不对劲。感觉像是“这不是应该的方式”。
那么,有什么建议或想法可以解决这个问题吗? 我完全开放所有建议。
免责声明: 我知道我可以使用旧的{{link1:ViewPager}}来实现这一点。但出于特殊原因,我要求在RecyclerView中实现。所以请不要建议在此处使用ViewPager,谢谢。

你尝试过什么了吗? - sushildlh
2个回答

3
你可以尝试这样做: 创建两个 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](https://dev59.com/F63la4cB1Zd3GeqPNn5q).
 */
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,这个方法非常好用❤️。目前只有“方向”是错误的(recyclerView.smoothScrollToPosition(position + 1)也可以是-1)。但我稍后会使用我的最终解决方案更新您的答案。但是感谢您,这很有效且非常流畅。 - StefMa

2

UgAr0FF的解决方案一般都能奏效,但它也会导致不必要的滚动行为:

错误的展示方式

正如您所看到的,PagerSnapHelper首先尝试聚焦于分隔符,然后才决定移动到下一个项目。我改进了滚动行为,使其看起来像这样:

正确的展示方式

以下是代码:

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接