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
DiffUtilto 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
ItemDecorationfor 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.