LazyRow Equivalent Implementation in XML Using RecyclerView

In Jetpack Compose, LazyRow offers a concise and efficient way to display a horizontally scrolling list of items. While Jetpack Compose is gaining popularity, many existing Android applications still rely on XML layouts. Implementing the equivalent of LazyRow in XML involves using RecyclerView with a horizontal LinearLayoutManager. This approach provides a similar level of performance and flexibility for handling large datasets.

Understanding LazyRow in Jetpack Compose

LazyRow is a composable function that displays a horizontally scrolling list of items. It only composes and lays out items that are currently visible, making it efficient for large lists. The basic structure is as follows:


import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun SimpleLazyRow() {
    LazyRow(contentPadding = PaddingValues(horizontal = 16.dp)) {
        items(10) { index ->
            Text(text = "Item $index")
            Spacer(modifier = Modifier.width(8.dp))
        }
    }
}

Implementing LazyRow Equivalent in XML using RecyclerView

To achieve the same functionality in XML, you will need to use RecyclerView, an Adapter, and a LinearLayoutManager configured for horizontal scrolling.

Step 1: Add RecyclerView Dependency

First, ensure that you have the RecyclerView dependency in your build.gradle file:

dependencies {
    implementation "androidx.recyclerview:recyclerview:1.3.2"
    implementation "androidx.recyclerview:recyclerview-selection:1.1.0" // Optional, for item selection
}

Step 2: Create the RecyclerView Layout

Add the RecyclerView to your XML layout file (e.g., activity_main.xml):


<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:android="http://schemas.android.com/apk/res/android"/>

Step 3: Create a Layout for RecyclerView Items

Create an XML layout file for each item in the RecyclerView (e.g., item_layout.xml):


<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp">

    <TextView
        android:id="@+id/itemTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Item Text"
        android:textSize="16sp"/>
</LinearLayout>

Step 4: Create the RecyclerView Adapter

Create a RecyclerView Adapter to bind the data to the views:


import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

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

    private List<String> itemList;

    public HorizontalAdapter(List<String> itemList) {
        this.itemList = itemList;
    }

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

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        String item = itemList.get(position);
        holder.itemTextView.setText(item);
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView itemTextView;

        public ViewHolder(View itemView) {
            super(itemView);
            itemTextView = itemView.findViewById(R.id.itemTextView);
        }
    }
}

Step 5: Set up RecyclerView in the Activity

In your Activity, initialize the RecyclerView, set the LinearLayoutManager to horizontal, and attach the adapter:


import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

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

        RecyclerView recyclerView = findViewById(R.id.recyclerView);

        // Create a horizontal layout manager
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        recyclerView.setLayoutManager(layoutManager);

        // Prepare data
        List<String> items = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            items.add("Item " + i);
        }

        // Create and set the adapter
        HorizontalAdapter adapter = new HorizontalAdapter(items);
        recyclerView.setAdapter(adapter);
    }
}

Complete Example

Here is a complete example of the implementation.

activity_main.xml:


<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:clipToPadding="false"
        android:padding="8dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

item_layout.xml:


<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="8dp">

    <TextView
        android:id="@+id/itemTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Item Text"
        android:textSize="16sp"/>

</LinearLayout>

MainActivity.java:


import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

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

        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        recyclerView.setLayoutManager(layoutManager);

        List<String> items = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            items.add("Item " + i);
        }

        HorizontalAdapter adapter = new HorizontalAdapter(items);
        recyclerView.setAdapter(adapter);
    }
}

HorizontalAdapter.java:


import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

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

    private List<String> itemList;

    public HorizontalAdapter(List<String> itemList) {
        this.itemList = itemList;
    }

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

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        String item = itemList.get(position);
        holder.itemTextView.setText(item);
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView itemTextView;

        public ViewHolder(View itemView) {
            super(itemView);
            itemTextView = itemView.findViewById(R.id.itemTextView);
        }
    }
}

Optimizations and Considerations

  • RecyclerView.setItemViewCacheSize(int size): Increase the cache size of RecyclerView to hold more views, especially if the view creation is costly.
  • DiffUtil: Use DiffUtil to efficiently update the RecyclerView when the data changes, minimizing unnecessary updates.
  • RecyclerView.RecycledViewPool: Share the recycled view pool between multiple RecyclerViews for better performance.
  • Item Decoration: Use ItemDecoration for adding spacing and dividers between items.

Conclusion

While LazyRow in Jetpack Compose offers a simple and efficient way to display horizontal lists, implementing an equivalent in XML requires using RecyclerView with a horizontal LinearLayoutManager. By following the steps outlined above, you can create a performant and flexible horizontal list in your XML-based Android applications, ensuring a similar level of efficiency as LazyRow. The RecyclerView approach is well-suited for large datasets and provides the necessary customization options to meet various UI requirements.