完整代码 ,下面是添加了自定义样式 Toast 的完整实现,兼容到 Android 16(API 16)
package com.nyw.mvvmmode.utils;
import android.content.Context;
import android.os.Build;
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 final class ToastManager {
public static final String MSG_ERROR = "系统错误,请稍候重试";
// 默认样式常量
public static final int STYLE_DEFAULT = 0;
public static final int STYLE_CUSTOM = 1;
private static volatile Toast sToast;
private static final Handler sHandler = new Handler(Looper.getMainLooper());
private static final Runnable sCancelRunnable = () -> {
if (sToast != null) {
sToast.cancel();
sToast = null;
}
};
private static boolean sCanShowMsg = true;
private static int sDefaultStyle = STYLE_DEFAULT; // 默认使用系统样式
private ToastManager() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
public static void setCanShowMsg(boolean canShowMsg) {
sCanShowMsg = canShowMsg;
}
/**
* 设置全局默认 Toast 样式
* @param style STYLE_DEFAULT 或 STYLE_CUSTOM
*/
public static void setDefaultStyle(int style) {
sDefaultStyle = style;
}
/**
* 显示 Toast(使用默认样式)
*/
public static void showToast(Context context, String text, int duration) {
showToast(context, text, duration, sDefaultStyle);
}
/**
* 显示 Toast(可指定样式)
* @param style 样式:STYLE_DEFAULT 或 STYLE_CUSTOM
*/
public static void showToast(Context context, String text, int duration, int style) {
if (!sCanShowMsg || text == null || text.isEmpty()) {
return;
}
// 防止内存泄漏,使用 Application Context
Context appContext = context.getApplicationContext();
sHandler.removeCallbacks(sCancelRunnable);
if (style == STYLE_CUSTOM) {
// 使用自定义样式
if (sToast == null) {
sToast = new Toast(appContext);
// 获取自定义布局
View view = LayoutInflater.from(appContext).inflate(R.layout.toast_custom, null);
TextView tvMessage = view.findViewById(R.id.toast_message);
tvMessage.setText(text);
sToast.setView(view);
sToast.setGravity(Gravity.CENTER, 0, 0);
sToast.setDuration(duration);
} else {
// 复用已有 Toast,更新文本
View view = sToast.getView();
if (view != null) {
TextView tvMessage = view.findViewById(R.id.toast_message);
if (tvMessage != null) {
tvMessage.setText(text);
}
}
}
} else {
// 使用系统样式
if (sToast != null) {
sToast.setText(text);
} else {
sToast = Toast.makeText(appContext, text, duration);
sToast.setGravity(Gravity.CENTER, 0, 0);
}
}
// 确保在主线程显示
sHandler.post(() -> sToast.show());
// 设置自动取消时间
long cancelDelay = (duration == Toast.LENGTH_LONG) ? 3500 : 2000;
sHandler.postDelayed(sCancelRunnable, cancelDelay);
}
/**
* 显示短时间 Toast(默认样式)
*/
public static void showShortToast(Context context, String text) {
showToast(context, text, Toast.LENGTH_SHORT);
}
/**
* 显示短时间 Toast(可指定样式)
*/
public static void showShortToast(Context context, String text, int style) {
showToast(context, text, Toast.LENGTH_SHORT, style);
}
/**
* 显示长时间 Toast(默认样式)
*/
public static void showLongToast(Context context, String text) {
showToast(context, text, Toast.LENGTH_LONG);
}
/**
* 显示长时间 Toast(可指定样式)
*/
public static void showLongToast(Context context, String text, int style) {
showToast(context, text, Toast.LENGTH_LONG, style);
}
/**
* 通过资源ID显示 Toast(默认样式)
*/
public static void showToast(Context context, int strId, int duration) {
try {
showToast(context, context.getString(strId), duration);
} catch (Exception e) {
// 资源不存在时的容错处理
showToast(context, String.valueOf(strId), duration);
}
}
/**
* 通过资源ID显示 Toast(可指定样式)
*/
public static void showToast(Context context, int strId, int duration, int style) {
try {
showToast(context, context.getString(strId), duration, style);
} catch (Exception e) {
// 资源不存在时的容错处理
showToast(context, String.valueOf(strId), duration, style);
}
}
/**
* 隐藏当前 Toast 并禁用显示
*/
public static void hideMsg() {
sHandler.removeCallbacks(sCancelRunnable);
if (sToast != null) {
sToast.cancel();
sToast = null;
}
sCanShowMsg = false;
}
}
自定义 Toast 布局
在 res/layout
目录下创建 toast_custom.xml
文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="@drawable/toast_bg"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:id="@+id/toast_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:textColor="#FFFFFF"
android:textSize="16sp" />
</LinearLayout>
自定义 Toast 背景
在 res/drawable
目录下创建 toast_bg.xml
文件:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#CC000000" /> <!-- 半透明黑色 -->
<corners android:radius="8dp" />
<padding
android:bottom="8dp"
android:left="12dp"
android:right="12dp"
android:top="8dp" />
</shape>
使用方法
显示系统样式 Toast
java
运行
ToastManager.showShortToast(context, "这是系统样式Toast");
显示自定义样式 Toast
java
运行
ToastManager.showShortToast(context, "这是自定义样式Toast", ToastManager.STYLE_CUSTOM);
设置全局默认使用自定义样式
java
运行
ToastManager.setDefaultStyle(ToastManager.STYLE_CUSTOM);
// 之后调用不带样式参数的方法都会使用自定义样式
ToastManager.showShortToast(context, "全局默认自定义样式");
兼容性说明
- Android 16+ 完全兼容:通过标准 Toast API 实现,不使用任何已弃用或高版本特性
- 自定义样式稳定性 :使用
setView()
方法设置自定义布局,这是官方推荐的自定义 Toast 方式 - 内存安全 :所有方法都使用
Application Context
避免内存泄漏 - 线程安全 :使用
volatile
关键字和Handler(Looper.getMainLooper())
确保多线程安全