1. 程式人生 > >只需體驗三分鐘,你就會跟我一樣,愛上這款Toast

只需體驗三分鐘,你就會跟我一樣,愛上這款Toast

這可能是效能最優、使用最簡單,支援自定義,不需要通知欄許可權的吐司

已投入公司專案多時,沒有任何毛病,可勝任任何需求,點選此處下載Demo

想了解實現原理的可以點選此連結檢視:ToastUtils 原始碼

整合步驟

dependencies {
    implementation 'com.hjq:toast:3.0'
}

初始化Toast

// 在Application中初始化
ToastUtils.init(this);

顯示Toast

ToastUtils.show("我是吐司");

獲取Toast物件

ToastUtils.getToast();

設定Toast佈局

ToastUtils.setView();

自定義Toast樣式

如果對Toast的預設樣式不滿意,可以在Application初始化樣式,具體可參考ToastBlackStyle類的實現

ToastUtils.initStyle(new IToastStyle());

框架亮點

  • 無需許可權:不管有沒有授予通知欄許可權都不影響吐司的彈出

  • 功能強大:不分主次執行緒都可以彈出Toast,自動區分資源id和int型別

  • 使用簡單:只需傳入文字,會自動根據文字長度決定吐司顯示的時長

  • 效能最佳:單例吐司,整個Toast只有一個TextView,並且通過程式碼建立

  • 體驗最優:限制Toast短時間內彈出的次數,避免頻繁彈出造成不良的使用者體驗

  • 支援多種樣式:預設為黑色樣式,夜間模式可使用白色樣式,還有仿QQ吐司樣式

  • 支援自定義樣式:吐司(背景、圓角、重心、偏移),文字(大小、顏色、邊距)

  • 支援自定義擴充套件:支援獲取ToastUtils中的Toast物件,支援重新自定義Toast佈局

  • 支援全域性配置樣式:可以在Application中初始化Toast樣式,達到一勞永逸的效果

  • 框架相容性良好:本框架不依賴任何第三方庫,支援Eclipse和Studio的整合使用

關於通知欄許可權

本框架已經完美解決這個問題

原博主

這就是原博主的那個工具類,寫的還是很不錯的,可以直接使用 

package com.hjq.toast;

import android.app.AppOpsManager;
import android.app.Application;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.hjq.toast.style.ToastBlackStyle;
import com.hjq.toast.style.ToastQQStyle;
import com.hjq.toast.style.ToastWhiteStyle;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *    author : HJQ
 *    github : https://github.com/getActivity/ToastUtils
 *    time   : 2018/09/01
 *    desc   : Toast工具類
 */
public final class ToastUtils {

    private static IToastStyle sDefaultStyle;

    private static Toast sToast;

    /**
     * 初始化ToastUtils,建議在Application中初始化
     *
     * @param application       應用的上下文
     */
    public static void init(Application application) {
        // 檢查預設樣式是否為空,如果是就建立一個預設樣式
        if (sDefaultStyle == null) {
            sDefaultStyle = new ToastBlackStyle();
        }

        // 判斷有沒有通知欄許可權
        if (isNotificationEnabled(application)) {
            sToast = new XToast(application);
        }else {
            sToast = new SupportToast(application);
        }

        sToast.setGravity(sDefaultStyle.getGravity(), sDefaultStyle.getXOffset(), sDefaultStyle.getYOffset());
        sToast.setView(createTextView(application));
    }

    /**
     * 顯示一個物件的吐司
     *
     * @param object      物件
     */
    public static void show(Object object) {
        show(object != null ? object.toString() : "null");
    }

    /**
     * 顯示一個吐司
     *
     * @param id      如果傳入的是正確的string id就顯示對應字串
     *                如果不是則顯示一個整數的string
     */
    public static void show(int id) {

        checkToastState();

        try {
            // 如果這是一個資源id
            show(sToast.getView().getContext().getResources().getText(id));
        } catch (Resources.NotFoundException ignored) {
            // 如果這是一個int型別
            show(String.valueOf(id));
        }
    }

    /**
     * 顯示一個吐司
     *
     * @param text      需要顯示的文字
     */
    public static void show(CharSequence text) {

        checkToastState();

        if (text == null || text.equals("")) return;

       // 如果顯示的文字超過了10個就顯示長吐司,否則顯示短吐司
        if (text.length() > 20) {
            sToast.setDuration(Toast.LENGTH_LONG);
        } else {
            sToast.setDuration(Toast.LENGTH_SHORT);
        }

        sToast.setText(text);
        sToast.show();
    }

    /**
     * 取消吐司的顯示
     */
    public void cancel() {
        checkToastState();
        sToast.cancel();
    }

    /**
     * 獲取當前Toast物件
     */
    public static Toast getToast() {
        return sToast;
    }

    /**
     * 給當前Toast設定新的佈局,具體實現可看{@link XToast#setView(View)}
     */
    public static void setView(Context context, int layoutId) {
        if (context != context.getApplicationContext()) {
            context = context.getApplicationContext();
        }
        setView(View.inflate(context, layoutId, null));
    }

    public static void setView(View view) {

        checkToastState();

        if (view == null) {
            throw new IllegalArgumentException("Views cannot be empty");
        }

        // 如果吐司已經建立,就重新初始化吐司
        if (sToast != null) {
            //取消原有吐司的顯示
            sToast.cancel();
            sToast.setView(view);
        }
    }

    /**
     * 統一全域性的Toast樣式,建議在{@link android.app.Application#onCreate()}中初始化
     *
     * @param style         樣式實現類,框架已經實現三種不同的樣式
     *                      黑色樣式:{@link ToastBlackStyle}
     *                      白色樣式:{@link ToastWhiteStyle}
     *                      仿QQ樣式:{@link ToastQQStyle}
     */
    public static void initStyle(IToastStyle style) {
        ToastUtils.sDefaultStyle = style;
        // 如果吐司已經建立,就重新初始化吐司
        if (sToast != null) {
            //取消原有吐司的顯示
            sToast.cancel();
            sToast.setView(createTextView(sToast.getView().getContext().getApplicationContext()));
        }
    }

    /**
     * 檢查吐司狀態,如果未初始化請先呼叫{@link ToastUtils#init(Application)}
     */
    private static void checkToastState() {
        //吐司工具類還沒有被初始化,必須要先呼叫init方法進行初始化
        if (sToast == null) {
            throw new IllegalStateException("ToastUtils has not been initialized");
        }
    }

    /**
     * 生成預設的 TextView 物件
     */
    private static TextView createTextView(Context context) {

        GradientDrawable drawable = new GradientDrawable();
        drawable.setColor(sDefaultStyle.getBackgroundColor()); // 設定背景色
        drawable.setCornerRadius(dp2px(context, sDefaultStyle.getCornerRadius())); // 設定圓角

        TextView textView = new TextView(context);
        textView.setId(R.id.toast_main_text_view_id);
        textView.setTextColor(sDefaultStyle.getTextColor());
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, sp2px(context, sDefaultStyle.getTextSize()));
        textView.setPadding(dp2px(context, sDefaultStyle.getPaddingLeft()), dp2px(context, sDefaultStyle.getPaddingTop()),
                dp2px(context, sDefaultStyle.getPaddingRight()), dp2px(context, sDefaultStyle.getPaddingBottom()));
        textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        // setBackground API版本相容
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            textView.setBackground(drawable);
        }else {
            textView.setBackgroundDrawable(drawable);
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            textView.setZ(sDefaultStyle.getZ()); // 設定 Z 軸陰影
        }

        if (sDefaultStyle.getMaxLines() > 0) {
            textView.setMaxLines(sDefaultStyle.getMaxLines()); // 設定最大顯示行數
        }

        return textView;
    }

    /**
     * dp轉px
     *
     * @param context       上下文
     * @param dpValue       dp值
     * @return              px值
     */
    private static int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * sp轉px
     *
     * @param context       上下文
     * @param spValue       sp值
     * @return              px值
     */
    private static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    /**
     * 檢查通知欄許可權有沒有開啟
     * 參考SupportCompat包中的: NotificationManagerCompat.from(context).areNotificationsEnabled();
     */
    public static boolean isNotificationEnabled(Context context){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).areNotificationsEnabled();
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            ApplicationInfo appInfo = context.getApplicationInfo();
            String pkg = context.getApplicationContext().getPackageName();
            int uid = appInfo.uid;

            try {
                Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
                Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class);
                Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION");
                int value = (Integer) opPostNotificationValue.get(Integer.class);
                return (Integer) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) == 0;
            } catch (NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException | RuntimeException | ClassNotFoundException ignored) {
                return true;
            }
        } else {
            return true;
        }
    }
}