Kotlin Android: Как реализовать поэлементную прокрутку RecyclerView — прокрутка к середине ближайшего элемента?
Имеется горизонтальный RecyclerView с CardView-элементами. При прокрутке пользователем список должен сам прокрутиться к ближайшему элементу при свайпе и остановиться так, чтобы элемент оказался по центру экрана и по бокам было видно 2 соседних элемента.
Идеальный пример того, что я хочу получить — ВК Клипы (уж извините, ничего другого не нашел ¯\_(ツ)_/¯): https://www.youtube.com/watch?v=FDAHxyUbt74
На данный момент получилось сделать что-то похожее, но если прокрутить быстро, то скролл не остановится на соседнем элементе, а прокрутит его.
Логика такая:
В onScrolled(), при условии, что сейчас выполняется прокрутка пользователем, получаем направление прокрутки scrollDirection (dx > 0 — вправо, dx < 0 — влево )
Затем, при остановке прокрутки срабатывает onScrollStateChanged() и если в данный момент список не прокручивается (newState == 1) и его нужно прокрутить (scrollDirection != 0), то получаем первый и последний видимые элементы и прокручиваем список к одному из них, в зависимости от нужного направления
val scrollListener = object : RecyclerView.OnScrollListener() {
var scrollDirection = 0
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState > 0 || scrollDirection == 0)
return
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val firstVisiblePosition = layoutManager.findFirstVisibleItemPosition()
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()
val firstCompletelyVisiblePosition = layoutManager.findFirstCompletelyVisibleItemPosition()
val lastCompletelyVisiblePosition = layoutManager.findLastCompletelyVisibleItemPosition()
if (lastVisiblePosition >= 0 && firstVisiblePosition >= 0){
var elementPositionToScroll = 0
if (scrollDirection > 0){
elementPositionToScroll = if (lastCompletelyVisiblePosition < 0)
lastVisiblePosition else lastCompletelyVisiblePosition
}
if (scrollDirection < 0){
elementPositionToScroll = if (firstCompletelyVisiblePosition < 0)
firstVisiblePosition else firstCompletelyVisiblePosition
}
val elementToScroll = layoutManager.findViewByPosition(elementPositionToScroll)
if (elementToScroll != null)
recyclerView.smoothScrollBy((elementToScroll.x - dpToPx(40)).toInt(), 0)
scrollDirection = 0
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_DRAGGING)
scrollDirection = dx
}
}
Может быть есть какая-то встроенная реализация для прокрутки свайпами?
Ответы (1 шт):
Чтобы получить от списка элементов желаемое поведение, нужно всего лишь заменить RecyclerView на ViewPager2.
Так как ViewPager2 наследуется от RecyclerView, в коде ничего не нужно изменять (по крайней мере, в моем случае).
Для отображения по краям соседних элементов использовал вот это решение с англоязычной версии сайта.