Implementing an Adapter to Populate the RecyclerView

Figure 9.7 is somewhat simplified. RecyclerView does not create ViewHolders itself. Instead, it asks an adapter. An adapter is a controller object that sits between the RecyclerView and the data set that the RecyclerView should display.

The adapter is responsible for:

  • creating the necessary ViewHolders when asked

  • binding ViewHolders to data from the model layer when asked

The recycler view is responsible for:

  • asking the adapter to create a new ViewHolder

  • asking the adapter to bind a ViewHolder to the item from the backing data at a given position

Time to create your adapter. Add a new inner class named CrimeAdapter to CrimeListFragment. Add a primary constructor that expects a list of crimes as input and stores the crime list passed in a property, as shown in Listing 9.11.

In your new CrimeAdapter, you are also going to override three functions: onCreateViewHolder(…), onBindViewHolder(…), and getItemCount(). To save you typing (and typos), Android Studio can generate these overrides for you. Once you have typed the initial line of the new code, put your cursor on CrimeAdapter and press Option-Return (Alt-Enter). Select Implement members from the pop-up. In the Implement members dialog, select all three function names and click OK. Then you only need to fill in the bodies as shown.

Listing 9.11  Creating CrimeAdapter (CrimeListFragment.kt)

class CrimeListFragment : Fragment() {
    ...
    private inner class CrimeHolder(view: View)
        : RecyclerView.ViewHolder(view) {
        ...
    }

    private inner class CrimeAdapter(var crimes: List<Crime>)
        : RecyclerView.Adapter<CrimeHolder>() {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
                : CrimeHolder {
            val view = layoutInflater.inflate(R.layout.list_item_crime, parent, false)
            return CrimeHolder(view)
        }

        override fun getItemCount() = crimes.size

        override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
            val crime = crimes[position]
            holder.apply {
                titleTextView.text = crime.title
                dateTextView.text = crime.date.toString()
            }
        }
    }
}

Adapter.onCreateViewHolder(…) is responsible for creating a view to display, wrapping the view in a view holder, and returning the result. In this case, you inflate list_item_view.xml and pass the resulting view to a new instance of CrimeHolder. (For now, you can ignore onCreateViewHolder(…)’s parameters. You only need these values if you are doing something fancy, like displaying different types of views within the same recycler view. See the section called Challenge: RecyclerView ViewTypes at the end of this chapter for more information.)

Adapter.onBindViewHolder(holder: CrimeHolder, position: Int) is responsible for populating a given holder with the crime from a given position. In this case, you get the crime from the crime list at the requested position. You then use the title and data from that crime to set the text in the corresponding text views.

When the recycler view needs to know how many items are in the data set backing it (such as when the recycler view first spins up), it will ask its adapter by calling Adapter.getItemCount(). Here, getItemCount() returns the number of items in the list of crimes to answer the recycler view’s request.

The RecyclerView itself does not know anything about the Crime object or the list of Crime objects to be displayed. Instead, the CrimeAdapter knows all of a Crime’s intimate and personal details. The adapter also knows about the list of crimes that backs the recycler view (Figure 9.9).

Figure 9.9  Adapter sits between recycler view and data set

Adapter sits between recycler view and data set

When the RecyclerView needs a view object to display, it will have a conversation with its adapter. Figure 9.10 shows an example of a conversation that a RecyclerView might initiate.

Figure 9.10  A scintillating RecyclerView-Adapter conversation

A scintillating RecyclerView-Adapter conversation

The RecyclerView calls the adapter’s onCreateViewHolder(ViewGroup, Int) function to create a new ViewHolder, along with its juicy payload: a View to display. The ViewHolder (and its itemView) that the adapter creates and hands back to the RecyclerView has not yet been populated with data from a specific item in the data set.

Next, the RecyclerView calls onBindViewHolder(ViewHolder, Int), passing a ViewHolder into this function along with the position. The adapter will look up the model data for that position and bind it to the ViewHolder’s View. To bind it, the adapter fills in the View to reflect the data in the model object.

After this process is complete, RecyclerView will place a list item on the screen.

Setting the RecyclerView’s adapter

Now that you have an Adapter, connect it to your RecyclerView. Implement a function called updateUI that sets up CrimeListFragment’s UI. For now, it will create a CrimeAdapter and set it on the RecyclerView.

Listing 9.12  Setting an Adapter (CrimeListFragment.kt)

class CrimeListFragment : Fragment() {

    private lateinit var crimeRecyclerView: RecyclerView
    private var adapter: CrimeAdapter? = null
    ...
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_crime_list, container, false)

        crimeRecyclerView =
                view.findViewById(R.id.crime_recycler_view) as RecyclerView
        crimeRecyclerView.layoutManager = LinearLayoutManager(context)

        updateUI()

        return view
    }

    private fun updateUI() {
        val crimes = crimeListViewModel.crimes
        adapter = CrimeAdapter(crimes)
        crimeRecyclerView.adapter = adapter
    }
    ...
}

In later chapters, you will add more to updateUI() as configuring your UI gets more involved.

Run CriminalIntent and scroll through your new RecyclerView, which should look like Figure 9.11.

Figure 9.11  RecyclerView populated with Crimes

RecyclerView populated with Crimes

Swipe or drag down and you will see even more views scroll across your screen. Every visible CrimeHolder should display a distinct Crime. (If your rows are much taller than these, or if you only see one row on the screen, then double-check that the layout_height on your row’s LinearLayout is set to wrap_content.)

When you fling the view up, the scrolling animation should feel as smooth as warm butter. This effect is a direct result of keeping onBindViewHolder(…) small and efficient, doing only the minimum amount of work necessary. Take heed: Always be efficient in your onBindViewHolder(…). Otherwise, your scroll animation could feel as chunky as cold Parmesan cheese.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.8.247