Open menu with table of contents Android Lists
Logo of Stuttgart Media University for light theme Logo of Stuttgart Media University for dark theme
Android Development

Android Lists

Stuttgart Media University

1 Agenda

  • Adapter
  • RecyclerView
  • LayoutManager
  • RecyclerView.ViewHolder
  • RecyclerView.Adapter

2 RecyclerView

  • A RecyclerView shows items in a scrollable list
  • The items are defined by an Adapter which is also responsible for the layout and rendering of the items in the list
  • Each item is selectable

40%

2.1 Adapter

  • The RecyclerView delegates the presentation of items to the corresponding Adapter
  • The Android Framework already provides us with implementations of the Adapter interface

80%

2.2 RecyclerView

  • Replaces old ListView
  • More advanced and flexible version of ListView
  • Solves performance bottleneck by forcing developers to implement ViewHolder pattern
  • Can be used to display
    • List
    • Grid
    • Pager
  • LayoutManager defines direction
    • vertical
    • horizontal
  • Highly customizable

2.3 RecyclerView dependency

  • Part of Android Jetpack
  • Must be added manually to gradle
dependencies {
    // ...
    implementation "androidx.recyclerview:recyclerview:1.2.1"
    // ... 
}  

2.4 RecyclerView Layout

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

2.5 LayoutManager

  • Is responsible for measuring and positioning item views within a RecyclerView
  • Can be used to implement a standard vertically scrolling list, a uniform grid, staggered grids, horizontally scrolling collections and more
  • Several stock layout managers are provided for general use.

Default vertical scrolling RecyclerView

RecyclerView recyclerView = findViewById(R.id.list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

Horizontal scrolling RecyclerView

recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));

Grid vertical scrolling RecyclerView

recyclerView.setLayoutManager(new GridLayoutManager(this, 3));

2.6 RecyclerView.ViewHolder

  • Describes an item view and metadata about its place within the RecyclerView
  • Lets the Adapter cache expensive View.findViewById(int) results
  • Will be created and reused by RecyclerView.Adapter
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
    val itemNumberView: TextView = itemView.findViewById(R.id.item_number)
    val contentView: TextView = itemView.findViewById(R.id.content)
}

2.7 RecyclerView.Adapter

  • Forces you to implement ViewHolder
  • Creates view holders as needed
class MovieAdapter(private val list: List<DummyItem>, private val movieClickListener: MovieClickListener): RecyclerView.Adapter<MovieAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

        val view = LayoutInflater.from(parent.context).inflate(R.layout.movie_view, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val dummyItemModel = list[position]
        holder.itemNumberView.text = dummyItemModel.id
        holder.contentView.text = dummyItemModel.content
    }

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

    class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        ...
    }
}

2.8 RecyclerView Setup

val recyclerView = findViewById<RecyclerView>(R.id.list)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))

val adapter = MovieAdapter(data)
recyclerView.adapter = adapter

3 RecyclerView Full Example

3.1 Activity Layout

<?xml version="1.0" encoding="utf-8"?>
<!-- activity_main3.xml -->
<androidx.recyclerview.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

3.2 List Item Layout

<?xml version="1.0" encoding="utf-8"?>
<!-- list_item_view.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/item_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/item_number" />
</androidx.constraintlayout.widget.ConstraintLayout>

3.3 Activity Class

package de.hdmstuttgart.manifestdemo;

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity(), MovieClickListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.list)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))

        val list: MutableList<DummyItem> = ArrayList<DummyItem>()
        for (i in 0..100) {
            list.add(DummyItem(i.toString(), " - "))
        }

        val adapter = MovieAdapter(data)
        recyclerView.adapter = adapter
    }

    class DummyItem(var id: String, var content: String)

    class MovieAdapter(private val list: List<DummyItem>, private val movieClickListener: MovieClickListener): RecyclerView.Adapter<MovieAdapter.ViewHolder>() {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

            val view = LayoutInflater.from(parent.context).inflate(R.layout.movie_view, parent, false)
            return ViewHolder(view)
        }

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val dummyItemModel = list[position]
            holder.itemNumberView.text = dummyItemModel.id
            holder.contentView.text = dummyItemModel.content
        }

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

        class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
            val itemNumberView: TextView = itemView.findViewById(R.id.item_number)
            val contentView: TextView = itemView.findViewById(R.id.content)
        }
    }
}

4 Android Assignment 2

5 Summary

  • A RecyclerView uses the delegate pattern
    • The Adapter is the delegate the defines the content and how the list is presented
      • This is the same pattern used on iOS for TableViews