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

รวม Snackbar ใน Android (Material Design)


Snackbar คืออะไร?

Snackbar เป็น Component ของ Material Design แสดงข้อความชั่วคราวที่ด้านล่างจอ พร้อม Action Button (เช่น Undo, Retry, Dismiss) ต่างจาก Toast ที่:

  • มี Action Button ได้
  • อยู่เหนือ Content (ไม่บัง UI)
  • Swipe to Dismiss ได้
  • Animation ทันสมัย
  • รองรับ Custom Layout

Dependency

dependencies {
    // Material Components (รวม Snackbar)
    implementation 'com.google.android.material:material:1.11.0'
    
    // หรือ legacy support library (Deprecated)
    // implementation 'com.android.support:design:28.0.0'
}

ข้อกำหนดพื้นฐาน: Parent View

Snackbar ต้องการ View ที่เป็น Container (CoordinatorLayout, ConstraintLayout, FrameLayout, etc.) เพื่อคำนวณตำแหน่งและจัดการ Behavior (Swipe, Floating Action Button)

Layout ที่ดี (CoordinatorLayout)

<?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:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:src="@drawable/ic_add" />

    <!-- Content -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Snackbar Demo"
        android:layout_gravity="center" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

การใช้งานพื้นฐาน

Simple Snackbar

View rootView = findViewById(R.id.coordinatorLayout); // หรือ any view in hierarchy

Snackbar.make(rootView, "Message saved", Snackbar.LENGTH_SHORT)
    .show();

Snackbar พร้อม Action

Snackbar.make(rootView, "Item deleted", Snackbar.LENGTH_LONG)
    .setAction("UNDO", v -> {
        // Undo Action
        restoreItem();
    })
    .setActionTextColor(Color.YELLOW) // Action Text Color
    .show();

Custom Text Color & Background

Snackbar snackbar = Snackbar.make(rootView, "Custom colored Snackbar", Snackbar.LENGTH_INDEFINITE)
    .setAction("DISMISS", v -> snackbar.dismiss())
    .setActionTextColor(ContextCompat.getColor(this, R.color.accent))
    .setBackgroundTint(ContextCompat.getColor(this, R.color.primary_dark))
    .setTextColor(ContextCompat.getColor(this, R.color.white));

// หรือเข้าถึง View โดยตรง (Legacy)
View snackbarView = snackbar.getView();
TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text);
textView.setTextColor(Color.WHITE);
textView.setMaxLines(3); // Multiline

snackbar.show();

ตัวอย่างครบถ้วน: MainActivity.java

import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.snackbar.Snackbar;

public class MainActivity extends AppCompatActivity {
    private CoordinatorLayout coordinatorLayout;

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

        coordinatorLayout = findViewById(R.id.coordinatorLayout);

        Button btnSimple = findViewById(R.id.btnSimple);
        Button btnAction = findViewById(R.id.btnAction);
        Button btnCustom = findViewById(R.id.btnCustom);
        Button btnIndefinite = findViewById(R.id.btnIndefinite);

        btnSimple.setOnClickListener(v -> showSimpleSnackbar());
        btnAction.setOnClickListener(v -> showActionSnackbar());
        btnCustom.setOnClickListener(v -> showCustomSnackbar());
        btnIndefinite.setOnClickListener(v -> showIndefiniteSnackbar());
    }

    private void showSimpleSnackbar() {
        Snackbar.make(coordinatorLayout, "This is a simple Snackbar", Snackbar.LENGTH_SHORT)
            .show();
    }

    private void showActionSnackbar() {
        Snackbar.make(coordinatorLayout, "Message deleted", Snackbar.LENGTH_LONG)
            .setAction("UNDO", v -> {
                Toast.makeText(this, "Undo clicked!", Toast.LENGTH_SHORT).show();
            })
            .setActionTextColor(Color.YELLOW)
            .show();
    }

    private void showCustomSnackbar() {
        Snackbar snackbar = Snackbar.make(coordinatorLayout, "Custom colors and multi-line text\nSecond line here", Snackbar.LENGTH_LONG)
            .setBackgroundTint(ContextCompat.getColor(this, R.color.purple_700))
            .setTextColor(Color.WHITE)
            .setActionTextColor(Color.YELLOW)
            .setAction("ACTION", v -> {});

        // Multi-line support
        View view = snackbar.getView();
        TextView tv = view.findViewById(com.google.android.material.R.id.snackbar_text);
        tv.setMaxLines(3);
        
        snackbar.show();
    }

    private void showIndefiniteSnackbar() {
        Snackbar snackbar = Snackbar.make(coordinatorLayout, "Indefinite Snackbar - Swipe to dismiss", Snackbar.LENGTH_INDEFINITE)
            .setAction("DISMISS", v -> snackbar.dismiss());
        snackbar.show();
    }
}

Duration Constants

ConstantDurationUse Case
LENGTH_SHORT~1.5 วินาทีข้อความสั้น, Non-critical
LENGTH_LONG~2.75 วินาทีข้อความปกติ, มี Action
LENGTH_INDEFINITEไม่หายเองCritical, ต้องการ User Action (Swipe/Action)

Advanced Features

1. Callback (Lifecycle Events)

Snackbar.make(view, "Message", Snackbar.LENGTH_LONG)
    .addCallback(new Snackbar.Callback() {
        @Override
        public void onShown(Snackbar sb) {
            super.onShown(sb);
            // แสดงแล้ว
        }

        @Override
        public void onDismissed(Snackbar sb, int event) {
            super.onDismissed(sb, event);
            // Event: DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT, DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE
            if (event == DISMISS_EVENT_SWIPE) {
                // User swipe ทิ้ง
            }
        }
    })
    .show();

2. Anchor View (วางเหนือ View เฉพาะ)

// วาง Snackbar เหนือ FAB/Button แทน Bottom ของหน้าจอ
Snackbar.make(rootView, "Anchored to FAB", Snackbar.LENGTH_LONG)
    .setAnchorView(fab) // API 29+ / Material 1.2+
    .show();

3. Custom Layout (Material 1.5+ / API 30+)

// res/layout/custom_snackbar.xml


    

    

    

Snackbar vs Toast vs Dialog

FeatureToastSnackbarDialog
Action Button❌✅✅
Swipe to Dismiss❌✅❌ (Back)
Block UI❌❌✅ (Modal)
PositionFixed (Bottom/Center)Bottom (Above FAB/Nav)Center
Custom Layout✅ (Hard)✅ (Easy, API 30+)✅
Duration ControlShort/LongShort/Long/IndefiniteManual
Material DesignLegacyStandardStandard

Best Practices

  • ใช้ CoordinatorLayout เป็น Root เพื่อรองรับ FAB Animation และ Swipe Behavior
  • ไม่ Spam Snackbar - ระบบจะ Queue ไว้แสดงทีละอัน
  • Action Text ต้องชัดเจน - UNDO, RETRY, DISMISS, SETTINGS
  • Length_INDEFINITE ต้องมี Action หรือ Swipe to Dismiss
  • Accessibility: ใช้ setContentDescription สำหรับ Action
  • Theme: ใช้ Theme.Material3.* สำหรับ Style ใหม่

Troubleshooting

ปัญหาสาเหตุแก้ไข
Snackbar ไม่อยู่เหนือ FABParent ไม่ใช่ CoordinatorLayoutใช้ CoordinatorLayout หรือ setAnchorView
Snackbar ไม่อยู่เหนือ Navigation BarWindow Insetsandroid:fitsSystemWindows="true" บน Root
Text ตัดทอน / ไม่ MultilineDefault MaxLines = 2tv.setMaxLines(3) หรือ أكثر
Background Color ไม่เปลี่ยนMaterial 3 ใช้ Surface Containerใช้ setBackgroundTint + Theme ที่ถูกต้อง
Crash: No CoordinatorLayoutView ไม่ได้อยู่ใน CoordinatorLayout Hierarchyหา View ที่อยู่ใน CoordinatorLayout หรือใช้ FrameLayout

สรุป

  • Snackbar = Material Design Standard สำหรับ Brief Message + Action
  • ต้องการ Parent View (CoordinatorLayout แนะนำ)
  • ใช้ Snackbar.make(view, text, duration) + setAction()
  • Customizable: Color, Text, Layout (API 30+), Anchor, Callback
  • แทน Toast สำหรับกรณีที่ต้องการ User Action