Blog para desarrollo de aplicaciones en Android, aprende paso a paso como crear aplicaciones.

Usamos cookies propias y de terceros que entre otras cosas recogen datos sobre sus hábitos de navegación para mostrarle publicidad personalizada y realizar análisis de uso de nuestro sitio.
Si continúa navegando consideramos que acepta su uso. OK Más información | Y más

RecyclerView con GridLayout en Android

RecyclerView con GridLayout en Android

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

LayoutManagerUsoColumnasTamaños
LinearLayoutManagerListas1Uniforme
GridLayoutManagerCuadrículaVariableUniforme
StaggeredGridLayoutManagerPinterestVariableVariable

🟩 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

  1. Abre Android Studio
  2. Crea el proyecto
  3. Copia el código
  4. Presiona Run
  5. 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