google-site-verification: google61fe8ba583a51912.html
RecyclerView and Adapters in Android Java

Introduction to RecyclerView

RecyclerView is a more advanced and flexible version of ListView for displaying large data sets efficiently. It recycles item views as they scroll off-screen, providing better performance and memory management.

Basic RecyclerView Setup

1. Add Dependencies

dependencies { implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation 'androidx.cardview:cardview:1.0.0' }

2. Layout Files

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>

item_layout.xml (Individual item layout)

<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" app:cardCornerRadius="8dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp"> <ImageView android:id="@+id/imageView" android:layout_width="60dp" android:layout_height="60dp" android:src="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginStart="16dp" android:orientation="vertical"> <TextView android:id="@+id/textTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/textDescription" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Description" android:textSize="14sp" android:layout_marginTop="4dp" /> </LinearLayout> <ImageButton android:id="@+id/btnDelete" android:layout_width="40dp" android:layout_height="40dp" android:src="@android:drawable/ic_delete" android:background="?android:attr/selectableItemBackground" /> </LinearLayout> </androidx.cardview.widget.CardView>

Data Model Class

public class Item { private String title; private String description; private int imageResource; private boolean isSelected; public Item(String title, String description, int imageResource) { this.title = title; this.description = description; this.imageResource = imageResource; this.isSelected = false; } // Getters and Setters public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public int getImageResource() { return imageResource; } public void setImageResource(int imageResource) { this.imageResource = imageResource; } public boolean isSelected() { return isSelected; } public void setSelected(boolean selected) { isSelected = selected; } }

ViewHolder Pattern

Basic ViewHolder

import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; public class ItemViewHolder extends RecyclerView.ViewHolder { public ImageView imageView; public TextView textTitle; public TextView textDescription; public ImageButton btnDelete; public ItemViewHolder(@NonNull View itemView) { super(itemView); imageView = itemView.findViewById(R.id.imageView); textTitle = itemView.findViewById(R.id.textTitle); textDescription = itemView.findViewById(R.id.textDescription); btnDelete = itemView.findViewById(R.id.btnDelete); } }

RecyclerView Adapter

Basic Adapter Implementation

import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.List; public class ItemAdapter extends RecyclerView.Adapter<ItemViewHolder> { private List<Item> itemList; private OnItemClickListener listener; // Interface for click events public interface OnItemClickListener { void onItemClick(int position); void onDeleteClick(int position); } public ItemAdapter(List<Item> itemList, OnItemClickListener listener) { this.itemList = itemList; this.listener = listener; } @NonNull @Override public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_layout, parent, false); return new ItemViewHolder(view); } @Override public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) { Item currentItem = itemList.get(position); holder.imageView.setImageResource(currentItem.getImageResource()); holder.textTitle.setText(currentItem.getTitle()); holder.textDescription.setText(currentItem.getDescription()); // Handle item click holder.itemView.setOnClickListener(v -> { if (listener != null) { listener.onItemClick(position); } }); // Handle delete button click holder.btnDelete.setOnClickListener(v -> { if (listener != null) { listener.onDeleteClick(position); } }); } @Override public int getItemCount() { return itemList.size(); } }

Advanced Adapter with Multiple View Types

Multiple View Types Adapter

import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.List; public class MultiTypeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<Object> items; private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; private static final int TYPE_FOOTER = 2; public MultiTypeAdapter(List<Object> items) { this.items = items; } @Override public int getItemViewType(int position) { Object item = items.get(position); if (item instanceof Header) { return TYPE_HEADER; } else if (item instanceof Footer) { return TYPE_FOOTER; } else { return TYPE_ITEM; } } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); switch (viewType) { case TYPE_HEADER: View headerView = inflater.inflate(R.layout.header_layout, parent, false); return new HeaderViewHolder(headerView); case TYPE_FOOTER: View footerView = inflater.inflate(R.layout.footer_layout, parent, false); return new FooterViewHolder(footerView); default: View itemView = inflater.inflate(R.layout.item_layout, parent, false); return new ItemViewHolder(itemView); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case TYPE_HEADER: Header header = (Header) items.get(position); HeaderViewHolder headerHolder = (HeaderViewHolder) holder; headerHolder.textHeader.setText(header.getTitle()); break; case TYPE_ITEM: Item item = (Item) items.get(position); ItemViewHolder itemHolder = (ItemViewHolder) holder; itemHolder.textTitle.setText(item.getTitle()); itemHolder.textDescription.setText(item.getDescription()); break; case TYPE_FOOTER: Footer footer = (Footer) items.get(position); FooterViewHolder footerHolder = (FooterViewHolder) holder; footerHolder.textFooter.setText(footer.getMessage()); break; } } @Override public int getItemCount() { return items.size(); } // Different ViewHolder classes static class HeaderViewHolder extends RecyclerView.ViewHolder { TextView textHeader; HeaderViewHolder(View view) { super(view); textHeader = view.findViewById(R.id.textHeader); } } static class FooterViewHolder extends RecyclerView.ViewHolder { TextView textFooter; FooterViewHolder(View view) { super(view); textFooter = view.findViewById(R.id.textFooter); } } }

DiffUtil for Efficient Updates

Using DiffUtil for Better Performance

import androidx.recyclerview.widget.DiffUtil; import java.util.List; public class ItemDiffCallback extends DiffUtil.Callback { private final List<Item> oldList; private final List<Item> newList; public ItemDiffCallback(List<Item> oldList, List<Item> newList) { this.oldList = oldList; this.newList = newList; } @Override public int getOldListSize() { return oldList.size(); } @Override public int getNewListSize() { return newList.size(); } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { Item oldItem = oldList.get(oldItemPosition); Item newItem = newList.get(newItemPosition); return oldItem.getTitle().equals(newItem.getTitle()); } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { Item oldItem = oldList.get(oldItemPosition); Item newItem = newList.get(newItemPosition); return oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getDescription().equals(newItem.getDescription()) && oldItem.getImageResource() == newItem.getImageResource(); } } // Enhanced Adapter with DiffUtil public class EfficientItemAdapter extends RecyclerView.Adapter<ItemViewHolder> { private List<Item> items; private OnItemClickListener listener; public EfficientItemAdapter(List<Item> items, OnItemClickListener listener) { this.items = items; this.listener = listener; } public void updateItems(List<Item> newItems) { DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff( new ItemDiffCallback(this.items, newItems) ); this.items.clear(); this.items.addAll(newItems); diffResult.dispatchUpdatesTo(this); } // ... rest of the adapter methods }

Complete MainActivity Implementation

import android.os.Bundle; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity implements ItemAdapter.OnItemClickListener { private RecyclerView recyclerView; private ItemAdapter adapter; private List<Item> itemList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initializeData(); setupRecyclerView(); } private void initializeData() { itemList = new ArrayList<>(); itemList.add(new Item("Java", "Object-oriented programming language", R.drawable.java_icon)); itemList.add(new Item("Kotlin", "Modern programming language for Android", R.drawable.kotlin_icon)); itemList.add(new Item("Python", "High-level programming language", R.drawable.python_icon)); itemList.add(new Item("JavaScript", "Scripting language for web development", R.drawable.js_icon)); itemList.add(new Item("C++", "General-purpose programming language", R.drawable.cpp_icon)); // Add more items as needed } private void setupRecyclerView() { recyclerView = findViewById(R.id.recyclerView); // Set layout manager LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); // Set adapter adapter = new ItemAdapter(itemList, this); recyclerView.setAdapter(adapter); // Add item decoration for spacing recyclerView.addItemDecoration(new ItemDecoration(16)); } @Override public void onItemClick(int position) { Item item = itemList.get(position); Toast.makeText(this, "Clicked: " + item.getTitle(), Toast.LENGTH_SHORT).show(); // Toggle selection item.setSelected(!item.isSelected()); adapter.notifyItemChanged(position); } @Override public void onDeleteClick(int position) { Item item = itemList.get(position); itemList.remove(position); adapter.notifyItemRemoved(position); Toast.makeText(this, "Deleted: " + item.getTitle(), Toast.LENGTH_SHORT).show(); } // Method to add new item public void addItem(Item newItem) { itemList.add(0, newItem); // Add to beginning adapter.notifyItemInserted(0); recyclerView.scrollToPosition(0); } // Method to update item public void updateItem(int position, Item updatedItem) { itemList.set(position, updatedItem); adapter.notifyItemChanged(position); } }

Custom Item Decoration

import android.graphics.Rect; import android.view.View; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; public class ItemDecoration extends RecyclerView.ItemDecoration { private final int spacing; public ItemDecoration(int spacing) { this.spacing = spacing; } @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { outRect.left = spacing; outRect.right = spacing; outRect.bottom = spacing; // Add top margin only for the first item if (parent.getChildAdapterPosition(view) == 0) { outRect.top = spacing; } } }

Advanced Features

1. Swipe to Delete

import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback { private ItemAdapter adapter; public SwipeToDeleteCallback(ItemAdapter adapter) { super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT); this.adapter = adapter; } @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { return false; // We don't support drag & drop } @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { int position = viewHolder.getAdapterPosition(); adapter.deleteItem(position); } } // Usage in MainActivity private void enableSwipeToDelete() { SwipeToDeleteCallback swipeToDeleteCallback = new SwipeToDeleteCallback(adapter); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeToDeleteCallback); itemTouchHelper.attachToRecyclerView(recyclerView); }

2. Drag and Drop

public class DragDropCallback extends ItemTouchHelper.Callback { private final ItemAdapter adapter; public DragDropCallback(ItemAdapter adapter) { this.adapter = adapter; } @Override public boolean isLongPressDragEnabled() { return true; } @Override public boolean isItemViewSwipeEnabled() { return false; } @Override public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; return makeMovementFlags(dragFlags, 0); } @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) { adapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); return true; } @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { // Not used } } // Add this method to your adapter public void onItemMove(int fromPosition, int toPosition) { Collections.swap(itemList, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition); }

3. Endless Scrolling

public abstract class EndlessScrollListener extends RecyclerView.OnScrollListener { private LinearLayoutManager layoutManager; private int visibleThreshold = 5; private int currentPage = 0; private int previousTotalItemCount = 0; private boolean loading = true; private int startingPageIndex = 0; public EndlessScrollListener(LinearLayoutManager layoutManager) { this.layoutManager = layoutManager; } @Override public void onScrolled(@NonNull RecyclerView view, int dx, int dy) { int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition(); int totalItemCount = layoutManager.getItemCount(); if (totalItemCount < previousTotalItemCount) { this.currentPage = this.startingPageIndex; this.previousTotalItemCount = totalItemCount; if (totalItemCount == 0) { this.loading = true; } } if (loading && (totalItemCount > previousTotalItemCount)) { loading = false; previousTotalItemCount = totalItemCount; } if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) { currentPage++; onLoadMore(currentPage, totalItemCount, view); loading = true; } } public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view); } // Usage recyclerView.addOnScrollListener(new EndlessScrollListener(layoutManager) { @Override public void onLoadMore(int page, int totalItemsCount, RecyclerView view) { // Load more data loadMoreData(page); } });

Best Practices and Performance Tips

1. ViewHolder Pattern

  • Always use ViewHolder pattern
  • Store view references in ViewHolder
  • Avoid findViewById() in onBindViewHolder()

2. Efficient Data Updates

  • Use DiffUtil for large datasets
  • Prefer notifyItemChanged() over notifyDataSetChanged()
  • Use stable IDs when possible

3. Memory Management

  • Clear references in onViewRecycled()
  • Use weak references for click listeners
  • Implement proper lifecycle management

4. Layout Optimization

  • Use ConstraintLayout for complex item layouts
  • Avoid nested weights
  • Use appropriate view types

Common Issues and Solutions

1. Item Click Not Working

// Make sure items are clickable holder.itemView.setClickable(true); // Or set focusable if needed holder.itemView.setFocusable(true);

2. RecyclerView Not Showing

// Check layout manager is set recyclerView.setLayoutManager(new LinearLayoutManager(this)); // Check adapter is set recyclerView.setAdapter(adapter); // Check data is not empty if (itemList.isEmpty()) { // Show empty state }

3. Performance Issues

// Use setHasFixedSize(true) if item size doesn't change recyclerView.setHasFixedSize(true); // Use setItemViewCacheSize() for better scrolling recyclerView.setItemViewCacheSize(20); // Use setRecycledViewPool() for multiple RecyclerViews RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool(); recyclerView.setRecycledViewPool(sharedPool);

RecyclerView with custom adapters provides a powerful and efficient way to display lists and grids in Android applications. By following these patterns and best practices, you can create smooth, responsive list interfaces that handle large datasets efficiently.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper