支持自定義布局:可以靈活地顯示自定義樣式的 Toast。
線程安全:確保在主線程中顯示 Toast,避免崩潰。
避免內存泄漏:使用 ApplicationContext 和取消機制,防止內存泄漏問題。
工具類:作為一個通用的工具類,方便在項目中復用。
ToastUtil
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;public class ToastUtil {private static Toast toast; // 全局Toast對象,避免重復創建private static final int DEFAULT_GRAVITY = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; // 默認顯示位置private static final int DEFAULT_Y_OFFSET = 100; // 默認Y軸偏移量private static final Handler mainHandler = new Handler(Looper.getMainLooper()); // 主線程Handler/*** 顯示短時間的Toast** @param context 上下文* @param message 要顯示的消息*/public static void showShort(Context context, String message) {showToast(context, message, Toast.LENGTH_SHORT, DEFAULT_GRAVITY, 0, DEFAULT_Y_OFFSET);}/*** 顯示長時間的Toast** @param context 上下文* @param message 要顯示的消息*/public static void showLong(Context context, String message) {showToast(context, message, Toast.LENGTH_LONG, DEFAULT_GRAVITY, 0, DEFAULT_Y_OFFSET);}/*** 顯示短時間的Toast(使用字符串資源ID)** @param context 上下文* @param resId 字符串資源ID*/public static void showShort(Context context, int resId) {showShort(context, context.getString(resId));}/*** 顯示長時間的Toast(使用字符串資源ID)** @param context 上下文* @param resId 字符串資源ID*/public static void showLong(Context context, int resId) {showLong(context, context.getString(resId));}/*** 顯示自定義位置的Toast** @param context 上下文* @param message 要顯示的消息* @param gravity 顯示位置(例如 Gravity.TOP)* @param xOffset X軸偏移量* @param yOffset Y軸偏移量*/public static void showAtPosition(Context context, String message, int gravity, int xOffset, int yOffset) {showToast(context, message, Toast.LENGTH_SHORT, gravity, xOffset, yOffset);}/*** 顯示自定義布局的Toast** @param context 上下文* @param layoutResId 自定義布局資源ID* @param message 要顯示的消息*/public static void showCustom(Context context, int layoutResId, String message) {runOnUiThread(() -> {if (toast != null) {toast.cancel(); // 取消之前的Toast}// 使用ApplicationContext,避免內存泄漏Context appContext = context.getApplicationContext();LayoutInflater inflater = (LayoutInflater) appContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);View layout = inflater.inflate(layoutResId, null);// 查找布局中的TextView(假設id為text)TextView textView = layout.findViewById(R.id.text);if (textView != null) {textView.setText(message);}toast = new Toast(appContext);toast.setDuration(Toast.LENGTH_SHORT);toast.setView(layout);toast.show();});}/*** 顯示自定義布局的Toast(支持自定義顯示時長)** @param context 上下文* @param layoutResId 自定義布局資源ID* @param message 要顯示的消息* @param duration 顯示時長(Toast.LENGTH_SHORT 或 Toast.LENGTH_LONG)*/public static void showCustom(Context context, int layoutResId, String message, int duration) {runOnUiThread(() -> {if (toast != null) {toast.cancel(); // 取消之前的Toast}// 使用ApplicationContext,避免內存泄漏Context appContext = context.getApplicationContext();LayoutInflater inflater = (LayoutInflater) appContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);View layout = inflater.inflate(layoutResId, null);// 查找布局中的TextView(假設id為text)TextView textView = layout.findViewById(R.id.text);if (textView != null) {textView.setText(message);}toast = new Toast(appContext);toast.setDuration(duration);toast.setView(layout);toast.show();});}/*** 核心方法:顯示Toast** @param context 上下文* @param message 要顯示的消息* @param duration 顯示時長(Toast.LENGTH_SHORT 或 Toast.LENGTH_LONG)* @param gravity 顯示位置* @param xOffset X軸偏移量* @param yOffset Y軸偏移量*/private static void showToast(Context context, String message, int duration, int gravity, int xOffset, int yOffset) {runOnUiThread(() -> {if (toast != null) {toast.cancel(); // 取消之前的Toast}// 使用ApplicationContext,避免內存泄漏Context appContext = context.getApplicationContext();toast = Toast.makeText(appContext, message, duration);toast.setGravity(gravity, xOffset, yOffset); // 設置顯示位置toast.show();});}/*** 取消Toast*/public static void cancelToast() {if (toast != null) {toast.cancel();toast = null; // 釋放引用}}/*** 確保在主線程中運行** @param runnable 需要執行的任務*/private static void runOnUiThread(Runnable runnable) {if (Looper.myLooper() == Looper.getMainLooper()) {runnable.run(); // 當前是主線程,直接運行} else {mainHandler.post(runnable); // 當前是子線程,切換到主線程運行}}
}
使用示例
- 顯示自定義布局的 Toast
ToastUtil.showCustom(MainActivity.this, R.layout.custom_toast, "這是一個自定義Toast");
在子線程中調用:
new Thread(() -> {// 在子線程中調用ToastUtil.showCustom(MainActivity.this, R.layout.custom_toast, "子線程中的自定義Toast");
}).start();
自定義布局示例:
假設 res/layout/custom_toast.xml 是一個自定義布局文件,例如:
<!-- res/layout/custom_toast.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/toast_background"android:padding="16dp"android:orientation="horizontal"><ImageViewandroid:id="@+id/icon"android:layout_width="24dp"android:layout_height="24dp"android:src="@drawable/ic_toast_icon"android:layout_marginEnd="8dp"/><TextViewandroid:id="@+id/text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@android:color/white"android:textSize="16sp"/>
</LinearLayout>