When I started coding, one of the first tasks that I supposed to do was to populate some data into a list. I remember how I struggled with this "simple" task.
Since almost every android App contains a list, I will share with you what I have learned.
Right, let's get started.
Normally to display a list in android we use the RecyclerView, which, well, recycles the view used to display each element without creating a new one when an item scrolls off the screen. Then the RecyclerView requests those views created by the view holder object and binds it to its data.
Adapter
The Adapter is a design pattern and used to convert one interface to work with another, e.g, data -> Adapter -> RecyclerView. That shows us that the data was adapted in order to be possible to display them in the RecyclerView.
To accomplish this task, the Adapter interface needs to know:
- How many items the list has
- How to draw an item
- How to create a new view
We can create a ListAdapter doing so:
class MoviesAdapter(private val click: () -> Unit) :
ListAdapter<MovieDto, MoviesAdapter.MoviesViewHolder>(MoviesDiff())
In the snippet above we can see that the adapter class
extends the ListAdapter
class, we have also to provide the type of your list (MovieDto
), a ViewHolder (MoviesViewHolder
), and a (MoviesDiff()
).
MoviesDiff()
extends DiffUtil.ItemCallback, it's just a helper class for the Adapter that calculates the changes in the list. The algorithm used by Google to do that is the Myers diff
algorithm.
Then it will be needed to implement 2 methods, onCreateViewHolder
, which is responsible to create a new view holder object whenever the RecyclerView needs it, and onBindViewHolder
, which is responsible to bind the appropriate data and uses it to fill the view holder's layout.
Let's take a look.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoviesViewHolder {
return MoviesViewHolder.from(parent)
}
override fun onBindViewHolder(holder: MoviesViewHolder, position: Int) {
holder.bind(getItem(position), click)
}
onCreateViewHolder
takes 2 arguments, a ViewGroup into which the new view will be added before it gets displayed to the screen, and a viewType parameter is used when there are multiple different views in the same RecyclerView, i.g. a header.
onBindViewHolder
takes also 2 arguments, a view holder object, and an integer for the position. The view holder is the class responsible for everything related to actually managing views, and the position tells the view holders to show the item at the position.
The adapter needs a view holder object too, you can think of them as objects that hold views, each individual element in the list is defined by the view holder. The need for the view holder is justified because the RecyclerView will never interact directly with views.
class MoviesViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(data: MovieDto, click: () -> Unit) {
with(itemView) {
ctnBestMovies.setOnClickListener { click.invoke() }
tvBestMovieName.text = data.name
}
}
companion object {
fun from(parent: ViewGroup): MoviesViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.item_movies, parent, false)
return BestMoviesViewHolder(view)
}
}
}
Our custom MoviesViewHolder
extends the RecyclerVeiw.ViewHolder and encapsulates the logic to bind the views, the creation from view holder itself, and handles the click event.
This gives us a clean way to create new view holders and encapsulate the work related to details of the inflation and what layout the view holder class. It's an excellent example of separation of concerns, which results in a better way to change either part whiteout to have to change all the code.
To pass a list of objects to the ListAdapter, in your Activity or Fragment after setting up the RecyclerView, just call adapter.submitList(yourList)
and you are done!
private fun displayMovies(movies: List<MovieDto>) {
moviesAdapter.submitList(movies)
}
Thank you for reading so far.
I appreciate some feedback :)
See you in the next one.
Top comments (0)