Step #5: Completing the Adapter and ViewHolder

Now, we can start filling in the implementations of those stub methods in our RosterAdapter, plus get our RosterRowHolder working.

The job of onCreateViewHolder() is to create instances of a ViewHolder, including working with the ViewHolder to set up the widgets. Since our widgets are defined in a layout resource, we will need a LayoutInflater to accomplish this. The best way to get a LayoutInflater is to call getLayoutInflater() on an activity or fragment… but RosterAdapter has none of these.

So, add a constructor parameter to RosterAdapter to take in a LayoutInflater:

class RosterAdapter(private val inflater: LayoutInflater) :
  ListAdapter<ToDoModel, RosterRowHolder>(DiffCallback) {

Then, modify onCreateViewHolder() in RosterAdapter to be:

  override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
  ) = RosterRowHolder(TodoRowBinding.inflate(inflater, parent, false))

Here, we are using inflate() on the generated TodoRowBinding class to not only inflate the todo_row layout, but also to set up the binding object we can use to populate that row’s widgets. The particular flavor of inflate() that we are calling says:

(some container classes, like RelativeLayout, really need to know their parent in order to work properly, so we use this standard recipe for calling inflate())

However, onCreateViewHolder() will have a compile error, as we are passing a constructor parameter to RosterRowHolder that does not exist. So, modify RosterRowHolder to look like this:

package com.commonsware.todo

import androidx.recyclerview.widget.RecyclerView
import com.commonsware.todo.databinding.TodoRowBinding

class RosterRowHolder(private val binding: TodoRowBinding) :
  RecyclerView.ViewHolder(binding.root) {
}

getRoot() on a binding object returns the root widget of the inflated layout, which in our case is the ConstraintLayout. We need to pass that to the ViewHolder constructor, so this change fixes that compile error that we had from when we originally set up this class.

Now our RosterAdapter knows to create RosterRowHolder objects as needed. However, somewhere, we need to get a ToDoModel object and fill in the text and is-completed state for the CheckBox.

With that in mind, modify onBindViewHolder() on RosterAdapter to look like this:

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

onBindViewHolder() is called when RecyclerView wants us to update a ViewHolder to reflect data from some item in the RecyclerView. We are given the position of that item, along with the RosterRowHolder that needs to be updated. Since RosterAdapter extends ListAdapter, we have a getItem() function that gives us our ToDoModel for a given position, and we pass that to a bind() function on RosterRowHolder.

This will have a compile error, as there is no bind() function on RosterRowHolder. The objective of bind() is to populate our widgets, and since we are using the data binding framework, that comes in the form of calling binding methods on the TodoRowBinding object.

So, add a bind() function to RosterRowHolder:

  fun bind(model: ToDoModel) {
    binding.apply {
      isCompleted.isChecked = model.isCompleted
      desc.text = model.description
    }
  }

Here, we use our binding property to access each of our widgets and update their states to match that of the associated ToDoModel.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.