Note: this article was previously published on Damian’s Medium.com profile. The author agreed to republish the article on the Schibsted Developers Blog.
https://github.com/damianpetla/FullscreenExample
Constraints
- make the ad content’s Y position fixed
- make the ad label sticky
- make the ad clickable
- (optional) add elevation to the ad label when it obscures ad content
Jump to the code directly if you want to or check out this piece of code where all the magic happens:
class AdItemHolder(item: View) : BaseHolder(item) { private val adLabel = item.findViewById(R.id.adlabel) private val adContent = item.findViewById (R.id.adcontent) private val labelElevation = item.resources.getDimension(R.dimen.cardview_default_elevation) init { item.viewTreeObserver.addOnScrollChangedListener { val offset = -item.top.toFloat() adLabel.translationY = Math.max(offset, 0f) adLabel.translationZ = if (offset < 0) labelElevation else 0f adContent.translationY = offset } } }
Step by step
The sample I have created has a very simple layout with RecyclerView
and two types of items: regular item and ad item. Each type has its own view holder. I will focus on the ad item holder called AdItemHolder
. I will break down the code into steps and explain what is going on.
First, we need a reference to our label and content which we are going to manipulate:
private val adLabel = item.findViewById(R.id.adlabel) private val adContent = item.findViewById (R.id.adcontent)
Inside the init block we are registering a scroll listener which is triggered whenever our view item is moving along the Y axis:
item.viewTreeObserver.addOnScrollChangedListener { ... }
Next, I am reading the root item top position. It is the Y position of the item relative to the RecycleView:
val offset = -item.top.toFloat()
And then I am updating the label and content positions accordingly:
adLabel.translationY = Math.max(offset, 0f) adContent.translationY = offset
Ad content
Below image will explain best why I am using an inverted top of the item to set translationY
of the content view.
Ad label
The label’s Y position is updated with Math.max
. That way the label sticks when an item goes out of the top edge but moves along when an item is going down.
Label elevation
Modifying translationZ
of the label’s view is not something I needed at work but I found it useful working on this post. It feels a bit nicer with respect to Material Design.
adLabel.translationZ = if (offset < 0) labelElevation else 0f
Clickable content
It is worth mentioning that it wasn’t my first approach to the problem. Initially, I tried to place the ad content under RecyclerView
and then add a transparent item. However, it was creating some issues and one of them was the fact that RecyclerView
was consuming all the click events on our ad. Who needs an ad without being able to click on it, right? ¯\_(ツ)_/¯
Kudos to my colleague Maciej for proofing this approach on iOS.
Summary
This is not rocket science but sometimes the simplest solution requires significant effort to get there. I hope it will be useful to someone else and allow to save a little bit of time. Happy coding!