Hola amigos 👋 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a crear RecyclerView con GridLayout, perfectos para mostrar galerÃas de imágenes, productos, contactos o cualquier contenido en formato de cuadrÃcula.
Al finalizar este tutorial tendrás:
- RecyclerView con GridLayoutManager
- Diferentes columnas (2, 3, 4)
- StaggeredGridLayoutManager
- GridLayout con span personalizado
- Click listeners
- Animaciones
- Ejemplos prácticos
🟩 1. ¿Qué es GridLayoutManager?
GridLayoutManager es un LayoutManager que organiza los elementos de un RecyclerView en una cuadrÃcula con múltiples columnas, ideal para mostrar colecciones de elementos visuales como fotos, productos o tarjetas.
🟩 2. Tipos de Grid
- GridLayoutManager: CuadrÃcula regular
- StaggeredGridLayoutManager: CuadrÃcula irregular (Pinterest-style)
- GridLayoutManager con Span: Elementos de diferentes tamaños
🟩 3. Crear el Proyecto
Creamos un nuevo proyecto en Android Studio con Empty Activity.
🟩 4. Agregar Dependencias
📄 build.gradle (Module: app)
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.cardview:cardview:1.0.0'
// Glide para cargar imágenes
implementation 'com.github.bumptech.glide:glide:4.16.0'
}🟩 5. Diseño XML Principal
📄 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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RecyclerView GridLayout"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#2196F3"
android:padding="16dp" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F5F5F5" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:clipToPadding="false" />
</LinearLayout>🟩 6. Layout del Item (Card)
📄 res/layout/item_grid.xml
<?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="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/imgPhoto"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop"
android:background="#E0E0E0" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TÃtulo"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#000000"
android:maxLines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/txtDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Descripción"
android:textSize="14sp"
android:textColor="#666666"
android:maxLines="2"
android:ellipsize="end"
android:layout_marginTop="4dp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>🟩 7. Layout para Staggered Grid
📄 res/layout/item_staggered.xml
<?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="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/imgPhoto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:background="#E0E0E0" />
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TÃtulo"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="#000000"
android:padding="12dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>🟩 8. Clase Modelo
📄 GridItem.java
public class GridItem {
private String title;
private String description;
private int imageResource;
private int height; // Para staggered grid
public GridItem(String title, String description, int imageResource) {
this.title = title;
this.description = description;
this.imageResource = imageResource;
this.height = 150;
}
public GridItem(String title, String description, int imageResource, int height) {
this.title = title;
this.description = description;
this.imageResource = imageResource;
this.height = height;
}
public String getTitle() { return title; }
public String getDescription() { return description; }
public int getImageResource() { return imageResource; }
public int getHeight() { return height; }
public void setTitle(String title) { this.title = title; }
public void setDescription(String description) { this.description = description; }
public void setImageResource(int imageResource) { this.imageResource = imageResource; }
public void setHeight(int height) { this.height = height; }
}🟩 9. Adaptador Grid Normal
📄 GridAdapter.java
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
private ArrayList<GridItem> items;
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(GridItem item, int position);
}
public GridAdapter(ArrayList<GridItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_grid, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
GridItem item = items.get(position);
holder.bind(item, listener);
}
@Override
public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView imgPhoto;
TextView txtTitle, txtDescription;
ViewHolder(View itemView) {
super(itemView);
imgPhoto = itemView.findViewById(R.id.imgPhoto);
txtTitle = itemView.findViewById(R.id.txtTitle);
txtDescription = itemView.findViewById(R.id.txtDescription);
}
void bind(GridItem item, OnItemClickListener listener) {
txtTitle.setText(item.getTitle());
txtDescription.setText(item.getDescription());
imgPhoto.setImageResource(item.getImageResource());
itemView.setOnClickListener(v ->
listener.onItemClick(item, getAdapterPosition())
);
// Animación de entrada
itemView.setAlpha(0f);
itemView.animate()
.alpha(1f)
.setDuration(300)
.start();
}
}
}🟩 10. Adaptador Staggered
📄 StaggeredAdapter.java
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class StaggeredAdapter extends RecyclerView.Adapter<StaggeredAdapter.ViewHolder> {
private ArrayList<GridItem> items;
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(GridItem item, int position);
}
public StaggeredAdapter(ArrayList<GridItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_staggered, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
GridItem item = items.get(position);
holder.bind(item, listener);
}
@Override
public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView imgPhoto;
TextView txtTitle;
ViewHolder(View itemView) {
super(itemView);
imgPhoto = itemView.findViewById(R.id.imgPhoto);
txtTitle = itemView.findViewById(R.id.txtTitle);
}
void bind(GridItem item, OnItemClickListener listener) {
txtTitle.setText(item.getTitle());
// Altura variable para efecto staggered
ViewGroup.LayoutParams params = imgPhoto.getLayoutParams();
params.height = item.getHeight();
imgPhoto.setLayoutParams(params);
imgPhoto.setImageResource(item.getImageResource());
itemView.setOnClickListener(v ->
listener.onItemClick(item, getAdapterPosition())
);
}
}
}🟩 11. MainActivity
📄 MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
TabLayout tabLayout;
ArrayList<GridItem> items;
GridAdapter gridAdapter;
StaggeredAdapter staggeredAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
tabLayout = findViewById(R.id.tabLayout);
createData();
setupTabs();
setupGrid(2); // Iniciar con 2 columnas
}
private void createData() {
items = new ArrayList<>();
Random random = new Random();
items.add(new GridItem("Android Studio", "IDE oficial", android.R.drawable.ic_menu_info_details, 150 + random.nextInt(100)));
items.add(new GridItem("Java", "Lenguaje POO", android.R.drawable.ic_menu_edit, 150 + random.nextInt(100)));
items.add(new GridItem("Kotlin", "Lenguaje moderno", android.R.drawable.ic_menu_manage, 150 + random.nextInt(100)));
items.add(new GridItem("Firebase", "Backend service", android.R.drawable.ic_menu_upload, 150 + random.nextInt(100)));
items.add(new GridItem("Material Design", "Sistema diseño", android.R.drawable.ic_menu_gallery, 150 + random.nextInt(100)));
items.add(new GridItem("Gradle", "Build system", android.R.drawable.ic_menu_preferences, 150 + random.nextInt(100)));
items.add(new GridItem("Git", "Control versiones", android.R.drawable.ic_menu_share, 150 + random.nextInt(100)));
items.add(new GridItem("XML", "Markup language", android.R.drawable.ic_menu_view, 150 + random.nextInt(100)));
items.add(new GridItem("RecyclerView", "Lista eficiente", android.R.drawable.ic_menu_sort_by_size, 150 + random.nextInt(100)));
items.add(new GridItem("ConstraintLayout", "Layout flexible", android.R.drawable.ic_menu_crop, 150 + random.nextInt(100)));
items.add(new GridItem("Navigation", "Navegación", android.R.drawable.ic_menu_directions, 150 + random.nextInt(100)));
items.add(new GridItem("Room", "Base de datos", android.R.drawable.ic_menu_save, 150 + random.nextInt(100)));
}
private void setupTabs() {
tabLayout.addTab(tabLayout.newTab().setText("2 Columnas"));
tabLayout.addTab(tabLayout.newTab().setText("3 Columnas"));
tabLayout.addTab(tabLayout.newTab().setText("4 Columnas"));
tabLayout.addTab(tabLayout.newTab().setText("Staggered"));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
switch (tab.getPosition()) {
case 0:
setupGrid(2);
break;
case 1:
setupGrid(3);
break;
case 2:
setupGrid(4);
break;
case 3:
setupStaggeredGrid();
break;
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {}
@Override
public void onTabReselected(TabLayout.Tab tab) {}
});
}
private void setupGrid(int columns) {
GridLayoutManager layoutManager = new GridLayoutManager(this, columns);
recyclerView.setLayoutManager(layoutManager);
gridAdapter = new GridAdapter(items, (item, position) ->
Toast.makeText(this, "Clickeado: " + item.getTitle(), Toast.LENGTH_SHORT).show()
);
recyclerView.setAdapter(gridAdapter);
}
private void setupStaggeredGrid() {
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(
2,
StaggeredGridLayoutManager.VERTICAL
);
recyclerView.setLayoutManager(layoutManager);
staggeredAdapter = new StaggeredAdapter(items, (item, position) ->
Toast.makeText(this, "Clickeado: " + item.getTitle(), Toast.LENGTH_SHORT).show()
);
recyclerView.setAdapter(staggeredAdapter);
}
}🟩 12. ItemDecoration para Espaciado
📄 GridSpacingItemDecoration.java
import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position >= spanCount) {
outRect.top = spacing;
}
}
}
}Uso:
int spacing = 16; // px
recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, spacing, true));🟩 13. Comparación de LayoutManagers
| LayoutManager | Uso | Columnas | Tamaños |
|---|---|---|---|
| LinearLayoutManager | Listas | 1 | Uniforme |
| GridLayoutManager | CuadrÃcula | Variable | Uniforme |
| StaggeredGridLayoutManager | Variable | Variable |
🟩 14. Configuraciones GridLayoutManager
// Grid vertical de 2 columnas
GridLayoutManager layoutManager = new GridLayoutManager(context, 2);
// Grid horizontal
GridLayoutManager layoutManager = new GridLayoutManager(
context,
3,
GridLayoutManager.HORIZONTAL,
false
);
// Span personalizado
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
// Header ocupa todas las columnas
return position == 0 ? 2 : 1;
}
});▶️ Cómo Ejecutar
- Abre Android Studio
- Crea el proyecto
- Copia el código
- Presiona Run
- Cambia entre tabs para ver diferentes grids
🧪 Resultado Final
RecyclerView con múltiples layouts de cuadrÃcula: 2, 3, 4 columnas y staggered grid.
📥 Descargar Proyecto
👉
🙌 Gracias por Visitar mi Blog
✔️ Compártelo
✔️ Déjame un comentario
✔️ SÃgueme para más contenido
❓ Preguntas Frecuentes
1. ¿Cuál es la diferencia entre Grid y Staggered?
Grid tiene elementos del mismo tamaño. Staggered permite diferentes alturas (estilo Pinterest).
2. ¿Cómo cambio el número de columnas?
Usa new GridLayoutManager(context, numeroColumnas).
3. ¿Puedo tener elementos de diferentes tamaños?
SÃ, usa setSpanSizeLookup() para controlar cuántas columnas ocupa cada elemento.
4. ¿Cómo agrego espaciado entre elementos?
Usa ItemDecoration o agrega margin en el layout del item.

No hay comentarios:
Publicar un comentario