Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Tích hợp Snackbar trong Android

Snackbar là component Material Design hiển thị thông báo nhẹ ở dưới màn hình, có thể chứa action (Undo, Retry, Dismiss...). Khác với Toast: Snackbar gắn với View hierarchy, có action, swipe để dismiss, animation mượt mà.

Dependency

dependencies {
    // Material Components (đã có Snackbar)
    implementation 'com.google.android.material:material:1.11.0'
    // Hoặc legacy support library
    // implementation 'com.android.support:design:28.0.0'
}

Yêu cầu: CoordinatorLayout

Snackbar cần CoordinatorLayout làm parent để hiển thị đúng vị trí, tránh che FAB, xử lý swipe dismiss, layout_anchor.

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: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_margin="16dp"
        android:contentDescription="FAB"
        android:src="@drawable/ic_add"
        app:layout_anchor="@id/bottomView"
        app:layout_anchorGravity="bottom|right|end" />

    <LinearLayout
        android:id="@+id/bottomView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:orientation="vertical"
        android:padding="16dp">

        <Button
            android:id="@+id/btnSimpleSnackbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Snackbar đơn giản" />

        <Button
            android:id="@+id/btnActionSnackbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="Snackbar có Action (Undo)" />

        <Button
            android:id="@+id/btnCustomSnackbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="Snackbar tùy chỉnh màu/chữ" />
    </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

MainActivity.java - Các loại Snackbar

import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
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);
        
        findViewById(R.id.btnSimpleSnackbar).setOnClickListener(v -> showSimpleSnackbar());
        findViewById(R.id.btnActionSnackbar).setOnClickListener(v -> showActionSnackbar());
        findViewById(R.id.btnCustomSnackbar).setOnClickListener(v -> showCustomSnackbar());
    }

    // 1. Snackbar đơn giản
    private void showSimpleSnackbar() {
        Snackbar.make(coordinatorLayout, "Đã lưu thay đổi", Snackbar.LENGTH_LONG)
            .show();
    }

    // 2. Snackbar với Action (Undo, Retry...)
    private void showActionSnackbar() {
        Snackbar.make(coordinatorLayout, "Mục đã bị xóa", Snackbar.LENGTH_LONG)
            .setAction("Hoàn tác", v -> {
                Toast.makeText(this, "Đã hoàn tác xóa", Toast.LENGTH_SHORT).show();
                // Thực hiện undo logic ở đây
            })
            .setActionTextColor(Color.YELLOW) // Màu chữ action
            .addCallback(new Snackbar.Callback() {
                @Override
                public void onDismissed(Snackbar transientBottomBar, int event) {
                    super.onDismissed(transientBottomBar, event);
                    // event: DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT, DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE
                }
                @Override
                public void onShown(Snackbar sb) {
                    super.onShown(sb);
                }
            })
            .show();
    }

    // 3. Snackbar tùy chỉnh màu, text, duration
    private void showCustomSnackbar() {
        Snackbar snackbar = Snackbar.make(coordinatorLayout, "Kết nối mạng thất bại", Snackbar.LENGTH_INDEFINITE)
            .setAction("Thử lại", v -> {
                // Retry logic
                Toast.makeText(this, "Đang thử lại...", Toast.LENGTH_SHORT).show();
            })
            .setActionTextColor(getColor(R.color.design_default_color_error)) // Màu đỏ
            .setTextColor(Color.WHITE) // Màu text chính
            .setBackgroundTint(Color.parseColor("#FF5722")) // Màu background (Material 1.2+)
            // .setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#FF5722"))) // Old API
            .setDuration(5000) // Custom duration (ms)
            .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE); // Slide/Fade

        // Custom view (nâng cao)
        View snackbarView = snackbar.getView();
        TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text);
        textView.setMaxLines(3); // Cho phép multi-line
        textView.setTextSize(15);

        snackbar.show();
    }
}

Snackbar vs Toast

Đặc điểmSnackbarToast
Vị tríDưới cùng, trên FAB/NavigationDưới cùng, fixed
Action buttonCó (setAction)Không
Swipe dismissCóKhông
AnimationSlide up/down mượtFade in/out đơn giản
Custom layoutKhó (chỉ màu/text)Dễ (setView)
Context cầnView (CoordinatorLayout)Context bất kỳ
BlockingKhông block touch khácKhông block

Các Duration có sẵn

  • Snackbar.LENGTH_SHORT ~ 1500ms
  • Snackbar.LENGTH_LONG ~ 2700ms
  • Snackbar.LENGTH_INDEFINITE Vô thời hạn (cần action hoặc swipe dismiss)
  • setDuration(int ms) Tùy chỉnh (API 1.2+)

Snackbar trong Fragment

// Cần view root của Fragment (thường là view trả về trong onCreateView)
View rootView = getView(); // hoặc binding.getRoot()
Snackbar.make(rootView, "Message", Snackbar.LENGTH_SHORT).show();

// Hoặc dùng requireActivity() tìm CoordinatorLayout
CoordinatorLayout coordinator = requireActivity().findViewById(R.id.coordinatorLayout);
Snackbar.make(coordinator, "Message", Snackbar.LENGTH_SHORT).show();

Snackbar với Custom Layout (Nâng cao)

Snackbar không hỗ trợ setView như Toast. Để custom hoàn toàn, dùng BottomSheetDialogFragment hoặc library FancyToast, SnackbarUtils.

Chạy ứng dụng

Nhấn các nút → Snackbar xuất hiện từ dưới lên, không che FAB (nhờ CoordinatorLayout). Snackbar có action hiển thị nút "Hoàn tác"/"Thử lại" màu vàng/đỏ. Swipe sang trái/phải để dismiss sớm.

Lưu ý

  • Phải có CoordinatorLayout trong hierarchy (thường là root layout)
  • Snackbar.make(view, ...) - view dùng để tìm CoordinatorLayout ancestor
  • Tránh show nhiều Snackbar cùng lúc - sẽ queue tự động
  • setBackgroundTint cần Material 1.2+, cũ dùng setBackgroundTintList
  • Text color/action color nên contrast với background