How the Android team Recycled our Screens

Roman Iatcyna
Revolut Tech
Published in
5 min readSep 7, 2017

Hello world! This article is addressed to the Android community and discusses our newly adopted development approach here at Revolut.

Mockups of existing Revolut app screens

The question of a new approach was raised only recently, as our engineering team started to grow. Despite the plan to launch an array of new features including device insurance, travel insurance, cryptocurrencies, ETFs and more, they all share the same design considerations and follow a common pattern. At Revolut, everything we build is based on small independent modules (think of these as Lego blocks), which come together as one in the final product.

A thing of the past

Previously, we had implemented screens by laying out all the new elements inside RelativeLayout (or LinearLayout, ConstraintLayout, you name it), mapping every XML view to Fragment/Activity, before setting titles, colours and so on. Different screen states were controlled by each View visibility component, which meant that a screen XML file contained everything that could appear on that screen and also meant that it could take up to 500 strings.

The problem with this approach was that for each new screen the developers would have to copy and paste View, findViewBylds and so on from one XML to another — absolutely inefficient process. What’s more, the View was often not even a custom SomeView extends SomeLayout, but rather just the plain XML hierarchy. Of course you can wrap every part in CustomView or use XML includes, but that still leaves Java mapping to be dealt with. To make things even worse, the XML contains every View that could ever occur on the screen. All in all, it was an absolute torture to use such copy-paste-oriented programming. Luckily, our clever team managed to find a solution.

RecyclerView Delegates

You’ve probably heard of different approaches for creating generic adapters for RecyclerView. Libraries such as AdapterDelegates and LastAdapter give us the luxury to ‘forget’ about the dozens of purposely-built adapters and offer us a better way of applying the composite reuse principle. Essentially, this meant that every adapter can be composed from small blocks only responsible for displaying one concrete model — the solution to our problem.

We decided to build a common adapter that can display “independent blocks” for every screen. Basically, our adapter is a handmade version of the AdapterDelegates (thanks to Hannes Dorfmann for his truly brilliant idea) and “blocks” are list item models, each displayed by their corresponding delegates.

It’s not worth going into too much detail here, since the source code can be found on github. If you want a more detailed explanation, feel free to read Hannes’s article. After some consideration, we decided to go one step further and make almost every screen a RecyclerView.

Recycle everything

But what are the actual benefits of ‘recycling’? Since screens are quite small, rarely occupying more than 200% of the screen height, there isn’t much point making these screens RecyclerViews.

The delegate class represents just a small part of Adapter, being only responsible for displaying one particular data type. Adapter proxies all methods (onBind, onCreate etc.) to the right delegate and the only logic inside Adapter is how to add, keep and select the correct delegate.

Here’s a Delegate API proxying Adapter method.

Each Delegate is only responsible for its own item. Here are some examples:

Each delegate represents one Recycler’s view type. So this is essentially just a list of different type models. Every type name is shown to the left of a mockup.

How to use

Suppose we just want to reuse some of the existing delegates:

1. Create adapter instance inside Fragment/Activity and register needed delegates

2. Make a list of needed objects and set this list to adapter

3. ….And that’s it. The delegates will do the rest!

How to write

Sometimes delegate is not available, so you need to create one.

Bassically, single delegate looks like RecyclerView.Adapter for single type items. The RecyclerView.Adapter should only implement ViewHolder creation (onCreateViewHolder), ViewHolder declaration (nested class extending RecyclerView.ViewHolder) and data binding (onBindViewHolder method).

You may also notice R.layout.delegate_button and small lambda being passed to the superclass. Layout id represents viewType in our example, but we can actually use separate ids and a different XML for the same Delegate. Lambda is the rule determining if this delegate suits the given model and ase the base class is only responsible for invoking that lambda.

So to sum up, this approach has lots of pros:

1. Reusability. Say we have transactions list implemented with the delegates. Then we can add few transactions to any screen in couple of strings: add TransactionItemDelegate to adapter and then add TransactionItem models to the items list of that screen.

2. Another advantage is ease of testing. To test one screen, we simply need to test that list of models, its order and attributes for each screen state, because they are all simple data models.

3. RecyclerView ItemDecorators. You probably noticed that the same elements in some cases are surrounded by shadows and “gaps,” while in others they are arranged in a row on a white background. We do not need separate XMLs for every combination of shadows and gaps, as we can control these via decorators.

4. RecyclerView animations — ItemsAnimators allow us to animate items between different screen states.

This is only a small part of what can be achieved by using RecyclerView delegates. We have adapted this approach to make complex forms with text input (we have lots of them), we’ve figured out a few tricks for handling separators, shadow and gaps between items with ease, but more on this in our next articles in the Revolut Engineering Blog.

--

--