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

สร้าง Custom Toast Message ใน Android


Toast และ Custom Toast

Toast คือข้อความแจ้งเตือนเล็กน้อย แสดงชั่วคราวแล้วหายเอง Custom Toast คือการปรับแต่ง Layout, สี, ฟอนต์, ไอคอน, Animation ให้ตรงกับ Brand/Design System ของแอป

วิธีสร้าง Custom Toast แบบ XML Layout

1. สร้าง Layout: res/layout/custom_toast.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:id="@+id/custom_toast_container"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/toast_background"
    android:orientation="horizontal"
    android:padding="12dp"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/toast_icon"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginEnd="12dp"
        android:src="@drawable/ic_check_circle"
        android:contentDescription="@string/toast_success_icon" />

    <TextView
        android:id="@+id/toast_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Custom Toast Message"
        android:textColor="@android:color/white"
        android:textSize="14sp"
        android:maxLines="2"
        android:ellipsize="end" />

</LinearLayout>

2. สร้าง Background Drawable: res/drawable/toast_background.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="https://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#323232" /> <!-- Dark Gray -->
    <corners android:radius="12dp" />
    <padding
        android:left="16dp"
        android:top="12dp"
        android:right="16dp"
        android:bottom="12dp" />
    <!-- Optional: Shadow (API 28+) -->
    <!-- 需要在 Layer List 中配合 elevation 使用 -->
</shape>

3. Helper Class: CustomToast.java

import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class CustomToast {
    public static final int TYPE_SUCCESS = 0;
    public static final int TYPE_ERROR = 1;
    public static final int TYPE_WARNING = 2;
    public static final int TYPE_INFO = 3;

    public static void show(Context context, String message, int type) {
        show(context, message, type, Toast.LENGTH_SHORT);
    }

    public static void show(Context context, String message, int type, int duration) {
        // Inflate layout
        LayoutInflater inflater = LayoutInflater.from(context);
        View layout = inflater.inflate(R.layout.custom_toast, null);

        // Setup views
        ImageView icon = layout.findViewById(R.id.toast_icon);
        TextView text = layout.findViewById(R.id.toast_message);
        text.setText(message);

        // Set icon & color by type
        switch (type) {
            case TYPE_SUCCESS:
                icon.setImageResource(R.drawable.ic_check_circle);
                layout.setBackgroundResource(R.drawable.toast_bg_success); // Green tint
                break;
            case TYPE_ERROR:
                icon.setImageResource(R.drawable.ic_error);
                layout.setBackgroundResource(R.drawable.toast_bg_error); // Red tint
                break;
            case TYPE_WARNING:
                icon.setImageResource(R.drawable.ic_warning);
                layout.setBackgroundResource(R.drawable.toast_bg_warning); // Orange tint
                break;
            case TYPE_INFO:
            default:
                icon.setImageResource(R.drawable.ic_info);
                layout.setBackgroundResource(R.drawable.toast_bg_info); // Blue tint
                break;
        }

        // Create and show Toast
        Toast toast = new Toast(context);
        toast.setDuration(duration);
        toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 100); // Offset from bottom
        toast.setView(layout);
        toast.show();
    }

    // Convenience methods
    public static void success(Context context, String msg) { show(context, msg, TYPE_SUCCESS); }
    public static void error(Context context, String msg) { show(context, msg, TYPE_ERROR); }
    public static void warning(Context context, String msg) { show(context, msg, TYPE_WARNING); }
    public static void info(Context context, String msg) { show(context, msg, TYPE_INFO); }
}

4. ใช้งานใน Activity/Fragment

// เรียกใช้ง่ายๆ
CustomToast.success(this, "บันทึกข้อมูลสำเร็จ!");
CustomToast.error(this, "เกิดข้อผิดพลาด: ไม่สามารถเชื่อมต่อเซิร์ฟเวอร์");
CustomToast.warning(this, "กรุณากรอกข้อมูลให้ครบถ้วน");
CustomToast.info(this, "มีการอัปเดตใหม่");

// หรือใช้แบบกำหนด Duration เอง
CustomToast.show(this, "ข้อความยาวขึ้น...", CustomToast.TYPE_INFO, Toast.LENGTH_LONG);

วิธีทันสมัย: ใช้ Snackbar (แนะนำ)

Material Design แนะนำให้ใช้ Snackbar** แทน Toast เพราะ:

  • มี Action Button ได้ (Undo, Retry, Dismiss)
  • อยู่เหนือ UI อื่นๆ (ไม่บังเนื้อหา)
  • Animation สวยงาม ทันสมัย
  • รองรับ Swipe to Dismiss

ตัวอย่าง Snackbar

import com.google.android.material.snackbar.Snackbar;
import androidx.core.content.ContextCompat;

// ใน Activity/Fragment (ต้องมี CoordinatorLayout หรือ ViewGroup เป็น Root)
View rootView = findViewById(android.R.id.content); // หรือ CoordinatorLayout

Snackbar snackbar = Snackbar.make(rootView, "ข้อความแจ้งเตือน", Snackbar.LENGTH_LONG)
    .setAction("UNDO", v -> { /* ทำ Undo */ })
    .setActionTextColor(ContextCompat.getColor(this, R.color.accent))
    .setBackgroundTint(ContextCompat.getColor(this, R.color.surface))
    .setTextColor(ContextCompat.getColor(this, R.color.onSurface));

// Custom View (Advanced)
Snackbar.make(rootView, "", Snackbar.LENGTH_INDEFINITE)
    .setContentView(R.layout.custom_snackbar_layout) // API 30+ / Material 1.5+
    .show();

snackbar.show();

Toast แบบไม่ใช้ Layout XML (Programmatic)

public static void showSimpleToast(Context context, String message, int backgroundColor, int textColor) {
    Toast toast = new Toast(context);
    TextView textView = new TextView(context);
    textView.setText(message);
    textView.setTextColor(textColor);
    textView.setTextSize(14);
    textView.setPadding(24, 16, 24, 16);
    textView.setBackgroundColor(backgroundColor);
    // Rounded corners ต้องใช้ Background Drawable แทน
    toast.setView(textView);
    toast.setDuration(Toast.LENGTH_SHORT);
    toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 100);
    toast.show();
}

ข้อจำกัดและข้อควรระวัง

  • Position: Toast ตำแหน่งคงที่ (Top/Center/Bottom) ไม่สามารถวางได้ตามต้องการแบบ Pixel Perfect
  • Queue: Toast หลายตัวจะคิวไว้แสดงทีละอัน (ไม่ทับกัน)
  • System Overlay: Android 12+ จำกัด Toast จาก Background Service/Receiver
  • Custom View Limitation: ไม่รองรับ Interactive View (Button, EditText) ภายใน Toast
  • Memory: Inflate Layout ทุกครั้งอาจช้า → Cache View หรือใช้ Singleton Pattern

Library แนะนำ (Modern Alternatives)

  • Toasty - Gionee/Toasty: `Toasty.success(context, "Success").show();`
  • Crouton - Rich Toast (Deprecated)
  • SuperToasts - Feature-rich
  • Snackbar (Material) - Built-in, Best Practice

สรุป

  • ใช้ Custom Toast Layout + Helper Class สำหรับโปรเจกต์ที่ยังใช้ Toast
  • ย้ายไปใช้ Snackbar สำหรับโปรเจกต์ใหม่ (Material Design Standard)
  • หลีกเลี่ยง Toast ใน Background/Service (Android 12+ Block)
  • พิจารณา Library Toasty หากต้องการความสะดวกและสวยงามโดยไม่อยากเขียนเอง