Fullscreen sticky item in RecyclerView

Damian Petla | Tilt
3 min readJun 7, 2019

--

It’s been a while since I wrote my last post. Recently I couldn’t find any good resources for solving my problem. So after I managed to handle it myself, I decided to share it with everyone else.

A project I am working on uses ads to sustain its existence. So from time to time, I need to work on ad-related implementations. This time it was about adding a new ad position to the front page of the app.

Expected Result

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:

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<View>(R.id.adlabel) 
private val adContent = item.findViewById<View>(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.

Layout structure

Don’t use your ad view as the root of the view item. You want your root item view to keep scrolling in the RecyclerView along with other items and clip your ad content. Otherwise, your ad would be visible under other items that have transparency, or between other items if there is a divider.

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!

--

--