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
| Constant | Duration | Use 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
// Java
View customView = getLayoutInflater().inflate(R.layout.custom_snackbar, null);
Snackbar snackbar = Snackbar.make(rootView, "", Snackbar.LENGTH_INDEFINITE); // Text ว่าง
snackbar.setContentView(customView); // API 30+
Button actionBtn = customView.findViewById(R.id.action);
actionBtn.setOnClickListener(v -> {
// Retry logic
snackbar.dismiss();
});
snackbar.show();
Snackbar vs Toast vs Dialog
| Feature | Toast | Snackbar | Dialog |
| Action Button | ❌ | ✅ | ✅ |
| Swipe to Dismiss | ❌ | ✅ | ❌ (Back) |
| Block UI | ❌ | ❌ | ✅ (Modal) |
| Position | Fixed (Bottom/Center) | Bottom (Above FAB/Nav) | Center |
| Custom Layout | ✅ (Hard) | ✅ (Easy, API 30+) | ✅ |
| Duration Control | Short/Long | Short/Long/Indefinite | Manual |
| Material Design | Legacy | Standard | Standard |
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 ไม่อยู่เหนือ FAB | Parent ไม่ใช่ CoordinatorLayout | ใช้ CoordinatorLayout หรือ setAnchorView |
| Snackbar ไม่อยู่เหนือ Navigation Bar | Window Insets | android:fitsSystemWindows="true" บน Root |
| Text ตัดทอน / ไม่ Multiline | Default MaxLines = 2 | tv.setMaxLines(3) หรือ أكثر |
| Background Color ไม่เปลี่ยน | Material 3 ใช้ Surface Container | ใช้ setBackgroundTint + Theme ที่ถูกต้อง |
| Crash: No CoordinatorLayout | View ไม่ได้อยู่ใน 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