支持自定义布局:可以灵活地显示自定义样式的 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">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_toast_icon"
android:layout_marginEnd="8dp"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="16sp"/>
</LinearLayout>