Image from Johannes PlenioPixabay

About lists, snackbars, and why they don’t get along.

Roberto Orgiu
Jul 23 · 4 min read

Recently, I have been working on a new feature of the core New York Times reading app, and I encountered a very challenging issue: whenever the user clicked on an item of a list, a permanent would appear. Since the should have been anchored to the lower end of the , the last element of the list would always be at least partially covered, and inaccessible.

We can see the last item being always covered

We discussed several solutions with the rest of the team: we tried to use the , but with the not being a , this approached revealed to be quite complicated; we talked about adding an empty at the end of the list, or some spacing, but we were not sold.

So, during my hack-day, I decided to poke around another solution: the generic idea was to leverage a callback, so that I could be warned when the was visible. The offers two methods to override, that will be fired after the is shown, and after is is dismissed.

The code

The main idea is to leverage the showing event to raise the content — only when needed — and lower it back when the is not visible anymore. Since we want this to be as smooth as possible, we updated the spacing after the is shown, but we won’t be using the other event: this method will be fired after the is already gone, so we would see a glitch.

We will instead use the on the , so that we could reset the spacing before the animation is started, masking the glitch:

The logic that powers the margin move is quite simple, but it took me a few hours to grasp completely. The first thing we have to do is checking what is the last item in the adapter.

Next step would be the actual move of the : we create a new and clone it from the root , then we apply a margin based either on the height or 0 (when such bar is not shown anymore), and then we apply this set to the root.

At this point, if the selected item is the last one, we scroll the list, so that every item is visible:

This code is based on a couple of assumptions, specific to our case:

  • Our elements are taller than the , so the bar can only cover one of them — in this way we only have to care about the last item.
  • In our scenario, when we press the Action button on the , we are brought to another screen, so the exit animation will not be seen by the users.
  • We only want to scroll if the interested item is the last — since we add the margin at the bottom, the users will still be able to scroll and see everything.

We also need to create a new instance of the at every iteration, as otherwise our app would start complaining about it being reused:

W/RecyclerView: An instance of SmoothScroller was started more than once. Each instance ofSmoothScroller is intended to only be used once. You should create a new instance for each use.

The use of a , which is a small extension of the , was needed so that we could define a scrolling speed that was looking good with the rest of the screen, but it can be easily elaborated further:

The final result looks pretty OK, even if I’m not 100% happy with the exit behaviour of the list:

Conclusion

This code is really tailored upon the issue we were facing, and it could be customised to work based on the number of visible items quite easily.

If you want to check the code and play around it — and maybe find a better solution to this issue — you can find it on GitHub.

Google Developers Experts

Experts on various Google products talking tech.

Thanks to Fabio Collini

Roberto Orgiu

Written by

Android developer and GDE for Android

Google Developers Experts

Experts on various Google products talking tech.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade