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
public class ViewHolder extends RecyclerView.ViewHolder {
        public final View mView;
        public final TextView mItemNumberView;
        public final TextView mContentView;
        public DummyItem mItem;

        public ViewHolder(View view) {
            super(view);
            mView = view;
            mItemNumberView = (TextView) view.findViewById(R.id.item_number);
            mContentView = (TextView) view.findViewById(R.id.content);
        }
    }

2.7 RecyclerView.Adapter

  • Forces you to implement ViewHolder
  • Creates view holders as needed
public class MyItemRecyclerViewAdapter
        extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> {

    private final List<DummyItem> mValues;

    public MyItemRecyclerViewAdapter(List<DummyItem> items) {
        mValues = items;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.fragment_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.mItem = mValues.get(position);
        holder.mItemNumberView.setText(mValues.get(position).id);
        holder.mContentView.setText(mValues.get(position).content);
    }

    @Override
    public int getItemCount() {
        return mValues.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        // ...
    }
}

2.8 RecyclerView Setup

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

// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
recyclerView.setHasFixedSize(true);

MyItemRecyclerViewAdapter adapter = new MyItemRecyclerViewAdapter(list);

recyclerView.setAdapter(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 androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity3 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

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

        List<DummyItem> list = new ArrayList<>();
        for (int i = 0; i <= 100; i++) {
            list.add(new DummyItem(String.valueOf(i), " - "));
        }

        MyItemRecyclerViewAdapter adapter = new MyItemRecyclerViewAdapter(list);

        recyclerView.setAdapter(adapter);
    }

    public class DummyItem {
        public DummyItem(String id, String content) {
            this.id = id;
            this.content = content;
        }

        public String id;
        public String content;
    }

    public class MyItemRecyclerViewAdapter
            extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> {

        private final List<DummyItem> mValues;

        public MyItemRecyclerViewAdapter(List<DummyItem> items) {
            mValues = items;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_item_view, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            holder.mItem = mValues.get(position);
            holder.mItemNumberView.setText(mValues.get(position).id);
            holder.mContentView.setText(mValues.get(position).content);
        }

        @Override
        public int getItemCount() {
            return mValues.size();
        }

        public class ViewHolder extends RecyclerView.ViewHolder {
            public final View mView;
            public final TextView mItemNumberView;
            public final TextView mContentView;
            public DummyItem mItem;

            public ViewHolder(View view) {
                super(view);
                mView = view;
                mItemNumberView = (TextView) view.findViewById(R.id.item_number);
                mContentView = (TextView) view.findViewById(R.id.content);
            }
        }

    }
}

3.4 ViewHolder

public class ViewHolder extends RecyclerView.ViewHolder {
    public final View mView;
    public final TextView mItemNumberView;
    public final TextView mContentView;
    public DummyItem mItem;

    public ViewHolder(View view) {
        super(view);
        mView = view;
        mItemNumberView = (TextView) view.findViewById(R.id.item_number);
        mContentView = (TextView) view.findViewById(R.id.content);

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(
                    v.getContext(),
                    mItemNumberView.getText().toString(),
                    Toast.LENGTH_SHORT
                ).show();
            }
        });
    }
}

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