Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I'm searching for a way to add a space between my items using a
RecyclerView
and the
PagerSnapHelper
.
To demonstrate what I mean here is a gif:
As you can see above between the two images there is a little black space. But these space is only available
on scroll
. If the
scrollState
is in
SCROLL_STATE_IDLE
the image get centered snapped and are "full width".
That is what I want to achieve.
My idea was to add a
new "placeholder"
viewType
to my
RecyclerView.Adapter
and customize the
PagerSnapHelper
to "jump over" the
placeholder
viewType
(to jump over each second
View
would be also possible).
But somehow this isn't working. I can't change the
PagerSnaperHelper
(or the
SnapHelper
with a lot of duplicated code from the
PagerSnapHelper
) to work like I want. Even further while implementing it felt somehow wrong. It felt like "this is not the way it should be".
So - any recommendations or ideas how I can solve this?
I'm totally open for all suggestions.
Disclamer:
I know I could use the old
ViewPager
for this. But for special reasons I ask for an implementation for a
RecyclerView
😉. So please don't suggest to use the
ViewPager
here, thanks 😃.
You can try this:
Create two
viewType
, the first is your image, the second is a black indent. Then at
SCROLL_STATE_IDLE
and
SCROLL_STATE_SETTLING
call
smoothScrollToPosition(position)
to the desired position.
val pagerSnapHelper = PagerSnapHelper()
val spacePagerSnapHelper = SpacePagerSnapHelper(pagerSnapHelper)
pagerSnapHelper.attachToRecyclerView(recyclerView)
spacePagerSnapHelper.attachToRecyclerView(recyclerView)
And this is the 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://stackoverflow.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's solution generally works, but it also leads to unwanted scrolling behavior:
how it shouldn't look
As you can see, the PagerSnapHelper first tries to focus on the divider and then decides to move to the next item. I fixed the scrolling behavior so that it looks like this:
how it should look
Here is the code:
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)
Set it like this:
PagerSnapAndSkipDividerHelper().attachToRecyclerView(recyclerView)
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.