CACS351 Mobile Programming

Mobile ProgrammingUnit 612 min read

Lists, Adapters, RecyclerView: Dynamic UI & Efficient Data Display

Unit 6 of Mobile Programming: Covers how to display dynamic lists in Android using ListView, RecyclerView, and custom Adapters, with a focus on performance optimization, layout types (Linear, Grid, Staggered), and real-world use cases like e-commerce product grids or chat message threads.

Core Concepts: Lists, Adapters, and RecyclerView

1. Why Lists?

Mobile apps often need to display dynamic data (e.g., chat messages, news feeds, shopping cart items). Static layouts (like LinearLayout) are inefficient for large datasets. Instead, we use lists to render items dynamically.

Key Idea:

  • A list is a scrollable container that displays a collection of items (e.g., ListView, RecyclerView).
  • Each item is typically a view (e.g., a TextView + ImageView combo for a chat bubble).

2. ListView vs. RecyclerView: A Performance Showdown

Feature ListView RecyclerView
Recycling Views ❌ No (creates new views on scroll) ✅ Yes (reuses views to save memory)
Flexible Layouts ❌ Only LinearLayout (vertical/horizontal) ✅ Supports LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager
Custom Animations ❌ Limited ✅ Full control (e.g., item swipe-to-delete)
Complex Data ❌ Struggles with large datasets ✅ Optimized for 1000+ items
Learning Curve ⚠️ Simpler (but outdated) 🚀 Steeper (but modern)

Visual: ListView vs. RecyclerView View Recycling

flowchart TD
    subgraph ListView["ListView (No Recycling)"]
        A["Item 1"] --> B["Item 2"] --> C["Item 3"]
        A -->|"Scroll Down"| D["New Item 1 (Created Fresh)"]
    end
    subgraph RecyclerView["RecyclerView (Recycling)"]
        A["Item 1"] --> B["Item 2"] --> C["Item 3"]
        A -->|"Scroll Down"| D["Item 1 Reused (Reassigned to New Data)"]
    end

Why RecyclerView is Better:

  • Memory Efficiency: Reuses views instead of creating new ones.
  • Smooth Scrolling: Critical for apps like WhatsApp (chat threads) or Daraz (product grids).
  • Future-Proof: Used in modern Android apps (e.g., Google Maps for dynamic markers).

3. Adapters: The Bridge Between Data and Views

An Adapter connects your data (e.g., an ArrayList) to a list (e.g., RecyclerView). It defines:

  1. How many items to display (getItemCount()).
  2. What each item looks like (getView() or onBindViewHolder()).
  3. How to populate each item (e.g., set text/image).

Example: Adapter for Programming Languages

public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.LanguageViewHolder> {
    private List<String> languages;

    public LanguageAdapter(List<String> languages) {
        this.languages = languages;
    }

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

    @Override
    public void onBindViewHolder(@NonNull LanguageViewHolder holder, int position) {
        String lang = languages.get(position);
        holder.textView.setText(lang);
        // Set image based on position (e.g., icon for Python, Java, etc.)
    }

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

    static class LanguageViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public LanguageViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.lang_name);
            iconView = itemView.findViewById(R.id.lang_icon);
        }
    }
}

Visual: Adapter Binding Process

sequenceDiagram
    participant App as App
    participant Adapter as Adapter
    participant RV as RecyclerView
    participant VH as ViewHolder

    App->>Adapter: onCreateViewHolder()
    Adapter->>RV: Inflates layout (item_language.xml)
    RV-->>Adapter: Returns ViewHolder
    Adapter->>VH: Calls onBindViewHolder()
    VH->>VH: Sets text/image (e.g., "Python")
    VH-->>Adapter: View bound

4. RecyclerView Layout Managers

RecyclerView doesn’t define how items are laid out—it uses LayoutManagers for that. Common types:

DefaultStaggeredCustom LayoutManagerLinearGridStaggeredGridCustom
Relationship between RecyclerView layout managers (simplified hierarchy)
Layout Manager Use Case Visualization
LinearLayoutManager Vertical/horizontal scroll Linear Layout
GridLayoutManager Grid (e.g., Daraz product cards) Grid Layout
StaggeredGridLayoutManager Pinch-zoom grids (e.g., Pinterest) Staggered Grid

Example: Setting a GridLayoutManager

RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); // 3 columns
recyclerView.setAdapter(new LanguageAdapter(languages));

Visual: GridLayoutManager (3 Columns)

| [Python] | [Java]  | [C++]   |
|----------|---------|---------|
| [Kotlin] | [Swift] | [Go]    |
| [Rust]   | [PHP]   | [Ruby]  |

5. Item Decorators and Dividers

Add visual enhancements like:

  • Dividers (e.g., gray lines between items in WhatsApp).
  • Spacing (e.g., margins between grid items in Daraz).
  • Headers/Footers (e.g., "Top Picks" section in a list).

Example: Adding a Divider

recyclerView.addItemDecoration(
    new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
);

Visual: RecyclerView with Dividers

graph TD
    A["Item 1"] --> B["Item 2"]
    B -->|"Divider"| C["Item 3"]
    C --> D["Item 4"]
    Divider -->|"Style"| "background: #ddd; height: 1px"

6. DiffUtil for Efficient Updates

When data changes (e.g., new messages in WhatsApp), RecyclerView reuses views but needs to know which items changed. DiffUtil compares old and new data to minimize unnecessary view updates.

Example: Using DiffUtil

public class LanguageDiffUtil extends DiffUtil.Callback {
    private final List<String> oldLanguages;
    private final List<String> newLanguages;

    public LanguageDiffUtil(List<String> oldLanguages, List<String> newLanguages) {
        this.oldLanguages = oldLanguages;
        this.newLanguages = newLanguages;
    }

    @Override
    public int getOldListSize() { return oldLanguages.size(); }
    @Override
    public int getNewListSize() { return newLanguages.size(); }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return oldLanguages.get(oldItemPosition).equals(newLanguages.get(newItemPosition));
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        return oldLanguages.get(oldItemPosition) == newLanguages.get(newItemPosition);
    }
}

Visual: DiffUtil in Action

-1-0.50.511.522.532468xyOld Data (Python, Java)New Data (Python, Kotlin)Python (Unchanged)Java → Kotlin (Updated)
DiffUtil detects changes between old and new datasets (simplified visualization)

7. Worked Example: GridView vs. RecyclerView for Languages

Task: Display 5 programming languages in a grid (like Daraz’s product cards).

Option 1: GridView (Legacy)
List<String> languages = Arrays.asList("Python", "Java", "C++", "Kotlin", "Swift");
ArrayAdapter<String> adapter = new ArrayAdapter<>(
    this, android.R.layout.simple_list_item_1, languages
);
GridView gridView = findViewById(R.id.grid_view);
gridView.setAdapter(adapter);
gridView.setNumColumns(2); // 2 columns

Problem: No view recycling → poor performance for >50 items.

Option 2: RecyclerView (Modern)
  1. Layout: GridLayoutManager(2).
  2. Adapter: Custom LanguageAdapter (as shown above).
  3. Item Layout (item_language.xml):
    <LinearLayout xmlns:android="..." android:orientation="vertical">
        <ImageView android:id="@+id/lang_icon" android:layout_width="50dp"/>
        <TextView android:id="@+id/lang_name" android:layout_width="wrap_content"/>
    </LinearLayout>
    

Visual: Final Grid (RecyclerView)

| [Python] | [Java]  |
|----------|---------|
| [C++]    | [Kotlin]|
| [Swift]  |         |

Performance Comparison:

Metric GridView RecyclerView
Memory Usage High (creates new views) Low (recycles views)
Scroll Speed Slow (>20 items) Smooth (1000+ items)
Customization Limited Full control (animations, etc.)

8. Real-World Applications

In the Real World
  1. Daraz (Nepal):

    • Idea: Uses RecyclerView with GridLayoutManager to display product cards efficiently.
    • Why? Smooth scrolling for 1000+ products; view recycling saves memory.
    • Worked Example: If Daraz shows 12 products per row, the GridLayoutManager(3) is set with spanSizeLookup to handle varying item heights (e.g., some items have images + text, others have banners).
  2. WhatsApp (Global):

    • Idea: Uses RecyclerView with LinearLayoutManager for chat messages.
    • Why? Supports:
      • Left/right alignment (sender/receiver).
      • Media previews (images/videos).
      • Typing indicators (real-time updates via DiffUtil).
  3. Pathao (Nepal):

    • Idea: Uses RecyclerView with StaggeredGridLayoutManager for driver cards.
    • Why? Pinch-zoom effect for driver photos (staggered rows).

Visual: Pathao’s Driver Grid (StaggeredGridLayoutManager)

graph TD
    subgraph Row1
        A["Driver 1"] --> B["Driver 2"] --> C["Driver 3"]
    end
    subgraph Row2
        D["Driver 4"] --> E["Driver 5"]
    end
    subgraph Row3
        F["Driver 6"]
    end

9. Common Pitfalls and Fixes

Issue Cause Solution
Empty RecyclerView Adapter not set or data empty Check adapter.notifyDataSetChanged()
Slow Scrolling No view recycling Use RecyclerView + DiffUtil
Incorrect Grid Spacing Missing itemDecoration Add GridSpacingItemDecoration
Memory Leaks Holder retains context Use WeakReference in ViewHolder
01ViewHolder234
Common pitfall: View recycling failure (null ViewHolder in position 2)

Example: Fixing Slow Scrolling

// Before (slow):
RecyclerView recyclerView = new RecyclerView(this);
recyclerView.setAdapter(new MyAdapter(data));

// After (fast):
RecyclerView recyclerView = new RecyclerView(this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new MyAdapter(data));
recyclerView.setHasFixedSize(true); // Optimize for static size

Exam Tip

  • Focus on:
    1. Difference between ListView and RecyclerView (always mention view recycling).
    2. LayoutManagers (know when to use Grid, Linear, or Staggered).
    3. Adapter implementation (show onCreateViewHolder and onBindViewHolder).
    4. Performance optimizations (e.g., DiffUtil, setHasFixedSize).
  • Past Exam Question: "List three features of RecyclerView and develop an app to display 5 languages in GridView."
    • Features:
      1. View recycling (memory efficiency).
      2. Supports multiple layout managers (Linear, Grid, Staggered).
      3. Custom animations and item decorators.
    • App Code: Use GridLayoutManager(2) + custom Adapter (as shown above).
  • Common Marks Lost:
    • Forgetting to call notifyDataSetChanged() after updating data.
    • Incorrectly setting the number of columns in GridLayoutManager.
    • Not using ViewHolder pattern (leads to performance issues).

Final Note: Always prefer RecyclerView over ListView in modern apps. For grids, use GridLayoutManager; for dynamic lists, use LinearLayoutManager. Optimize with DiffUtil for large datasets!

Based on the TU BCA syllabus for Mobile Programming (CACS351), unit 6.

Discussion

Loading…