Creating complex feed based on RecyclerView with Groupie

 The main way to present content in android mobile applications is RecyclerView. RecyclerView makes it easy to efficiently display large sets of data. Wide variety of applications are use RecyclerView: social network, app for reading books, or event Google Play app also uses RecyclerView with different items. In this article I will explain how to build complex feed with nested RecyclerView in 20 minutes using Groupie library.

All popular android apps are use RecyclerView for presenting complex content

Groupie is an android library which makes making complex RecyclerViews in your android app super easy. At the end of this article I will show you how to create such feed as shown below:

Complex feed with nested RecyclerView

At this example we consider 2 RecyclerView. The main feed is RecyclerView and each item in this list has infinite items, so we will use nested RecyclerView for each row in this list, as shown below:

Scheme of RecyclerView with nested RecyclerView in each row

So, let’s do it using Groupie.

Add Groupie to your project

First of all, let’s add Groupie to your android project.

  1. Add viewBinding to build.gradle into android section:
android {
...
viewBinding {
enabled true
}
...
}

2. Add Groupie library into build.gradle like shown below:


    
      implementation 'com.github.lisawray.groupie:groupie:2.10.0'
implementation "com.github.lisawray.groupie:groupie-viewbinding:2.10.0"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'androidx.cardview:cardview:1.0.0'

Press Sync Now and go to the next step.

Create items for each cell in row

In order to present content we need to create 3 types of cell

  1. Cell-container for nested list. I will show it using red color.
  2. Inner cell for movie presentation. This presented by blue color.
  3. Square cell in order to present games cover. This presented by green color.
In order to show this content we need 3 types of items.

Creating a container cell with a nested RecyclerView

First, let’s create a shared cell with a nested RecyclerView to display smaller cells:

Container cell wii show Title, Description and list of covers

The layout of such a cell will consist of a CardView with a LinearLayout to display the title, description, and a RecyclerView to display the inner cells.








    

        

        

        
    



Now let’s create the binding logic to display the cell data.


class MainCardContainer(
    private val title: String? = "",
    private val description: String? = "",
    private val onClick: (url: String) -> Unit,
    private val items: List>
) : BindableItem() {

    override fun getLayout() = R.layout.item_card

    override fun bind(binding: ItemCardBinding, position: Int) {
        binding.titleTextView.text = title
        binding.descriptionTextView.text = description
        binding.itemsContainer.adapter = GroupieAdapter().apply { addAll(items) }
    }

    override fun initializeViewBinding(view: View): ItemCardBinding {
        return ItemCardBinding.bind(view)
    }
}


Each cell, when using Groupie, must inherit from the abstract Item class. To do this, you need to override only 2 getLayout() and bind() methods. That is, to create a cell, you need to specify the layout that will be used to display the UI and add the data generation logic for this cell, and that’s it! There is no need to write adapters of the same type for different cells or to combine many different types of cells in one adapter, violating the principles of SOLID. With Groupie, for each cell, you need to create your own class, and describe the UI and logic in it!

In this example, we will use one base cell, which shows a name, description and a list of other Items. The most interesting thing here in this line:

That is, for a RecyclerView that is inside this cell, you need to add a common GroupAdapter and fill it with cells that are descendants of Item.

val adapter = GroupieAdapter().apply { addAll(movies) }

The general container is ready, now it remains to lay out private cells for each content type. There will be 2 of them:

  1. Item for movie presentation.
  2. Square item in order to present games cover.

Item for movie.

The movie cell must also inherit from class Item and must implement 3 methods:






 class MovieItem(private val content: MovieContent) : BindableItem() {

    override fun getLayout() = R.layout.item_with_text

    override fun bind(binding: ItemWithTextBinding, position: Int) {
        binding.description.text = content.title
        Picasso.get()
            .load(content.url)
            .into(binding.imagePreview)
    }

    override fun initializeViewBinding(view: View): ItemWithTextBinding {
        return ItemWithTextBinding.bind(view)
    }
}

The layout is quite simple and the code can be viewed in the project on GitHub. Square item for game cover. This cell is also quite simple, so take a look at the project code.

 class SquareItem(private val content: Content) : BindableItem() {

    override fun getLayout() = R.layout.item_square

    override fun bind(viewHolder: ItemSquareBinding, position: Int) {
        Picasso.get()
            .load(content.url)
            .into(viewHolder.preview)
    }

    override fun initializeViewBinding(view: View): ItemSquareBinding {
        return ItemSquareBinding.bind(view)
    }
}



Connecting all cells together.

To create a feed, we need to create cells with content and pass them to the RecyclerView adapter. To create cells, 2 methods getPopularMovies() and getPopularGames() were created that return cells of the Item type.




private fun getPopularMovies(): BindableItem {
        return MainCardContainer(
            "Movies", "Trending Now", ::onItemClick,
            listOf(
                MovieItem(
                    MovieContent(
                        "Joker",
                        "https://upload.wikimedia.org/wikipedia/en/e/e1/Joker_%282019_film%29_poster.jpg"
                    )
                ),
                MovieItem(
                    MovieContent(
                        "Bloodshot",
                        "https://upload.wikimedia.org/wikipedia/en/4/45/Bloodshot_-_official_film_poster.jpeg"
                    )
                ),
                MovieItem(
                    MovieContent(
                        "The Gentlemen",
                        "https://upload.wikimedia.org/wikipedia/en/0/06/The_Gentlemen_poster.jpg"
                    )
                ),
                MovieItem(
                    MovieContent(
                        "Ford v Ferrari",
                        "https://upload.wikimedia.org/wikipedia/en/a/a4/Ford_v._Ferrari_%282019_film_poster%29.png"
                    )
                ),
                MovieItem(
                    MovieContent(
                        "Fight Club",
                        "https://upload.wikimedia.org/wikipedia/ru/8/8a/Fight_club.jpg"
                    )
                )
            )
        )
    }



The last lines just uses a GroupieAdapter to which we can pass any cells that are descendants of BindableItem.

val adapter = GroupieAdapter().apply { addAll(movies) }
binding.itemsContainer.layoutManager = LinearLayoutManager(this)
binding.itemsContainer.adapter = adapter

So, we have created a complex recycler view with just some lines of code and not to mention without making adapters, viewholders, etc. In just 20 minutes, we created a complex list for displaying various types of content with a nested horizontal list! Compare this approach with the traditional one and draw your own conclusions! Absolutely, this approach will save you time on developing such UI components and get rid of a bunch of useless code.

Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.