4.4 Implementation with Input Controls

Callback Interface

Often times, we’ll want to use the items in the RecyclerView as an entry point to another screen and the simplest way to do so is through an interface inside the adapter class.

For this example, we'll be building on top of the code shown in section 4.3.

1. We can define our own onClick() method in our interface and it can take in as many as parameters as we need.

In this example, our click listener will just take in the position of the item on the list. Just remember that if you plan to use the onClick() method to start a new fragment or activity that you may want to pass in enough information, like a list or a key, to be able to make the correct computation.

CustomAdapter.kt
class CustomAdapter(
    private val dataSet: Array<Book>,
    private val mAdapterOnClickHandler: AdapterOnClickHandler
  )
    : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

  interface AdapterOnClickHandler {
    // you can define the parameters to be what you need
    fun onClick(position: Int)
  }
}

2. Inside onBindViewHolder we can customize the onClickListeners of our various views to refer to the callback functions we just created! Below, we override the onClickListener of a button defined in our cell! We pass whatever is specified by the onClick method from our button.

CustomAdapter.kt
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
    viewHolder.button.setOnClickListener {
        mAdapterOnClickHandler.onClick(position)
    }
}

3. In our activity or fragment, wherever the adapter is actually created, we’ll be implementing the onClick() method to our custom listener. In this example, the adapter will start a new activity when the item is clicked on.

Don't forget to extend the interface as we did at the header of the MainActivity! Android Studio will automatically tell you to implement the given functions once you do!

MainActivity.kt
class MainActivity : AppCompatActivity(),
   CustomAdapter.AdapterOnClickHandler {
   
   override fun onCreate(Bundle savedInstanceState) {
    ...
    recyclerView.adapter = CustomAdapter(myDataset, this)
    ...
  }
  
  override fun onClick(position: Int) {
    // add code for action to happen on click of the button in your RecyclerView row
    var i = Intent(this, SomeOtherActivity::class.java)
    i.putExtra("position", position)
    startActivity(i)
  }
}

Higher-order functions

If your interface has a small amount of functions (i.e. one or two), the above can be simplified using higher-order functions!

Kotlin functions are first-class, which means they can be stored in variables and data structures, and can be passed as arguments to and returned from other higher-order functions. You can perform any operations on functions that are possible for other non-function values.

Instead of defining some interface, we can pass the function in directly when creating a new adapter:

class CustomAdapter(
    private val dataSet: Array<Book>,
    private val onItemClick: (Int) -> Unit
  )
    : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
    
    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
    viewHolder.button.setOnClickListener {
        onItemClick(position)
    }
}

When initializing our adapter in our activity, we can pass in a function with the corresponding type or define it directly:

// An example that defines the function directly
val adapter = CustomAdapter(myDataset) { position -> 
   // The click action you want to perform.
}

Last updated