Mobile Development and 3D Graphics - Part 1

Lists with RecyclerView

This week we will look at how we can create lists of items in Android, making use of RecyclerView.

RecyclerView

Components of a RecyclerView

A RecyclerView contains:

How to code a RecyclerView

Full example of adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

// The names and descriptions to be displayed are passed as parameters to
// our Adapter.
class MyAdapter(val titles: List<String>, 
                val descriptions: List<String>): 
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    // A ViewHolder is associated with a particular adapter, so it makes
    // sense to create the holder as an inner class of the adapter.
    // The ViewHolder takes the associated View as a parameter

    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        // Inflate the child elements of the View and store them as properties,
        // so we have easy access to them later

        val tvName = itemView.findViewById(R.id.moduleName) as TextView
        val tvDescription = itemView.findViewById(R.id.moduleDescription) as TextView
    }

    // onCreateViewHolder() - called when a ViewHolder is first created.

    override fun onCreateViewHolder(parent: ViewGroup, viewType:Int) : RecyclerView.ViewHolder {
        // Inflate the XML layout and create a ViewHolder using it 

        val layoutInflater = LayoutInflater.from(parent.context)
        val inflatedLayout = layoutInflater.inflate(R.layout.list_item_layout, parent, false)
        return MyViewHolder(inflatedLayout)
    }

    // onBindViewHolder() - called when a ViewHolder is bound to a certain item of data.
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, index: Int) {
        // Cast to our specific ViewHolder class

        val myViewHolder = holder as MyViewHolder
    
        // Fill the name and description within the ViewHolder to the 
        // current elements within our two lists of data

        myViewHolder.tvName.text = titles[index]
        myViewHolder.tvDescription.text = descriptions[index]
    }

    override fun getItemCount(): Int {
        return titles.size
    }
}

Explanation of example: onCreateViewHolder()

In onCreateViewHolder(), we inflate a layout for an individual list item within the list.

Explanation of example: onBindViewHolder()

In onBindViewHolder(), we bind the appropriate data to the current list item (e.g. the elements within our lists of data corresponding to the list item index)

Layout for a list item

Handling events

Exercise

  1. Turn the example above into a complete working application (at the moment, the RecyclerView part of the application has been coded, but not the main activity, so you need to implement an activity). Give the main activity an XML layout with a RecyclerView, as shown above. Fill the RecyclerView with the following titles and descriptions:
  2. More advanced: Modify your exercise from last year to search for all songs by a given artist by connecting to a web API, so that the search results appear in a RecyclerView. If you do not have it, please clone this repository containing a working solution:
    https://github.com/nwcourses/NetworkComm
    Each list item in the RecyclerView should contain: You will need to replace the TextView of your main activity layout with a RecyclerView, and you will need to pass two lists, one containing the titles, and one containing the descriptions, using the data obtained from the JSON. Note that if you update your adapter with new data (i.e. new song details when doing a new search) you need to call the adapter's notifyDataSetChanged() method to inform the adapter that the data has changed and it needs to be redrawn. For example:
    myRecyclerView.adapter.notifyDataSetChanged()
    You will also need to download the Node.js based MAD song server from last year, and run it. Download from here.
  3. Modify your answer to add a callback function when a list item is pressed. This callback can be passed in to to the adapter as a lambda function. The callback should simply display the details of the selected song as a Toast.