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()) 确保多线程安全
相关推荐
踢球的打工仔13 小时前
PHP面向对象(7)
android·开发语言·php
安卓理事人14 小时前
安卓socket
android
安卓理事人19 小时前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学21 小时前
Android M3U8视频播放器
android·音视频
q***577421 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober21 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿1 天前
关于ObjectAnimator
android
zhangphil1 天前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我1 天前
从头写一个自己的app
android·前端·flutter
lichong9511 天前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端