Fullscreen sticky item in RecyclerView

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 new ad position to the front page of the app.

Expected Result

https://github.com/damianpetla/FullscreenExample

Constraints

  • make ad content Y position fixed
  • make ad label sticky
  • make the ad clickable
  • (optional) add elevation to ad label when it obscure 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, ad item. Each type has own view holder. I will focus on 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 init block we are registering scroll listener which is triggered whenever our view item is moving along 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 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

Label’s Y position is updated with Math.max. That way label stick when an item goes out of the top edge but move 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 have tried to place 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 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!