I'm using a horizontal RecyclerView
with PagerSnapHelper
to make it look like a ViewPager
. It's displaying CardViews
.
My question is, how can I make it show a small peek of the edge of the next and previous card? The user needs to see a little of those cards so they understand intuitively that they need to swipe horizontally so they can view other cards.
I'll also note that the current card always needs to be centered, even for the first one, which would not have a previous card peeking on the left. Also, the design requirements are out of my control; I need to peek, I can't use dot indicators or anything else.
I could use a LinearSnapHelper
and make the width of the cards smaller, but then 1) the first item will be left-aligned instead of centered, since there's no card peeking on the left side and 2) how much of each card displays would vary based on the width of the phone.
This seems like it should be a common and simple task, so I hope I'm missing something obvious to make it happen.
After some trial and error, I found a solution. Fortunately, it wasn't as complicated as I thought, although not as clean as I hoped.
So start with a RecyclerView and its Adapter, use a LinearLayoutManager with a Horizontal orientation, add in a PagerSnapHelper and so on... then to fix the specific issue in this question I made some adjustments to the adapter:
private var orientation: Int? = null
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
orientation = (recyclerView.layoutManager as LinearLayoutManager).orientation
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// Kludge to adjust margins for horizontal, ViewPager style RecyclerView
if (orientation != LinearLayout.VERTICAL) {
holder.itemView.layoutParams = (holder.itemView.layoutParams as RecyclerView.LayoutParams).apply {
val displayMetrics = DisplayMetrics()
baseActivity.windowManager.defaultDisplay.getMetrics(displayMetrics)
// To show the edge of the next/previous card on the screen, we'll adjust the width of our MATCH_PARENT card to make
// it just slightly smaller than the screen. That way, no matter the size of the screen, the card will fill most of
// it and show a hint of the next cards.
val widthSubtraction = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40f, displayMetrics).toInt()
width = displayMetrics.widthPixels - widthSubtraction
// We always want the spot card centered. But the RecyclerView will left-align the first card and right-align the
// last card, since there's no card peeking on that size. We'll adjust the margins in those two places to pad it out
// so those cards appear centered.
// Theoretically we SHOULD be able to just use half of the amount we shrank the card by, but for some reason that's
// not quite right, so I'm adding a fudge factor developed via trial and error to make it look better.
val fudgeFactor = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6f, displayMetrics).toInt()
val endAdjustment = (widthSubtraction / 2) - fudgeFactor
marginStart = if (position == 0) endAdjustment else 0
marginEnd = if (position == (itemCount - 1)) endAdjustment else 0
}
}
}
Of course, you'll want to change 40f
and 6f
to the appropriate values for your use case.
In case you're wondering, I have the orientation check because in my case, the same adapter is used for two different RecyclerViews. One is a simple vertical list. The other is horizontal and functions like a ViewPager, which greatly increased the complexity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With