From 5f4588666d66efb4c92daf5c0d18deffd852c312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Android=20=E8=BD=AE=E5=AD=90=E5=93=A5?= Date: Mon, 7 Nov 2022 23:04:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A1=86=E6=9E=B6=E5=B8=B8?= =?UTF-8?q?=E8=A7=81=E7=96=91=E9=97=AE=E6=96=87=E6=A1=A3=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E5=92=8C=E4=BC=98=E5=8C=96=E6=A1=86=E6=9E=B6=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=96=87=E6=A1=A3=20=E8=A1=A5=E5=85=85=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E9=95=BF=E7=9F=AD=20Toast=20=E6=96=B9=E6=B3=95=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A1=86=E6=9E=B6=E9=83=A8=E5=88=86=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=91=BD=E5=90=8D=E5=8F=8A=E6=B3=A8=E9=87=8A=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=20ToastUtils=20=E6=96=B9=E6=B3=95=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HelpDoc.md | 160 ++++++++++++++++++ README.md | 63 +++---- app/build.gradle | 10 +- .../java/com/hjq/toast/demo/MainActivity.java | 21 ++- app/src/main/res/layout/activity_main.xml | 36 +++- library/build.gradle | 4 +- .../main/java/com/hjq/toast/ToastParams.java | 21 ++- .../java/com/hjq/toast/ToastStrategy.java | 8 +- .../main/java/com/hjq/toast/ToastUtils.java | 104 ++++++++---- 9 files changed, 323 insertions(+), 104 deletions(-) create mode 100644 HelpDoc.md diff --git a/HelpDoc.md b/HelpDoc.md new file mode 100644 index 0000000..e5dfe6e --- /dev/null +++ b/HelpDoc.md @@ -0,0 +1,160 @@ +#### 目录 + +* [怎么自定义 Toast 显示动画](#怎么自定义-toast-显示动画) + +* [怎么自定义 Toast 显示时长](#怎么自定义-toast-显示时长) + +* [怎么自定义 Toast 布局样式](#怎么自定义-toast-布局样式) + +* [怎么切换成 Toast 排队显示的策略](#怎么切换成-toast-排队显示的策略) + +* [框架无法满足我当前使用的场景怎么办](#框架无法满足我当前使用的场景怎么办) + +* [为什么框架优先使用 WindowManager 来实现 Toast](#为什么框架优先使用-windowManager-来实现-toast) + +#### 怎么自定义 Toast 显示动画 + +* 在 Toast 初始化的时候,修改 Toast 策略即可 + +```java +ToastUtils.init(this, new ToastStrategy() { + + @Override + public IToast createToast(IToastStyle style) { + if (toast instanceof CustomToast) { + CustomToast customToast = ((CustomToast) toast); + // 设置 Toast 动画效果 + customToast.setAnimationsId(R.anim.xxx); + } + return toast; + } +}); +``` + +* 这种方式的缺点是只有应用在前台的情况下才会生效,这是因为前台的 Toast 是用框架实现的,本质上是一个 WindowManager,优点是非常灵活,不受系统 Toast 机制限制,缺点是无法在后台的情况下显示;而后台的 Toast 是用系统来实现的,优点是能在后台的情况下显示,缺点是局限性非常大,无法做太深的定制化;而框架正是利用了两种方式的优缺点进行了互补。 + +#### 怎么自定义 Toast 显示时长 + +* 在 Toast 初始化的时候,修改 Toast 策略即可 + +```java +ToastUtils.init(this, new ToastStrategy() { + + @Override + public IToast createToast(IToastStyle style) { + IToast toast = super.createToast(style); + if (toast instanceof CustomToast) { + CustomToast customToast = ((CustomToast) toast); + // 设置短 Toast 的显示时长(默认是 2000 毫秒) + customToast.setShortDuration(1000); + // 设置长 Toast 的显示时长(默认是 3500 毫秒) + customToast.setLongDuration(5000); + } + return toast; + } +}); +``` + +* 这种方式的缺点是只有应用在前台的情况下才会生效,这是因为前台的 Toast 是用框架实现的,本质上是一个 WindowManager,优点是非常灵活,不受系统 Toast 机制限制,缺点是无法在后台的情况下显示;而后台的 Toast 是用系统来实现的,优点是能在后台的情况下显示,缺点是局限性非常大,无法做太深的定制化;而框架正是利用了两种方式的优缺点进行了互补。 + +#### 怎么自定义 Toast 布局样式 + +* 如果你想设置全局的 Toast 样式,可以这样调用(选择任一一种即可) + +```java +// 修改 Toast 布局 +ToastUtils.setView(int id); +``` + +```java +// 修改 Toast 布局,Toast 显示重心,Toast 显示位置偏移 +ToastUtils.setStyle(IToastStyle style); +``` + +* 如果你想为某次 Toast 显示设置单独的样式,可以这样样用(选择任一一种即可) + +```java +// 修改 Toast 布局 +ToastParams params = new ToastParams(); +params.text = "我是自定义布局的 Toast(局部生效)"; +params.style = new CustomViewToastStyle(R.layout.toast_custom_view); +ToastUtils.show(params); +``` + +```java +// 修改 Toast 布局、Toast 显示重心、Toast 显示位置偏移 +ToastParams params = new ToastParams(); +params.text = "我是自定义布局的 Toast(局部生效)"; +params.style = new CustomViewToastStyle(R.layout.toast_custom_view, Gravity.CENTER, 10, 20); +ToastUtils.show(params); +``` + +#### 怎么切换成 Toast 排队显示的策略 + +* 只需要修改 Toast 框架的初始化方式,手动传入 Toast 策略类,这里使用框架已经封装好的 ToastStrategy 类即可, + +```java +// 初始化 Toast 框架 +// ToastUtils.init(this); +ToastUtils.init(this, new ToastStrategy(ToastStrategy.SHOW_STRATEGY_TYPE_QUEUE)); +``` + +* 注意构造函数需要传入 `ToastStrategy.SHOW_STRATEGY_TYPE_QUEUE`,关于这个字段的介绍可以看下面的代码注释 + +``` +public class ToastStrategy + + /** + * 即显即示模式(默认) + * + * 在发起多次 Toast 的显示请求情况下,显示下一个 Toast 之前 + * 会先立即取消上一个 Toast,保证当前显示 Toast 消息是最新的 + */ + public static final int SHOW_STRATEGY_TYPE_IMMEDIATELY = 0; + + /** + * 不丢消息模式 + * + * 在发起多次 Toast 的显示请求情况下,等待上一个 Toast 显示 1 秒或者 1.5 秒后 + * 然后再显示下一个 Toast,不按照 Toast 的显示时长来,因为那样等待时间会很长 + * 这样既能保证用户能看到每一条 Toast 消息,又能保证用户不会等得太久,速战速决 + */ + public static final int SHOW_STRATEGY_TYPE_QUEUE = 1; +} +``` + +#### 框架无法满足我当前使用的场景怎么办 + +* ToastUtils 框架意在解决一些的 Toast 需求,如果 ToastUtils 无法满足你的需求,你可以考虑使用 [XToast](https://github.com/getActivity/XToast) 悬浮窗框架来实现。 + +#### 为什么框架优先使用 WindowManager 来实现 Toast + +* 系统 Toast 的坑太多了,主要问题表现如下: + + * 系统 Toast 会引发一些内存泄漏的问题 + + * 系统 Toast 无法实现自定义显示动画、显示时长控制 + + * Android 7.1 版本会主线程阻塞会出现 BadTokenException 的问题 + + * Android 10.0 以下关闭通知栏权限会导致系统 Toast 显示不出来的问题 + + * Android 11 及以上版本,无法自定义 Toast 样式(布局、位置重心、位置偏移) + +* 所以框架优先使用 WindowManager 来实现 Toast 显示,具体优缺点以下: + + * 优点 + + * 不会出现内存泄漏,也不会有那么多奇奇怪怪的问题 + + * 可定制程度高,支持自定义动画和自定义显示时长 + + * 突破 Google 在新版本 Android 对 Toast 的一些限制 + + * 缺点 + + * WindowManager 无法在没有悬浮窗权限情况下在后台弹出
(框架的解决方案:如果是在后台的情况下显示,则使用系统的 Toast 来显示) + + * WindowManager 会和 Activity 绑定,会随 Activity 销毁而消失
(框架的解决方案:延迟 200 毫秒显示,由此等待最新的 Activity 创建出来才调用显示,这样 WindowManager 就和最新 Activity 绑定在一起,就不会出现和旧 Activity finish 时一起消失的问题) + +* 当然不是说用系统 Toast 就不好,用 WindowManger 一定就好,视具体的使用场景而定,我觉得最好的方式是:应用在前台的情况下使用 WindowManager 来显示,在后台的情况下使用系统 Toast 来显示,两者相结合,优势互补才是最佳方案。 diff --git a/README.md b/README.md index c45142d..b9524a9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ * 博客地址:[只需体验三分钟,你就会跟我一样,爱上这款 Toast](https://www.jianshu.com/p/9b174ee2c571) -* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/ToastUtils/releases/download/11.0/ToastUtils.apk) +* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/ToastUtils/releases/download/11.2/ToastUtils.apk) ![](picture/demo_code.png) @@ -47,7 +47,7 @@ android { dependencies { // 吐司框架:https://github.com/getActivity/ToastUtils - implementation 'com.github.getActivity:ToastUtils:11.0' + implementation 'com.github.getActivity:ToastUtils:11.2' } ``` @@ -72,23 +72,38 @@ public class XxxApplication extends Application { // 显示 Toast ToastUtils.show(CharSequence text); ToastUtils.show(int id); -ToastUtils.show(ToastParams params); +ToastUtils.show(Object object); // debug 模式下显示 Toast -ToastUtils.debugShow(int id); ToastUtils.debugShow(CharSequence text); +ToastUtils.debugShow(int id); +ToastUtils.debugShow(Object object); // 延迟显示 Toast -ToastUtils.delayedShow(int id, long delayMillis); ToastUtils.delayedShow(CharSequence text, long delayMillis); +ToastUtils.delayedShow(int id, long delayMillis); +ToastUtils.delayedShow(Object object, long delayMillis); + +// 显示短 Toast +ToastUtils.showShort(CharSequence text); +ToastUtils.showShort(int id); +ToastUtils.showShort(Object object); + +// 显示长 Toast +ToastUtils.showLong(CharSequence text); +ToastUtils.showLong(int id); +ToastUtils.showLong(Object object); + +// 自定义显示 Toast +ToastUtils.show(ToastParams params); // 取消 Toast ToastUtils.cancel(); -// 设置 Toast 布局 +// 设置 Toast 布局(全局生效) ToastUtils.setView(int id); -// 设置 Toast 布局样式 +// 设置 Toast 布局样式(全局生效) ToastUtils.setStyle(IToastStyle style); // 获取 Toast 布局样式 ToastUtils.getStyle() @@ -96,7 +111,7 @@ ToastUtils.getStyle() // 判断当前框架是否已经初始化 ToastUtils.isInit(); -// 设置 Toast 策略 +// 设置 Toast 策略(全局生效) ToastUtils.setStrategy(IToastStrategy strategy); // 获取 Toast 策略 ToastUtils.getStrategy(); @@ -105,43 +120,19 @@ ToastUtils.getStrategy(); ToastUtils.setGravity(int gravity); ToastUtils.setGravity(int gravity, int xOffset, int yOffset); -// 设置 Toast 拦截器 +// 设置 Toast 拦截器(全局生效) ToastUtils.setInterceptor(IToastInterceptor interceptor); // 获取 Toast 拦截器 ToastUtils.getInterceptor(); ``` -* 如果你需要对 Toast 的进行深度定制化,可以使用以下方式 - -```java -ToastUtils.init(this, new ToastStrategy() { - - @Override - public IToast createToast(Application application) { - IToast toast = super.createToast(application); - if (toast instanceof CustomToast) { - CustomToast customToast = ((CustomToast) toast); - // 设置 Toast 动画效果 - customToast.setAnimationsId(R.anim.xxx); - // 设置短 Toast 的显示时长(默认是 2000 毫秒) - customToast.setShortDuration(1000); - // 设置长 Toast 的显示时长(默认是 3500 毫秒) - customToast.setLongDuration(5000); - } - return toast; - } -}); -``` - -* 这种方式的缺点是只有应用在前台的情况下才会生效,这是因为前台的 Toast 是用框架实现的,本质上是一个 WindowManager,优点是非常灵活,不受原生 Toast 机制限制,缺点是无法在后台的情况下显示;而后台的 Toast 是用系统来实现的,优点是能在后台的情况下显示,缺点是局限性非常大,无法做太深的定制化;而框架正是利用了两种方式的优缺点进行了互补。 - -### 温馨提示:框架意在解决一些常规的 Toast 需求,如果是有一些特殊的定制化需求请配搭 [XToast](https://github.com/getActivity/XToast) 悬浮窗框架使用 +## [常见疑问请点击此处查看](HelpDoc.md) #### 不同 Toast 框架之间的对比 | 功能或细节 | [ToastUtils](https://github.com/getActivity/ToastUtils) | [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) | [Toasty](https://github.com/GrenderG/Toasty) | | :----: | :------: | :-----: | :-----: | -| 对应版本 | 11.0 | 1.30.6 | 1.5.0 | +| 对应版本 | 11.2 | 1.30.6 | 1.5.0 | | issues 数 | [![](https://img.shields.io/github/issues/getActivity/ToastUtils.svg)](https://github.com/getActivity/ToastUtils/issues) | [![](https://img.shields.io/github/issues/Blankj/AndroidUtilCode.svg)](https://github.com/Blankj/AndroidUtilCode/issues) | [![](https://img.shields.io/github/issues/GrenderG/Toasty.svg)](https://github.com/GrenderG/Toasty/issues) | | **aar 包大小** | 31 KB | 500 KB | 50 KB | | 框架维护状态 | **维护中** | 停止维护 | 停止维护 | @@ -151,7 +142,7 @@ ToastUtils.init(this, new ToastStrategy() { | 支持设置**全局** Toast 样式 | ✅ | ❌ | ❌ | | 支持 Toast **即显即示** | ✅ | ✅ | ❌ | | 支持 Toast **排队显示** | ✅ | ❌ | ✅ | -| 支持 Toast **延迟显示** | ✅ | ❌ | ❌ | +| 支持 Toast **延迟显示** | ✅ | ❌ | ❌ | | **处理 Toast 在 Android 7.1 崩溃的问题** | ✅ | ✅ | ❌ | | **兼容通知栏权限关闭后 Toast 显示不出来的问题** | ✅ | ✅ | ❌ | | **适配 Android 11 不能在后台显示 Toast 的问题** | ✅ | ❌ | ❌ | diff --git a/app/build.gradle b/app/build.gradle index e483d75..ed689d1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.hjq.toast.demo" minSdkVersion 16 targetSdkVersion 31 - versionCode 1100 - versionName "11.0" + versionCode 1120 + versionName "11.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -62,16 +62,14 @@ dependencies { implementation 'com.google.android.material:material:1.4.0' // 标题栏框架:https://github.com/getActivity/TitleBar - implementation 'com.github.getActivity:TitleBar:9.5' + implementation 'com.github.getActivity:TitleBar:9.6' // 权限请求框架:https://github.com/getActivity/XXPermissions implementation 'com.github.getActivity:XXPermissions:16.2' // 悬浮窗框架:https://github.com/getActivity/XToast - implementation 'com.github.getActivity:XToast:8.5' + implementation 'com.github.getActivity:XToast:8.6' // 内存泄漏捕捉:https://github.com/square/leakcanary debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' - - implementation 'com.tencent.mars:mars-xlog:1.2.6' } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/toast/demo/MainActivity.java b/app/src/main/java/com/hjq/toast/demo/MainActivity.java index c18ef5a..9421dcc 100644 --- a/app/src/main/java/com/hjq/toast/demo/MainActivity.java +++ b/app/src/main/java/com/hjq/toast/demo/MainActivity.java @@ -51,6 +51,14 @@ public void showToast(View v) { ToastUtils.show("我是普通的 Toast"); } + public void showShortToast(View v) { + ToastUtils.showShort("我是一个短 Toast"); + } + + public void showLongToast(View v) { + ToastUtils.showLong("我是一个长 Toast"); + } + public void showThriceToast(View v) { for (int i = 0; i < 3; i++) { ToastUtils.show("我是第 " + (i + 1) + " 个 Toast"); @@ -102,7 +110,14 @@ public void switchToastStrategy(View v) { } public void toBackgroundShowToast(View v) { - Snackbar.make(getWindow().getDecorView(), "温馨提示:安卓 10 在后台显示 Toast 需要有通知栏权限或者悬浮窗权限的情况下才可以显示", Snackbar.LENGTH_SHORT).show(); + Snackbar.make(getWindow().getDecorView(), "系好安全带,即将跳转到手机桌面", Snackbar.LENGTH_SHORT).show(); + + v.postDelayed(new Runnable() { + @Override + public void run() { + Snackbar.make(getWindow().getDecorView(), "温馨提示:安卓 10 在后台显示 Toast 需要有通知栏权限或者悬浮窗权限的情况下才可以显示", Snackbar.LENGTH_SHORT).show(); + } + }, 2000); v.postDelayed(new Runnable() { @Override @@ -111,7 +126,7 @@ public void run() { intent.addCategory(Intent.CATEGORY_HOME); startActivity(intent); } - }, 2000); + }, 4000); v.postDelayed(new Runnable() { @Override @@ -126,7 +141,7 @@ public void run() { ToastUtils.show("我是在后台显示的 Toast"); } } - }, 3000); + }, 5000); } public void combinationXToastShow(View v) { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8e0d888..998402e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -34,6 +34,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" + android:layout_marginTop="5dp" android:onClick="showToast" android:text="简简单单显示 Toast" /> @@ -41,7 +42,23 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:layout_marginTop="15dp" + android:layout_marginTop="10dp" + android:onClick="showShortToast" + android:text="显示一个短 Toast" /> + +