After a recent update of the Android support libraries, a new component called RecyclerView was introduced to replace ListView and brought with it many unique opportunities. It is intended for creating big complex lists and allows the use of different LayoutManagers. In particular, with RecyclerView you can build a custom LayoutManager implementation.
In this article, we are going to tell you how to implement a custom LayoutManager. Creating custom layouts requires a fair amount of code that you have to write yourself. In order to simplify the task, we’d like to share with you the key findings.
Building your Own LayoutManager
A basic LayoutManager includes:
- LinearLayoutManger — for traditional lists
- GridLayoutManager — for table view
- StaggeredGridLayoutManager — for presenting elements in Pinterest
Even this choice doesn’t help to solve some tasks for UI design of mobile apps.
The main principles of implementing layouts
1. Draw only what is displayed on the screen.
2. Use a fast algorithm to calculate the element's position instead of running through the whole list from RecyclerView.Adapter
3. Do not inflate views that are on the screen already. Reuse them again.
4. Do not call requestLayout on every occasion. Instead move the views; for example, when you need to make animations.
When you have learnt the rules, begin studying the methods.
Methods for implementing LayoutManagers:
- onLayoutChildren — the fundamental method for building elements that will be displayed on the screen
- onMeasure — a very important method; if the sizes of child views depend on this parameter, you need to set it to zero and calculate the size as well as all the child views with required sizes
- scrollToPosition(int) — allows scrolling the entire layout till a specific position in Adapter
- smoothScrollToPosition(recyclerView, state, position) — animated scroll that requires pointing out the direction of the list scrolling and the type of animation speed
- canScrollVertically/canScrollHorizontal — a great way to block scroll abilities in particular directions
- scrollVerticallyBy/scrollHorizontalBy — help to change the state of the LayoutManager. Using this method, you need to scroll all the elements and give back a number equal to the number of scrolls made. For example, you can give back zero. It implies almost the ban of scrolling for the methods above.
- onSaveInstanceState/onRestoreInstanceState — keep the state of the LayoutManager that can be useful for turnings, for instance.
All the methods mentioned are responsible for layout features. For example, our ExpandLayoutManager allows showing additional information for every element in the list. It uses various scrollBy, scrollHorizontallyBy for the scrolling:
Our Carousel LayoutManager applies again ScrollVerticallyBy and ScrollHorizontallyBy in order to scroll the list of app elements. It can work in the cycle by scrolling the list infinitely as well as up to a certain point. Besides, it supports scrollToPosition and smoothScrollToPosition for the ability to go to the required code element immediately:
Different architectural features such as CarouselZoomPostLayoutListener gives the effect of a merry-go-round and zoom to the LayoutManager. In particular, the listener reduces and shifts every element on the screen a little depending on its location using a maths algorithm.
When you determine methods and key principles of layout implementation, you can move to the architecture of LayoutManagers.
Approaching the Architecture of LayoutManagers
1. Organize the LayoutManager state to know at any moment what shift it has, where the first and last elements are situated, what size they have and other important features of the layout.
For example, mScrollOffset, mDecoratredChildHeight, mExpandItemPosition,
mExecutingAnimationData are used for ExpandLayoutManager. Using this data, we can always follow what state our LayoutManager has. It is necessary to rebuild it for onLayoutChildren, scrollVerticallyBy/scrollHorizontalBy, and animation support.
2. Delete all the unnecessary views before you fill out the required. It doesn’t mean that you need to remove all the views. It means you should delete only the views that became invisible.
3. Do not create new views if they are already in the LayoutManager. Go through all the views and find the required one. If you didn’t find this view, then it’s better to create a new view using getViewForPosition, and then bind it again to the needed position in the layout via bindViewToPosition.
There is a feature concerning changes of content in Adapter. You have to catch situations when the content changes. When the element is found in the list of child views, you need to call bindViewToPosition again. Otherwise, you can lose the changes in Adapter.
4. Another method state.didStructureChange can be called when you’ve shifted, removed or added elements from the middle of the list. It shows the current changes in the layout.
In general, layout implementation refers to attaching, measuring, and laying out all the child views in a specific order in real-time. When a user scrolls the app screen with the list of elements, it depends on the LayoutManager to define when new child views can be added, and old views can be removed or hidden. For this reason, if your layout has a reliable architecture, it means you can guarantee the user simple and effective interaction with the app and its custom elements.
You can easily solve all the UI design tasks for Android apps using custom LayoutManagers. In particular, you can add various animations both for the views and for their content. If you have any questions about the described examples or you’d like to share your personal experience, leave a comment below.