Добавьте пространство в RecyclerView с помощью PagerSnapHelper
Я ищу способ добавить пробел между моими элементами, используя RecyclerView
и PagerSnapHelper
,
Чтобы продемонстрировать, что я имею в виду, вот gif:
Как вы можете видеть выше между двумя изображениями, есть небольшое черное пространство. Но эти места доступны только на свитке. Если scrollState находится в SCROLL_STATE_IDLE
изображение будет отцентрировано и будет "полной шириной".
Это то, чего я хочу достичь.
Моя идея заключалась в том, чтобы добавить новый "заполнитель" viewType
к моему RecyclerView.Adapter
и настроить PagerSnapHelper
"перепрыгнуть" заполнитель viewType
(перепрыгивать каждую секунду View
было бы также возможно). Но почему-то это не работает. Я не могу изменить PagerSnaperHelper
(или SnapHelper
с большим количеством дублированного кода из PagerSnapHelper
) работать так, как я хочу. Еще дальше при реализации это чувствовало себя как-то не так. Мне казалось, что "это не так, как должно быть".
Итак - какие-либо рекомендации или идеи, как я могу решить это? Я полностью открыт для всех предложений.
Юридическая информация:
Я знаю, что мог бы использовать старый ViewPager
за это. Но по особым причинам я прошу о реализации для RecyclerView
, Поэтому, пожалуйста, не предлагайте использовать ViewPager
здесь, спасибо
2 ответа
Вы можете попробовать это: создать два viewType
Первый - это ваш образ, второй - черный отступ. Тогда в SCROLL_STATE_IDLE
а также SCROLL_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://stackru.com/q/51747104).
*/
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 в целом работает, но оно также приводит к нежелательному поведению прокрутки:
Как видите, 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)