Making the adapter code cleaner with MergeAdapter

Tired of the overloaded and complex adapters in your project like the picture below? Every time you add a new cell type, do you want to rewrite the adapter for the RecyclerView to make the code easier to read? There are many approaches, most often it is recommended to use the delegate adapter approach or, for example, a library for dynamically creating lists with different types of views like a groupie, which you can familiarize yourself with in this article . But today we will talk about a new class that will help encapsulate the logic of your adapter for different cells, thereby complying with the principles of SOLID.



image



MergeAdapter is a new class introduced in recyclerview: 1.2.0-alpha02 that allows you to combine multiple display adapters into a single RecyclerView. This will allow you to encapsulate the logic for each cell in your adapter, and will allow you to reuse it in the future.



Problem



Let's start with an example. Suppose we have a task to display a feed with two types of data - a text with a description and a picture. The code in the method onCreateViewHolder in the most common case will look like this:



override fun onCreateViewHolder(
    parent: ViewGroup, viewType: Int
): RecyclerView.ViewHolder? {
    val holder: RecyclerView.ViewHolder
    val inflater = LayoutInflater.from(parent.context)
    when (viewType) {
        TEXT_VIEW_TYPE -> {
            holder = TextViewHolder(
                inflater.inflate(R.layout.text_item, parent, false)
            )
        }
        IMAGE_VIEW_TYPE -> {
            holder = ImageViewHolder(
                inflater.inflate(R.layout.image_item, parent, false),
                imageClickListener
            )
        }
        else -> {
            throw IllegalArgumentException(
                "Can't create view holder from view type $viewType"
            )
        }
    }
    return holder
}


Why is that bad? The disadvantage of this implementation is in violation of the principles of DRY and SOLID (single responsibility and open closed). To make sure of this, it is enough to add two requirements: enter a new data type (checkbox) and one more tape, where there will be only checkboxes and pictures.



We are faced with a choice - to use the same adapter for the second tape or create a new one? Regardless of the solution we choose, we will have to change the code (about the same thing, but in different places). It will be necessary to add a new VIEW_TYPE, ViewHolder new and edit methods: getItemViewType(), onCreateViewHolder() onBindViewHolder().



If we decide to keep one adapter, then the changes will end there. But if in the future new data types with new logic are added only to the second feed, the first one will have extra functionality, and it will also need to be tested, although it has not changed.



If we decide to create a new adapter, there will be a lot of duplicate code.



Decision



The new MergeAdapter class allows you to combine different adapters for different kinds of cells. For example, a very common use case is to display a spinner while loading data in the feed, and if, suddenly, a loading error occurs, display a cell with an error at the end of the feed.



image



The solution to this problem can be the use of MergeAdapter Suppose we have 3 adapters:




val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …val mergeAdapter = MergeAdapter(firstAdapter, secondAdapter, thirdAdapter)
recyclerView.adapter = mergeAdapter


The RecyclerView will display the items of each adapter sequentially, in the same order as passed to the constructor. Different adapters allow you to separate logic for different cells in the list. For example, if you need to add a title to the list, you do not need to implement this logic in the adapter, which is responsible for displaying the main content in the list, you can separate the adapters for different types of cells. This approach helps to encapsulate logic and reuse it in the future for different screens.



image



Display the download in the header or at the bottom of the list.



To display the download status at the top or bottom of the list, you need to add adapters, respectively:



val mergeAdapter = MergeAdapter(headerAdapter, listAdapter, footerAdapter)
recyclerView.adapter = mergeAdapter


The top cell and the bottom one use the same layout, ViewHolder and UI logic (show loading status and hide). In general, it would be enough to use 2 instances of the same adapter for the top and bottom of the list. An example can be found here or here .



In short, in such a simple way you can improve the code of your project if you use a complex adapter with different types of cells.



Did you like the article? Do not forget to join us on Telegram , and useful materials for Android developers and modern tutorials are published on the AndroidSchool.ru platform .



All Articles