Implementing ListView inside RecyclerView and observing LiveData
Tags: kotlin, android, mvvm, recyclerview
Published: 2020-09-06 14:13:52

Recently I wanted to show cards in my [application](https://github.com/Anureet19/ExpenseManager) which gets updated according to the month I have added in my list. Inside the card, I wanted to show the list of items that I have added for that month. Being a beginner in android, it took me a long time to figure this out so I decided to write a "how to" post on the same. I will be explaining the main files, complete project is available [here](https://github.com/Anureet19/NestedRecyclerViewExample).

Let's get started :information_desk_person:

I have created a simple application as an example

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/smyx0549zl1j0j8lsb8t.png)

In the above screen, there is an option to add name and a category to which it belongs. For selection of a single category, I have implemented a spinner inside this fragment. On the home page, a card will be generated according to the category added and it will show the name of the category and the respective list of items belonging to that category. Floating action button can be used to add more items to the list.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lldzqkr323zed2ud0xex.png)  

I have used MVVM architecture and single activity multiple fragments pattern to implement this app. There are two parts to this. One is on adding cards according to the category added to the database and the other one is on how to insert list inside an existing list.

#Adding card dynamically
So firstly add recyclerview to your fragment

```xml
<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/card_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:listitem="@layout/card_item" />
```
I have created a database to store items which uses data class Favourites.kt

Favourites.kt
```kotlin
import androidx.room.Entity
import androidx.room.PrimaryKey

enum class Categories{
    Movies, Sports, Fruits, Vegetables
}

@Entity(tableName = "favourites")
data class Favourites(@PrimaryKey(autoGenerate = true) val id: Long,
                      val name : String,
                      val category: Int)
```

FavouritesListDao.kt
```kotlin
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Query

@Dao
interface FavouritesListDao{
    @Query("SELECT f1.category, (SELECT f2.name FROM favourites as f2 WHERE f1.category = f2.category) FROM favourites as f1 GROUP BY category")
    fun getData(): LiveData<List<Category>>

}
```
The above sqlite query is responsible for displaying cards according to the category inserted. The **GROUP BY** clause will group the data with respect to category.
This query will fetch data from the database. Following data class is being used to store the results obtained by the above query.

```kotlin
import androidx.room.ColumnInfo
import androidx.room.Relation


data class Category(
    @ColumnInfo(name = "category")
    var category: Int,

    @Relation(parentColumn = "category", entityColumn = "category", entity = Favourites::class)
    val children : List<itemName>
)
```
The code below denotes that the result stored inside children list will be obtained by a **self-join** .

```kotlin
@Relation(parentColumn = "category", entityColumn = "category", entity = Favourites::class)
```
#Adding ListView inside RecyclerView

```sql
(SELECT f2.name FROM favourites as f2 WHERE f1.category = f2.category)
```

Results obtained from this query present inside the above sqlite query will be stored inside 'children' list. This will be added to the listView inside cardView. 

Here is the itemAdapter class: 

```kotlin
class ItemAdapter(val context: Context):
    ListAdapter<Category, ItemAdapter.ViewHolder>(
        DiffCallback()
    ){

    override fun onCreateViewHolder(parent: ViewGroup,
                                    viewType: Int): ViewHolder {
        val itemLayout = LayoutInflater.from(parent.context)
            .inflate(R.layout.card_item, parent, false)

        return ViewHolder(itemLayout)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    inner class ViewHolder (override val containerView: View) : RecyclerView.ViewHolder(containerView),
        LayoutContainer {

        fun bind(item: Category){
            with(item){
                when(item.category){
                    Categories.Movies.ordinal ->{
                        category_name.text = Categories.Movies.toString()
                    }
                    Categories.Sports.ordinal ->{
                        category_name.text = Categories.Sports.toString()
                    }
                    Categories.Fruits.ordinal ->{
                        category_name.text = Categories.Fruits.toString()
                    }
                    else ->  category_name.text = Categories.Vegetables.toString()
                }

                // Setting up list view
                val list: List<String> = item.children.map { it.name }
                val adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1,list)
                item_list.adapter = adapter

            }
        }
    }
}

class DiffCallback : DiffUtil.ItemCallback<Category>() {
    override fun areItemsTheSame(oldItem: Category, newItem: Category): Boolean {
        return oldItem.category == newItem.category
    }

    override fun areContentsTheSame(oldItem: Category, newItem: Category): Boolean {
        return oldItem == newItem
    }
}
```
This code segment inside the above code is used to set up listView inside card_item (cardView)

```kotlin
// Setting up list view
val list: List<String> = item.children.map { it.name }
val adapter = ArrayAdapter(context,android.R.layout.simple_list_item_1,list)
                item_list.adapter = adapter
```

To set-up the recyclerView inside home fragment, add this code
```kotlin
with(card_list){
     layoutManager = LinearLayoutManager(activity)
     adapter = ItemAdapter (requireContext())
}
```

#Observing Live Data

Create a repository class for communication between viewModel and DAO
```kotlin
import android.app.Application
import androidx.lifecycle.LiveData

class FavouritesListRepository(context:Application) {
    private val favouritesListDao: FavouritesListDao = FavouritesDatabase.getDatabase(context).favouritesListDao()

    fun getData(): LiveData<List<Category>> {
        return favouritesListDao.getData()
    }

}
```
To observe live data, create a viewModel class for home fragment.

```kotlin
class FavouriteListViewModel(application: Application): AndroidViewModel(application) {
    private val repo : FavouritesListRepository = FavouritesListRepository(application)

    val items: LiveData<List<Category>>
        get() = repo.getData()

}
```
Now observe the data inside items list in home fragment
```kotlin
viewModel.items.observe(viewLifecycleOwner, Observer{
            (card_list.adapter as ItemAdapter).submitList(it)
        })
```

To checkout how items are getting inserted in the database and the structure of the app, [here is the github link](https://github.com/Anureet19/NestedRecyclerViewExample) from where you can download this project

This is my first post. I hope this is helpful. 
![Alt Text](https://media.giphy.com/media/xT1R9zev4imeW6DV2U/giphy.gif)