Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ GridLayoutManager ใน RecyclerView

ภาพรวม

ตัวอย่างนี้แสดงวิธีใช้ GridLayoutManager กับ RecyclerView เพื่อแสดงข้อมูลในรูปแบบกริด (Grid) พร้อม AppBarLayout และ Toolbar ตาม Material Design

ขั้นตอนการสร้าง

ขั้นตอนที่ 1: สร้างโปรเจกต์และเพิ่ม Dependency

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.3.0'
    implementation 'com.google.android.material:material:1.9.0'
}

ขั้นตอนที่ 2: Layout หลัก (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways|snap"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

ขั้นตอนที่ 3: โค้ดหลัก (src/MainActivity.java)

package com.example.myapplication;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private CustomAdapter adapter;
    private ArrayList<String> dataList = new ArrayList<>();

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

        // Toolbar
        Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.setTitle("GridLayoutManager Demo");
        setSupportActionBar(toolbar);

        // RecyclerView with GridLayoutManager
        recyclerView = findViewById(R.id.recycler_view);
        
        // 2 คอลัมน์ (spanCount = 2)
        GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
        recyclerView.setLayoutManager(layoutManager);
        
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        
        adapter = new CustomAdapter(this, dataList, position -> {
            dataList.remove(position);
            adapter.notifyItemRemoved(position);
            adapter.notifyItemRangeChanged(position, dataList.size());
        });
        
        recyclerView.setAdapter(adapter);
        
        // Divider (Grid ใช้ทั้ง Horizontal และ Vertical)
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL));
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));

        // Sample data
        for (int i = 0; i < 20; i++) {
            dataList.add("Item " + (i + 1));
        }
        adapter.notifyDataSetChanged();
    }
}

ขั้นตอนที่ 4: Adapter (src/CustomAdapter.java)

package com.example.myapplication;

import android.content.Context;
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.ArrayList;
import java.util.function.IntConsumer;

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private Context context;
    private ArrayList<String> dataList;
    private IntConsumer onItemClick;

    public CustomAdapter(Context context, ArrayList<String> dataList, IntConsumer onItemClick) {
        this.context = context;
        this.dataList = dataList;
        this.onItemClick = onItemClick;
    }

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

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.textView.setText(dataList.get(position));
        holder.itemView.setOnClickListener(v -> {
            if (onItemClick != null) onItemClick.accept(position);
        });
    }

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

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        ViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.title);
        }
    }
}

ขั้นตอนที่ 5: Item Layout (res/layout/grid_item.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="120dp"
    app:cardElevation="4dp"
    app:cardCornerRadius="8dp"
    app:cardUseCompatPadding="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:padding="16dp">

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:textStyle="bold"
            android:gravity="center" />

    </LinearLayout>
</androidx.cardview.widget.CardView>

GridLayoutManager Configuration

1. Span Count (จำนวนคอลัมน์)

// แนวนอน (Vertical scrolling) - 2 คอลัมน์
GridLayoutManager layoutManager = new GridLayoutManager(context, 2);

// แนวตั้ง (Horizontal scrolling) - 3 แถว
GridLayoutManager layoutManager = new GridLayoutManager(context, 3, GridLayoutManager.HORIZONTAL, false);

2. Span Size Lookup (รวมเซลล์ - Header/Footer/Full-width)

layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int position) {
        if (isHeader(position) || isFooter(position)) {
            return spanCount; // กว้างเต็มจอ
        }
        return 1; // กว้าง 1 คอลัมน์
    }
});

3. Gap Decoration (ช่องว่างระหว่าง Item)

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;
            outRect.bottom = spacing;
        }
    }
}

// ใช้งาน
recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, 16, true));

GridLayoutManager vs LinearLayoutManager vs StaggeredGridLayoutManager

LayoutManagerเหมาะสำหรับคุณสมบัติ
LinearLayoutManagerList แนวตั้ง/แนวนอนSimple, Efficient
GridLayoutManagerGrid เท่ากันทุก CellUniform size, SpanSizeLookup
StaggeredGridLayoutManagerGrid ความสูงไม่เท่ากัน (Pinterest style)Variable height, Gaps handling
FlexboxLayoutManagerWrap content, ResponsiveFlexbox CSS-like, Third-party

การทดสอบ

รันแอป จะเห็น Grid 2 คอลัมน์ พร้อม Toolbar ที่ซ่อน/แสดงเมื่อ Scroll คลิก Item จะลบ Item นั้นออกพร้อม Animation