toastutils报错_ToastUtils-我愿称之为最强

背景

在 Android 11 上谷歌对 Toast 进行了变更,导致 Toast#getView() 返回 null,具体可以查看文档 Android 11 中消息框的更新,对此,1.30.0 版本日志,其中就包括重构 ToastUtils,其重构后的 APIs 如下所示:

吐司相关 -> [ToastUtils.java][toast.java] -> [Demo][toast.demo]

make : 制作吐司

make.setMode : 设置模式

make.setGravity : 设置位置

make.setBgColor : 设置背景颜色

make.setBgResource : 设置背景资源

make.setTextColor : 设置字体颜色

make.setTextSize : 设置字体大小

make.setDurationIsLong : 设置是否长时间显示

make.setLeftIcon : 设置左侧图标

make.setTopIcon : 设置顶部图标

make.setRightIcon : 设置右侧图标

make.setBottomIcon : 设置底部图标

make.setNotUseSystemToast: 设置不使用系统吐司

make.show : 显示吐司

getDefaultMaker : 获取默认制作实例(控制 showShort、showLong 样式)1.30.1 新增

showShort : 显示短时吐司

showLong : 显示长时吐司

cancel : 取消吐司显示

使用

作为工具类,基本是看完 APIs 就知道该如何使用了,我这里就做下简单的介绍,想玩 Demo 的话可以在 1.30.0 版本日志 中下载。

// 短时间显示 toast

ToastUtils.showShort(xxx)

// 长时间显示 toast

ToastUtils.showLong(xxx)

// 长时间显示带左侧 icon 的暗黑模式 toast

ToastUtils.make().setLeftIcon(icon).setDurationIsLong(true).setMode(ToastUtils.MODE.DARK).show(xxx)

// 自定义 toast

private fun show(text: CharSequence, isLong: Boolean) {

val textView = ViewUtils.layoutId2View(R.layout.toast_custom) as TextView

textView.text = text

ToastUtils.make().setDurationIsLong(isLong).show(textView)

}

源码分析

系统 Toast 展示是需要有通知权限的(某些修改了 ROM 手机除外),所以如果不进行处理的话,关闭了通知权限就会导致 Toast 不显示的 BUG,针对不同版本及权限我们有不同的处理方案,下面来看下工具类中的相关代码:

// 最终 show 都会走到这个函数来处理

private static void show(@Nullable final View view, final CharSequence text, final int duration, final ToastUtils utils) {

UtilsBridge.runOnUiThread(new Runnable() {

@Override

public void run() {

cancel();

iToast = newToast(utils);

if (view != null) {

iToast.setToastView(view);

} else {

iToast.setToastView(text);

}

iToast.show(duration);

}

});

}

private static IToast newToast(ToastUtils toastUtils) {

if (!toastUtils.isNotUseSystemToast) {

if (NotificationManagerCompat.from(Utils.getApp()).areNotificationsEnabled()) {

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {

return new SystemToast(toastUtils);

}

if (!UtilsBridge.isGrantedDrawOverlays()) {

return new SystemToast(toastUtils);

}

}

}

// not use system or notification disable

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {

return new WindowManagerToast(toastUtils, WindowManager.LayoutParams.TYPE_TOAST);

} else if (UtilsBridge.isGrantedDrawOverlays()) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

new WindowManagerToast(toastUtils, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);

} else {

new WindowManagerToast(toastUtils, WindowManager.LayoutParams.TYPE_PHONE);

}

}

return new ActivityToast(toastUtils);

}

可以看到我们有 SystemToast、WindowManagerToast 和 ActivityToast 三种类型的 Toast,它们都继承自 AbsToast,其相关源码如下所示:

static abstract class AbsToast implements IToast {

protected Toast mToast;

protected ToastUtils mToastUtils;

protected View mToastView;

AbsToast(ToastUtils toastUtils) {

mToast = new Toast(Utils.getApp());

mToastUtils = toastUtils;

if (mToastUtils.mGravity != -1 || mToastUtils.mXOffset != -1 || mToastUtils.mYOffset != -1) {

mToast.setGravity(mToastUtils.mGravity, mToastUtils.mXOffset, mToastUtils.mYOffset);

}

}

@Override

public void setToastView(View view) {

mToastView = view;

mToast.setView(mToastView);

}

@Override

public void setToastView(CharSequence text) {

// 当设置了 mode 或者 设置了 icon 则会使用 utils_toast_view 布局

View utilsToastView = mToastUtils.tryApplyUtilsToastView(text);

if (utilsToastView != null) {

setToastView(utilsToastView);

return;

}

// API30 getView() 为空,则使用 utils_toast_view 布局来兼容

mToastView = mToast.getView();

if (mToastView == null || mToastView.findViewById(android.R.id.message) == null) {

setToastView(UtilsBridge.layoutId2View(R.layout.utils_toast_view));

}

TextView messageTv = mToastView.findViewById(android.R.id.message);

messageTv.setText(text);

if (mToastUtils.mTextColor != COLOR_DEFAULT) {

messageTv.setTextColor(mToastUtils.mTextColor);

}

if (mToastUtils.mTextSize != -1) {

messageTv.setTextSize(mToastUtils.mTextSize);

}

setBg(messageTv);

}

protected void setBg(final TextView msgTv) {

if (mToastUtils.mBgResource != -1) {

mToastView.setBackgroundResource(mToastUtils.mBgResource);

msgTv.setBackgroundColor(Color.TRANSPARENT);

} else if (mToastUtils.mBgColor != COLOR_DEFAULT) {

Drawable toastBg = mToastView.getBackground();

Drawable msgBg = msgTv.getBackground();

if (toastBg != null && msgBg != null) {

toastBg.mutate().setColorFilter(new PorterDuffColorFilter(mToastUtils.mBgColor, PorterDuff.Mode.SRC_IN));

msgTv.setBackgroundColor(Color.TRANSPARENT);

} else if (toastBg != null) {

toastBg.mutate().setColorFilter(new PorterDuffColorFilter(mToastUtils.mBgColor, PorterDuff.Mode.SRC_IN));

} else if (msgBg != null) {

msgBg.mutate().setColorFilter(new PorterDuffColorFilter(mToastUtils.mBgColor, PorterDuff.Mode.SRC_IN));

} else {

mToastView.setBackgroundColor(mToastUtils.mBgColor);

}

}

}

@Override

@CallSuper

public void cancel() {

if (mToast != null) {

mToast.cancel();

}

mToast = null;

mToastView = null;

}

}

interface IToast {

void setToastView(View view);

void setToastView(CharSequence text);

void show(int duration);

void cancel();

}

这部分代码比较简单,很多都是做兼容处理,我就不做解释了,下面我们依次来看 SystemToast、WindowManagerToast 和 ActivityToast。

```java

static final class SystemToast extends AbsToast {

SystemToast(ToastUtils toastUtils) {

super(toastUtils);

if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {

try {

//noinspection JavaReflectionMemberAccess

Field mTNField = Toast.class.getDeclaredField("mTN");

mTNField.setAccessible(true);

Object mTN = mTNField.get(mToast);

Field mTNmHandlerField = mTNField.getType().getDeclaredField("mHandler");

mTNmHandlerField.setAccessible(true);

Handler tnHandler = (Handler) mTNmHandlerField.get(mTN);

mTNmHandlerField.set(mTN, new SafeHandler(tnHandler));

} catch (Exception ignored) {}

}

}

@Override

public void show(int duration) {

if (mToast == null) return;

mToast.setDuration(duration);

mToast.show();

}

static class SafeHandler extends Handler {

private Handler impl;

SafeHandler(Handler impl) {

this.impl = impl;

}

@Override

public void handleMessage(@NonNull Message msg) {

impl.handleMessage(msg);

}

@Override

public void dispatchMessage(@NonNull Message msg) {

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值