android 自定义样式 Toast 实现(兼容 Android 4.1+~Android 16(API 16))

完整代码 ,下面是添加了自定义样式 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()) 确保多线程安全
相关推荐
一直向钱2 小时前
android SharedPreferences 工具类 * 兼容 Android 16+ (API 16)
android
2501_915909063 小时前
App Store 上架完整流程解析,iOS 应用发布步骤、ipa 文件上传工具、TestFlight 测试与苹果审核经验
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_916008893 小时前
iOS 26 全景揭秘,新界面、功能创新、兼容挑战与各种工具在新版系统中的定位
android·macos·ios·小程序·uni-app·cocoa·iphone
小趴菜822712 小时前
安卓接入Kwai广告源
android·kotlin
2501_9160137412 小时前
iOS 混淆与 App Store 审核兼容性 避免被拒的策略与实战流程(iOS 混淆、ipa 加固、上架合规)
android·ios·小程序·https·uni-app·iphone·webview
程序员江同学13 小时前
Kotlin 技术月报 | 2025 年 9 月
android·kotlin
码农的小菜园14 小时前
探究ContentProvider(一)
android
时光少年16 小时前
Compose AnnotatedString实现Html样式解析
android·前端
hnlgzb16 小时前
安卓中,kotlin如何写app界面?
android·开发语言·kotlin