├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── issue_en_template_bug.yml │ ├── issue_en_template_question.yml │ ├── issue_en_template_suggest.yml │ ├── issue_zh_template_bug.yml │ ├── issue_zh_template_question.yml │ └── issue_zh_template_suggest.yml └── workflows │ └── android.yml ├── .gitignore ├── HelpDoc-en.md ├── HelpDoc-zh.md ├── LICENSE ├── README-en.md ├── README.md ├── app ├── AppSignature.jks ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── hjq │ │ └── toast │ │ └── demo │ │ ├── MainActivity.java │ │ └── ToastApplication.java │ └── res │ ├── drawable │ ├── shape_gradient.xml │ ├── toast_error_bg.xml │ ├── toast_error_ic.xml │ ├── toast_hint_bg.xml │ ├── toast_info_ic.xml │ ├── toast_success_bg.xml │ ├── toast_success_ic.xml │ ├── toast_warn_bg.xml │ └── toast_warn_ic.xml │ ├── layout │ ├── activity_main.xml │ ├── toast_custom_view.xml │ ├── toast_error.xml │ ├── toast_info.xml │ ├── toast_success.xml │ └── toast_warn.xml │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v23 │ └── styles.xml │ ├── values-zh │ └── strings.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── hjq │ └── toast │ ├── ActivityStack.java │ ├── ActivityToast.java │ ├── CustomToast.java │ ├── GlobalToast.java │ ├── NotificationServiceProxy.java │ ├── NotificationToast.java │ ├── SafeHandler.java │ ├── SafeToast.java │ ├── SystemToast.java │ ├── ToastImpl.java │ ├── ToastLogInterceptor.java │ ├── ToastParams.java │ ├── ToastStrategy.java │ ├── Toaster.java │ ├── WindowLifecycle.java │ ├── config │ ├── IToast.java │ ├── IToastInterceptor.java │ ├── IToastStrategy.java │ └── IToastStyle.java │ └── style │ ├── BlackToastStyle.java │ ├── CustomToastStyle.java │ ├── LocationToastStyle.java │ └── WhiteToastStyle.java ├── logo.png ├── picture ├── en │ ├── demo_logcat_code.jpg │ ├── demo_toast_activity.jpg │ ├── demo_toast_layout_custom.jpg │ ├── demo_toast_layout_error.jpg │ ├── demo_toast_layout_info.jpg │ ├── demo_toast_layout_success.jpg │ ├── demo_toast_layout_warn.jpg │ ├── demo_toast_style_black.jpg │ └── demo_toast_style_white.jpg └── zh │ ├── demo_logcat_code.jpg │ ├── demo_toast_activity.jpg │ ├── demo_toast_layout_custom.jpg │ ├── demo_toast_layout_error.jpg │ ├── demo_toast_layout_info.jpg │ ├── demo_toast_layout_success.jpg │ ├── demo_toast_layout_warn.jpg │ ├── demo_toast_style_black.jpg │ ├── demo_toast_style_white.jpg │ ├── download_demo_apk_qr_code.png │ └── help_doc_rename_vote.jpg └── settings.gradle /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_ali.png 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_en_template_bug.yml: -------------------------------------------------------------------------------- 1 | name: Submit Bug 2 | description: Please let me know the issues with the framework, and I will assist you in resolving them! 3 | title: "[Bug]:" 4 | labels: ["bug"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## [Warning: Please make sure to fill in the issue template accurately. If an issue is found to be filled incorrectly, it will be closed without further notice.](https://github.com/getActivity/IssueTemplateGuide) 11 | - type: input 12 | id: input_id_1 13 | attributes: 14 | label: Framework Version [Required] 15 | description: Please enter the version of the framework you are using. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: input_id_2 20 | attributes: 21 | label: Issue Description [Required] 22 | description: Please describe the issue you are facing. 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: input_id_3 27 | attributes: 28 | label: Steps to Reproduce [Required] 29 | description: Please provide steps to reproduce the issue. 30 | validations: 31 | required: true 32 | - type: dropdown 33 | id: input_id_4 34 | attributes: 35 | label: Is the Issue Reproducible? [Required] 36 | multiple: false 37 | options: 38 | - "Not Selected" 39 | - "Yes" 40 | - "No" 41 | validations: 42 | required: true 43 | - type: input 44 | id: input_id_5 45 | attributes: 46 | label: Project targetSdkVersion [Required] 47 | validations: 48 | required: true 49 | - type: input 50 | id: input_id_6 51 | attributes: 52 | label: Device Information [Required] 53 | description: Please provide the brand and model of the device where the issue occurred. 54 | validations: 55 | required: true 56 | - type: input 57 | id: input_id_7 58 | attributes: 59 | label: Android Version [Required] 60 | description: Please provide the Android version where the issue occurred. 61 | validations: 62 | required: true 63 | - type: dropdown 64 | id: input_id_8 65 | attributes: 66 | label: Issue Source Channel [Required] 67 | multiple: true 68 | options: 69 | - Encountered by myself 70 | - Identified in Bugly 71 | - User feedback 72 | - Other channels 73 | - type: input 74 | id: input_id_9 75 | attributes: 76 | label: Is it specific to certain device models? [Required] 77 | description: Specify whether the issue is specific to certain devices (e.g., specific brand or Android version). 78 | validations: 79 | required: true 80 | - type: dropdown 81 | id: input_id_10 82 | attributes: 83 | label: Does the latest version of the framework have this issue? [Required] 84 | description: If you are using an older version, it is recommended to upgrade and check if the issue still persists. 85 | multiple: false 86 | options: 87 | - "Not Selected" 88 | - "Yes" 89 | - "No" 90 | validations: 91 | required: true 92 | - type: dropdown 93 | id: input_id_11 94 | attributes: 95 | label: Is the issue mentioned in the framework documentation? [Required] 96 | description: The documentation provides answers to frequently asked questions. Please check if the information you are looking for is already provided. 97 | multiple: false 98 | options: 99 | - "Not Selected" 100 | - "Yes" 101 | - "No" 102 | validations: 103 | required: true 104 | - type: dropdown 105 | id: input_id_12 106 | attributes: 107 | label: Did you consult the framework documentation but couldn't find a solution? [Required] 108 | description: If you have consulted the documentation but still couldn't find a solution, you can select "Yes." 109 | multiple: false 110 | options: 111 | - "Not Selected" 112 | - "Yes" 113 | - "No" 114 | validations: 115 | required: true 116 | - type: dropdown 117 | id: input_id_13 118 | attributes: 119 | label: Has a similar issue been reported in the issue list? [Required] 120 | description: You can search the issue list for keywords related to your problem and refer to the solutions provided by others. 121 | multiple: false 122 | options: 123 | - "Not Selected" 124 | - "Yes" 125 | - "No" 126 | validations: 127 | required: true 128 | - type: dropdown 129 | id: input_id_14 130 | attributes: 131 | label: Have you searched the issue list but couldn't find a solution? [Required] 132 | description: If you have searched the issue list and couldn't find a solution, you can select "Yes." 133 | multiple: false 134 | options: 135 | - "Not Selected" 136 | - "Yes" 137 | - "No" 138 | validations: 139 | required: true 140 | - type: dropdown 141 | id: input_id_15 142 | attributes: 143 | label: Can the issue be reproduced with a demo project? [Required] 144 | description: Check if the issue can be reproduced in a minimal demo project to isolate potential issues in your own code. 145 | multiple: false 146 | options: 147 | - "Not Selected" 148 | - "Yes" 149 | - "No" 150 | validations: 151 | required: true 152 | - type: textarea 153 | id: input_id_16 154 | attributes: 155 | label: Provide Error Stack Trace 156 | description: If there is an error, please provide the stack trace. Note, Do not include obfuscated code in the stack trace. 157 | render: text 158 | validations: 159 | required: false 160 | - type: textarea 161 | id: input_id_17 162 | attributes: 163 | label: Provide Screenshots or Videos 164 | description: Provide screenshots or videos if necessary. This field is optional. 165 | validations: 166 | required: false 167 | - type: textarea 168 | id: input_id_18 169 | attributes: 170 | label: Provide a Solution 171 | description: If you have already found a solution, this field is optional. 172 | validations: 173 | required: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_en_template_question.yml: -------------------------------------------------------------------------------- 1 | name: Ask a Question 2 | description: Ask your questions, and I will provide you with answers. 3 | title: "[Question]:" 4 | labels: ["question"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## [Warning: Please make sure to fill in the issue template accurately. If an issue is found to be filled incorrectly, it will be closed without further notice.](https://github.com/getActivity/IssueTemplateGuide) 11 | - type: textarea 12 | id: input_id_1 13 | attributes: 14 | label: Question Description [Required] 15 | description: Please describe your question (Note, If it is a framework bug, please do not raise it here, as it will not be accepted). 16 | validations: 17 | required: true 18 | - type: dropdown 19 | id: input_id_2 20 | attributes: 21 | label: Is the issue mentioned in the framework documentation? [Required] 22 | description: The documentation provides answers to frequently asked questions. Please check if the information you are looking for is already provided. 23 | multiple: false 24 | options: 25 | - "Not Selected" 26 | - "Yes" 27 | - "No" 28 | validations: 29 | required: true 30 | - type: dropdown 31 | id: input_id_3 32 | attributes: 33 | label: Did you consult the framework documentation but couldn't find a solution? [Required] 34 | description: If you have consulted the documentation but still couldn't find a solution, you can select "Yes." 35 | multiple: false 36 | options: 37 | - "Not Selected" 38 | - "Yes" 39 | - "No" 40 | validations: 41 | required: true 42 | - type: dropdown 43 | id: input_id_4 44 | attributes: 45 | label: Has a similar issue been reported in the issue list? [Required] 46 | description: You can search the issue list for keywords related to your problem and refer to the solutions provided by others. 47 | multiple: false 48 | options: 49 | - "Not Selected" 50 | - "Yes" 51 | - "No" 52 | validations: 53 | required: true 54 | - type: dropdown 55 | id: input_id_5 56 | attributes: 57 | label: Have you searched the issue list but couldn't find a solution? [Required] 58 | description: If you have searched the issue list and couldn't find a solution, you can select "Yes." 59 | multiple: false 60 | options: 61 | - "Not Selected" 62 | - "Yes" 63 | - "No" 64 | validations: 65 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_en_template_suggest.yml: -------------------------------------------------------------------------------- 1 | name: Submit Suggestion 2 | description: Please let me know the shortcomings of the framework, so that I can improve it! 3 | title: "[Suggestion]:" 4 | labels: ["help wanted"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## [Warning: Please make sure to fill in the issue template accurately. If an issue is found to be filled incorrectly, it will be closed without further notice.](https://github.com/getActivity/IssueTemplateGuide) 11 | - type: textarea 12 | id: input_id_1 13 | attributes: 14 | label: What are the shortcomings you have noticed in the framework? [Required] 15 | description: You can describe any aspects of the framework that you are not satisfied with. 16 | validations: 17 | required: true 18 | - type: dropdown 19 | id: input_id_2 20 | attributes: 21 | label: Has a similar suggestion been made in the issue list? [Required] 22 | description: If a similar suggestion has already been made, I will not address it again. 23 | multiple: false 24 | options: 25 | - "Not Selected" 26 | - "Yes" 27 | - "No" 28 | validations: 29 | required: true 30 | - type: dropdown 31 | id: input_id_3 32 | attributes: 33 | label: Is the suggestion mentioned in the framework documentation? [Required] 34 | description: The documentation provides answers to frequently asked questions. Please check if the information you are looking for is already provided. 35 | multiple: false 36 | options: 37 | - "Not Selected" 38 | - "Yes" 39 | - "No" 40 | validations: 41 | required: true 42 | - type: dropdown 43 | id: input_id_4 44 | attributes: 45 | label: Did you consult the framework documentation but couldn't find a solution? [Required] 46 | description: If you have consulted the documentation but still couldn't find a solution, you can select "Yes." 47 | multiple: false 48 | options: 49 | - "Not Selected" 50 | - "Yes" 51 | - "No" 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: input_id_5 56 | attributes: 57 | label: How do you suggest improving it? [Optional] 58 | description: You can provide your ideas or approaches for the author's reference. 59 | validations: 60 | required: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_zh_template_bug.yml: -------------------------------------------------------------------------------- 1 | name: 提交 Bug 2 | description: 请告诉我框架存在的问题,我会协助你解决此问题! 3 | title: "[Bug]:" 4 | labels: ["bug"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## [【警告:请务必按照 issue 模板填写,不要抱有侥幸心理,一旦发现 issue 没有按照模板认真填写,一律直接关闭】](https://github.com/getActivity/IssueTemplateGuide) 11 | - type: input 12 | id: input_id_1 13 | attributes: 14 | label: 框架版本【必填】 15 | description: 请输入你使用的框架版本 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: input_id_2 20 | attributes: 21 | label: 问题描述【必填】 22 | description: 请输入你对这个问题的描述 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: input_id_3 27 | attributes: 28 | label: 复现步骤【必填】 29 | description: 请输入问题的复现步骤 30 | validations: 31 | required: true 32 | - type: dropdown 33 | id: input_id_4 34 | attributes: 35 | label: 是否必现【必填】 36 | multiple: false 37 | options: 38 | - "未选择" 39 | - "是" 40 | - "否" 41 | validations: 42 | required: true 43 | - type: input 44 | id: input_id_5 45 | attributes: 46 | label: 项目 targetSdkVersion【必填】 47 | validations: 48 | required: true 49 | - type: input 50 | id: input_id_6 51 | attributes: 52 | label: 出现问题的手机信息【必填】 53 | description: 请填写出现问题的品牌和机型 54 | validations: 55 | required: true 56 | - type: input 57 | id: input_id_7 58 | attributes: 59 | label: 出现问题的安卓版本【必填】 60 | description: 请填写出现问题的 Android 版本 61 | validations: 62 | required: true 63 | - type: dropdown 64 | id: input_id_8 65 | attributes: 66 | label: 问题信息的来源渠道【必填】 67 | multiple: true 68 | options: 69 | - 自己遇到的 70 | - Bugly 看到的 71 | - 用户反馈 72 | - 其他渠道 73 | - type: input 74 | id: input_id_9 75 | attributes: 76 | label: 是部分机型还是所有机型都会出现【必答】 77 | description: 部分/全部(例如:某为,某 Android 版本会出现) 78 | validations: 79 | required: true 80 | - type: dropdown 81 | id: input_id_10 82 | attributes: 83 | label: 框架最新的版本是否存在这个问题【必答】 84 | description: 如果用的是旧版本的话,建议升级看问题是否还存在 85 | multiple: false 86 | options: 87 | - "未选择" 88 | - "是" 89 | - "否" 90 | validations: 91 | required: true 92 | - type: dropdown 93 | id: input_id_11 94 | attributes: 95 | label: 框架文档是否提及了该问题【必答】 96 | description: 文档会提供最常见的问题解答,可以先看看是否有自己想要的 97 | multiple: false 98 | options: 99 | - "未选择" 100 | - "是" 101 | - "否" 102 | validations: 103 | required: true 104 | - type: dropdown 105 | id: input_id_12 106 | attributes: 107 | label: 是否已经查阅框架文档但还未能解决的【必答】 108 | description: 如果查阅了文档但还是没有解决的话,可以选择是 109 | multiple: false 110 | options: 111 | - "未选择" 112 | - "是" 113 | - "否" 114 | validations: 115 | required: true 116 | - type: dropdown 117 | id: input_id_13 118 | attributes: 119 | label: issue 列表中是否有人曾提过类似的问题【必答】 120 | description: 可以在 issue 列表在搜索问题关键字,参考一下别人的解决方案 121 | multiple: false 122 | options: 123 | - "未选择" 124 | - "是" 125 | - "否" 126 | validations: 127 | required: true 128 | - type: dropdown 129 | id: input_id_14 130 | attributes: 131 | label: 是否已经搜索过了 issue 列表但还未能解决的【必答】 132 | description: 如果搜索过了 issue 列表但是问题没有解决的话,可以选择是 133 | multiple: false 134 | options: 135 | - "未选择" 136 | - "是" 137 | - "否" 138 | validations: 139 | required: true 140 | - type: dropdown 141 | id: input_id_15 142 | attributes: 143 | label: 是否可以通过 Demo 来复现该问题【必答】 144 | description: 排查一下是不是自己的项目代码写得有问题导致的 145 | multiple: false 146 | options: 147 | - "未选择" 148 | - "是" 149 | - "否" 150 | validations: 151 | required: true 152 | - type: textarea 153 | id: input_id_16 154 | attributes: 155 | label: 提供报错堆栈 156 | description: 如果有报错的话必填,注意不要拿被混淆过的代码堆栈上来 157 | render: text 158 | validations: 159 | required: false 160 | - type: textarea 161 | id: input_id_17 162 | attributes: 163 | label: 提供截图或视频 164 | description: 根据需要提供,此项不强制 165 | validations: 166 | required: false 167 | - type: textarea 168 | id: input_id_18 169 | attributes: 170 | label: 提供解决方案 171 | description: 如果已经解决了的话,此项不强制 172 | validations: 173 | required: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_zh_template_question.yml: -------------------------------------------------------------------------------- 1 | name: 提出疑问 2 | description: 提出你的困惑,我会给你解答 3 | title: "[疑惑]:" 4 | labels: ["question"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## [【警告:请务必按照 issue 模板填写,不要抱有侥幸心理,一旦发现 issue 没有按照模板认真填写,一律直接关闭】](https://github.com/getActivity/IssueTemplateGuide) 11 | - type: textarea 12 | id: input_id_1 13 | attributes: 14 | label: 问题描述【必填】 15 | description: 请描述一下你的问题(注意:如果确定是框架 bug 请不要在这里提,否则一概不受理) 16 | validations: 17 | required: true 18 | - type: dropdown 19 | id: input_id_2 20 | attributes: 21 | label: 框架文档是否提及了该问题【必答】 22 | description: 文档会提供最常见的问题解答,可以先看看是否有自己想要的 23 | multiple: false 24 | options: 25 | - "未选择" 26 | - "是" 27 | - "否" 28 | validations: 29 | required: true 30 | - type: dropdown 31 | id: input_id_3 32 | attributes: 33 | label: 是否已经查阅框架文档但还未能解决的【必答】 34 | description: 如果查阅了文档但还是没有解决的话,可以选择是 35 | multiple: false 36 | options: 37 | - "未选择" 38 | - "是" 39 | - "否" 40 | validations: 41 | required: true 42 | - type: dropdown 43 | id: input_id_4 44 | attributes: 45 | label: issue 列表中是否有人曾提过类似的问题【必答】 46 | description: 可以在 issue 列表在搜索问题关键字,参考一下别人的解决方案 47 | multiple: false 48 | options: 49 | - "未选择" 50 | - "是" 51 | - "否" 52 | validations: 53 | required: true 54 | - type: dropdown 55 | id: input_id_5 56 | attributes: 57 | label: 是否已经搜索过了 issue 列表但还未能解决的【必答】 58 | description: 如果搜索过了 issue 列表但是问题没有解决的话,可以选择是 59 | multiple: false 60 | options: 61 | - "未选择" 62 | - "是" 63 | - "否" 64 | validations: 65 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_zh_template_suggest.yml: -------------------------------------------------------------------------------- 1 | name: 提交建议 2 | description: 请告诉我框架的不足之处,让我做得更好! 3 | title: "[建议]:" 4 | labels: ["help wanted"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## [【警告:请务必按照 issue 模板填写,不要抱有侥幸心理,一旦发现 issue 没有按照模板认真填写,一律直接关闭】](https://github.com/getActivity/IssueTemplateGuide) 11 | - type: textarea 12 | id: input_id_1 13 | attributes: 14 | label: 你觉得框架有什么不足之处?【必答】 15 | description: 你可以描述框架有什么令你不满意的地方 16 | validations: 17 | required: true 18 | - type: dropdown 19 | id: input_id_2 20 | attributes: 21 | label: issue 是否有人曾提过类似的建议?【必答】 22 | description: 一旦出现重复提问我将不会再次解答 23 | multiple: false 24 | options: 25 | - "未选择" 26 | - "是" 27 | - "否" 28 | validations: 29 | required: true 30 | - type: dropdown 31 | id: input_id_3 32 | attributes: 33 | label: 框架文档是否提及了该问题【必答】 34 | description: 文档会提供最常见的问题解答,可以先看看是否有自己想要的 35 | multiple: false 36 | options: 37 | - "未选择" 38 | - "是" 39 | - "否" 40 | validations: 41 | required: true 42 | - type: dropdown 43 | id: input_id_4 44 | attributes: 45 | label: 是否已经查阅框架文档但还未能解决的【必答】 46 | description: 如果查阅了文档但还是没有解决的话,可以选择是 47 | multiple: false 48 | options: 49 | - "未选择" 50 | - "是" 51 | - "否" 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: input_id_5 56 | attributes: 57 | label: 你觉得该怎么去完善会比较好?【非必答】 58 | description: 你可以提供一下自己的想法或者做法供作者参考 59 | validations: 60 | required: false -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: set up JDK 1.8 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 1.8 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | .cxx 4 | .externalNativeBuild 5 | build 6 | captures 7 | 8 | ._* 9 | *.iml 10 | .DS_Store 11 | local.properties -------------------------------------------------------------------------------- /HelpDoc-en.md: -------------------------------------------------------------------------------- 1 | #### Catalog 2 | 3 | * [How to customize toast display animation](#how-to-customize-toast-display-animation) 4 | 5 | * [How to customize toast display duration](#how-to-customize-toast-display-duration) 6 | 7 | * [How to customize toast layout style](#how-to-customize-toast-layout-style) 8 | 9 | * [How to switch to toast queue display strategy](#how-to-switch-to-toast-queue-display-strategy) 10 | 11 | * [What should I do if the framework cannot meet the scene I am currently using](#what-should-i-do-if-the-framework-cannot-meet-the-scene-i-am-currently-using) 12 | 13 | * [How to implement cross page display of toast in the framework](#how-to-implement-cross-page-display-of-toast-in-the-framework) 14 | 15 | * [Why the framework prefers to use window manager to implement toast](#why-the-framework-prefers-to-use-window-manager-to-implement-toast) 16 | 17 | #### How to customize toast display animation 18 | 19 | * When toast is initialized, just modify the toast strategy 20 | 21 | ```java 22 | Toaster.init(this, new ToastStrategy() { 23 | 24 | @Override 25 | public IToast createToast(IToastStyle style) { 26 | IToast toast = super.createToast(style); 27 | if (toast instanceof CustomToast) { 28 | CustomToast customToast = ((CustomToast) toast); 29 | // Set the toast animation effect 30 | customToast.setAnimationsId(R.anim.xxx); 31 | } 32 | return toast; 33 | } 34 | }); 35 | ``` 36 | 37 | * The disadvantage of this method is that it will only take effect when the application is in the foreground. This is because the toast in the foreground is implemented with a framework, which is essentially a WindowManager. The advantage is that it is very flexible and is not limited by the system toast mechanism. The disadvantage is that it cannot It is displayed in the background; while the toast in the background is implemented by the system, the advantage is that it can be displayed in the background, the disadvantage is that it is very limited and cannot be customized too deeply; and the framework uses two The advantages and disadvantages of the two methods are complementary. 38 | 39 | #### How to customize toast display duration 40 | 41 | * When toast is initialized, just modify the toast strategy 42 | 43 | ```java 44 | Toaster.init(this, new ToastStrategy() { 45 | 46 | @Override 47 | public IToast createToast(IToastStyle style) { 48 | IToast toast = super.createToast(style); 49 | if (toast instanceof CustomToast) { 50 | CustomToast customToast = ((CustomToast) toast); 51 | // Set the display duration of the short toast (default is 2000 milliseconds) 52 | customToast.setShortDuration(1000); 53 | // Set the display duration of the long Toast (default is 3500 milliseconds) 54 | customToast.setLongDuration(5000); 55 | } 56 | return toast; 57 | } 58 | }); 59 | ``` 60 | 61 | * The disadvantage of this method is that it will only take effect when the application is in the foreground. This is because the toast in the foreground is implemented with a framework, which is essentially a WindowManager. The advantage is that it is very flexible and is not limited by the system toast mechanism. The disadvantage is that it cannot It is displayed in the background; while the toast in the background is implemented by the system, the advantage is that it can be displayed in the background, the disadvantage is that it is very limited and cannot be customized too deeply; and the framework uses two The advantages and disadvantages of the two methods are complementary. 62 | 63 | #### How to customize toast layout style 64 | 65 | * If you want to set the global toast style, you can call it like this (choose any one) 66 | 67 | ```java 68 | // Modify toast layout 69 | Toaster.setView(int id); 70 | ``` 71 | 72 | ```java 73 | // Modified toast layout, toast shows center of gravity, toast shows position offset 74 | Toaster.setStyle(IToastStyle style); 75 | ``` 76 | 77 | * If you want to set a separate Toast display style for one occasion, you can do all of these (select either) 78 | 79 | ```java 80 | // Modify toast layout 81 | ToastParams params = new ToastParams(); 82 | params.text = "I am toast of custom layout (partial effect)"; 83 | params.style = new CustomViewToastStyle(R.layout.toast_custom_view); 84 | Toaster.show(params); 85 | ``` 86 | 87 | ```java 88 | // Modify the toast layout, toast display center of gravity, and toast display position offset 89 | ToastParams params = new ToastParams(); 90 | params.text = "I am toast of custom layout (partial effect)"; 91 | params.style = new CustomViewToastStyle(R.layout.toast_custom_view, Gravity.CENTER, 10, 20); 92 | Toaster.show(params); 93 | ``` 94 | 95 | * At this point, you may have a doubt, why setting a new toast style can only pass in the layout id instead of the View object? Because every time the framework displays toast, it will create a new toast object and View object. If the View object is passed in, it will not be able to create it every time it is displayed. As for why the framework does not reuse this View object, it is because if After reusing this View object, the following exceptions may be triggered: 96 | 97 | ```text 98 | java.lang.IllegalStateException: View android.widget.TextView{7ffea98 V.ED..... ......ID 0,0-396,153 #102000b android:id/message} 99 | has already been added to the window manager. 100 | at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:371) 101 | at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:131) 102 | at android.widget.Toast$TN.handleShow(Toast.java:501) 103 | at android.widget.Toast$TN$1.handleMessage(Toast.java:403) 104 | at android.os.Handler.dispatchMessage(Handler.java:112) 105 | at android.os.Looper.loop(Looper.java:216) 106 | at android.app.ActivityThread.main(ActivityThread.java:7625) 107 | at java.lang.reflect.Method.invoke(Native Method) 108 | at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524) 109 | at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987) 110 | ``` 111 | 112 | * This is because WindowManager succeeded when addingView, but failed when removingView, which caused the View object of the previous toast to be unable to be reused when the next toast is displayed. Although this situation is relatively rare, there are still people who have reported this to me. Problem, in order to solve this problem, I decided not to reuse the View object. For specific adjustments to this piece, you can check the release record: [Toaster/releases/tag/9.0](https://github.com/getActivity/Toaster/releases/tag/9.0) 113 | 114 | #### How to switch to toast queue display strategy 115 | 116 | * You only need to modify the initialization method of the toast framework and manually pass in the toast strategy class. Here, you can use the ToastStrategy class that has been encapsulated by the framework. 117 | 118 | ```java 119 | // Initialize the toast framework 120 | // Toaster.init(this); 121 | Toaster.init(this, new ToastStrategy(ToastStrategy.SHOW_STRATEGY_TYPE_QUEUE)); 122 | ``` 123 | 124 | * Note that the constructor needs to pass in `ToastStrategy.SHOW_STRATEGY_TYPE_QUEUE`. For an introduction to this field, see the code comments below 125 | 126 | ```java 127 | public class ToastStrategy { 128 | 129 | /** 130 | * Instant display mode (default) 131 | * 132 | * In the case of multiple toast display requests, before displaying the next toast 133 | * The previous toast will be canceled immediately to ensure that the currently displayed toast message is up to date 134 | */ 135 | public static final int SHOW_STRATEGY_TYPE_IMMEDIATELY = 0; 136 | 137 | /** 138 | * No message loss mode 139 | * 140 | * In the case of multiple toast display requests, wait for the previous toast to be displayed for 1 second or 1.5 seconds 141 | * Then display the next toast, not according to the display duration of the toast, because the waiting time will be very long 142 | * This can not only ensure that the user can see every toast message, but also ensure that the user will not wait too long 143 | */ 144 | public static final int SHOW_STRATEGY_TYPE_QUEUE = 1; 145 | } 146 | ``` 147 | 148 | #### What should I do if the framework cannot meet the scene I am currently using 149 | 150 | * The Toaster framework is intended to solve some toast requirements. If Toaster cannot meet your needs, you can consider using the [EasyWindow](https://github.com/getActivity/EasyWindow) floating window framework to achieve it. 151 | 152 | #### How to implement cross page display of toast in the framework 153 | 154 | * By default, Toasts in the Toaster framework are only displayed on the current Activity. If an Activity switch occurs, the Toast becomes invisible as the current Activity is destroyed, making it impossible to display on the new Activity. The framework added support for this feature in version [12.5](https://github.com/getActivity/Toaster/releases/tag/12.5). The following is an example of how to use it: 155 | 156 | ```java 157 | ToastParams params = new ToastParams(); 158 | params.text = "I am a Toast that can be displayed across pages"; 159 | // Indicates that this Toast needs to be displayed across pages 160 | params.crossPageShow = true; 161 | Toaster.show(params); 162 | 163 | #### Why the framework prefers to use window manager to implement toast 164 | 165 | * There are too many pits in the system toast, the main problems are as follows: 166 | 167 | * System toast will cause some memory leaks 168 | 169 | * System toast cannot realize custom display animation and display duration control 170 | 171 | * Android 7.1 version will block the main thread and cause BadTokenException 172 | 173 | * Closing the permission of the notification bar below Android 10.0 will cause the problem that the system toast cannot be displayed 174 | 175 | * Android 11 and above, cannot customize the toast style (layout, position center of gravity, position offset) 176 | 177 | * Therefore, the framework prefers to use WindowManager instead of implementing toast display. The specific advantages and disadvantages are as follows: 178 | 179 | * advantage 180 | 181 | * There will be no memory leaks, and there will not be so many strange problems 182 | 183 | * High degree of customization, support custom animation and custom display duration 184 | 185 | * Break through Google's restrictions on toast in the new version of Android 186 | 187 | * shortcoming 188 | 189 | * WindowManager cannot pop up
in the background without floating window permission (frame solution: if it is displayed in the background, use the system's toast to display) 190 | 191 | * The WindowManager will be bound to the Activity and will disappear with the Activity being destroyed
(framework solution: the display is delayed by 200ms, thus waiting for the latest Activity to be created before calling the display, so WindowManager is bound to the latest Activity and does not have the problem of disappearing with the old Activity when it finishes) 192 | 193 | * Of course, it is not to say that using the system toast is not good. It must be good to use WindowManger. It depends on the specific usage scenario. I think the best way is: use WindowManager to display the application in the foreground, and use the system in the background. the best solution is to use WindowManager in the foreground state and system Toast in the background state. 194 | -------------------------------------------------------------------------------- /HelpDoc-zh.md: -------------------------------------------------------------------------------- 1 | #### 目录 2 | 3 | * [框架怎么改名了](#框架怎么改名了) 4 | 5 | * [怎么自定义 Toast 显示动画](#怎么自定义-toast-显示动画) 6 | 7 | * [怎么自定义 Toast 显示时长](#怎么自定义-toast-显示时长) 8 | 9 | * [怎么自定义 Toast 布局样式](#怎么自定义-toast-布局样式) 10 | 11 | * [怎么切换成 Toast 排队显示的策略](#怎么切换成-toast-排队显示的策略) 12 | 13 | * [框架无法满足我当前使用的场景怎么办](#框架无法满足我当前使用的场景怎么办) 14 | 15 | * [框架中的 Toast 如何实现跨页面显示](#框架中的-toast-如何实现跨页面显示) 16 | 17 | * [为什么框架优先使用 WindowManager 来实现 Toast](#为什么框架优先使用-windowManager-来实现-toast) 18 | 19 | #### 框架怎么改名了 20 | 21 | * 框架改名是一个重大的操作,我为什么选择在现在这个时候改,是基于以下思考: 22 | 23 | * 框架第一次提交是在 2018 年 9 月,不知不觉我已经维护了将近了 5 年的时间,我觉得 ToastUtils 这个名称已经配不上它的气质了,名字虽然好记,但是过于大众化,没有辨识度。 24 | 25 | * 虽然名称叫 ToastUtils,但是经过无数次改造和重构后,它变得不像工具类了,比如它需要先调用 init 方法来初始化框架,才能使用 show 方法来显示 Toast,另外框架还对外提供了设置 Toast 策略类、Toast 拦截器、Toast 样式类,而这些方法不应该出现一个工具类中,工具类应该是只对外提供模板方法,而不应该把外部传入的对象作为静态持有着。 26 | 27 | * 至于为什么选择在这个时候改名,这是框架基本稳定无 Bug 了,该解决的问题都已经解决完了,最近几个月已经没有人提 issue 了,相比前几年,一两个星期就能收到一个 issue 相比,框架已经非常稳定了,根据我以往的经验来讲,大家其实对框架的要求极其苛刻,如果这个框架在 Bugly 中有出现崩溃或者 ANR,哪怕是报一个用户一次异常,只要框架还在维护,就会有人找上门提 issue,而这次最近几个月没有人找上门,并不是用的人少了,更不是奇迹诞生了,大概率是调试阶段和线上阶段都没有找到框架的问题,框架的功能也能满足需求。 28 | 29 | * 至于为什么改名叫 Toaster,很大一部分原因是大家的选择,我发起了一项投票,票数最多的就是这个名字,同时我也采纳了这一项,因为不仅仅是名字好记有辨识度,还具备了特殊的含义,我们都知道 Toast 中文翻译是面包的意思,而 Toaster 中文翻译是烤面包机的意思,吃 Toast 之前需要先用烤一下,口感会更加酥脆。 30 | 31 | ![](picture/zh/help_doc_rename_vote.jpg) 32 | 33 | #### 怎么自定义 Toast 显示动画 34 | 35 | * 在 Toast 初始化的时候,修改 Toast 策略即可 36 | 37 | ```java 38 | Toaster.init(this, new ToastStrategy() { 39 | 40 | @Override 41 | public IToast createToast(IToastStyle style) { 42 | IToast toast = super.createToast(style); 43 | if (toast instanceof CustomToast) { 44 | CustomToast customToast = ((CustomToast) toast); 45 | // 设置 Toast 动画效果 46 | customToast.setAnimationsId(R.anim.xxx); 47 | } 48 | return toast; 49 | } 50 | }); 51 | ``` 52 | 53 | * 这种方式的缺点是只有应用在前台的情况下才会生效,这是因为前台的 Toast 是用框架实现的,本质上是一个 WindowManager,优点是非常灵活,不受系统 Toast 机制限制,缺点是无法在后台的情况下显示;而后台的 Toast 是用系统来实现的,优点是能在后台的情况下显示,缺点是局限性非常大,无法做太深的定制化;而框架正是利用了两种方式的优缺点进行了互补。 54 | 55 | #### 怎么自定义 Toast 显示时长 56 | 57 | * 在 Toast 初始化的时候,修改 Toast 策略即可 58 | 59 | ```java 60 | Toaster.init(this, new ToastStrategy() { 61 | 62 | @Override 63 | public IToast createToast(IToastStyle style) { 64 | IToast toast = super.createToast(style); 65 | if (toast instanceof CustomToast) { 66 | CustomToast customToast = ((CustomToast) toast); 67 | // 设置短 Toast 的显示时长(默认是 2000 毫秒) 68 | customToast.setShortDuration(1000); 69 | // 设置长 Toast 的显示时长(默认是 3500 毫秒) 70 | customToast.setLongDuration(5000); 71 | } 72 | return toast; 73 | } 74 | }); 75 | ``` 76 | 77 | * 这种方式的缺点是只有应用在前台的情况下才会生效,这是因为前台的 Toast 是用框架实现的,本质上是一个 WindowManager,优点是非常灵活,不受系统 Toast 机制限制,缺点是无法在后台的情况下显示;而后台的 Toast 是用系统来实现的,优点是能在后台的情况下显示,缺点是局限性非常大,无法做太深的定制化;而框架正是利用了两种方式的优缺点进行了互补。 78 | 79 | #### 怎么自定义 Toast 布局样式 80 | 81 | * 如果你想设置全局的 Toast 样式,可以这样调用(选择任一一种即可) 82 | 83 | ```java 84 | // 修改 Toast 布局 85 | Toaster.setView(int id); 86 | ``` 87 | 88 | ```java 89 | // 修改 Toast 布局,Toast 显示重心,Toast 显示位置偏移 90 | Toaster.setStyle(IToastStyle style); 91 | ``` 92 | 93 | * 如果你想为某次 Toast 显示设置单独的样式,可以这样样用(选择任一一种即可) 94 | 95 | ```java 96 | // 修改 Toast 布局 97 | ToastParams params = new ToastParams(); 98 | params.text = "我是自定义布局的 Toast(局部生效)"; 99 | params.style = new CustomViewToastStyle(R.layout.toast_custom_view); 100 | Toaster.show(params); 101 | ``` 102 | 103 | ```java 104 | // 修改 Toast 布局、Toast 显示重心、Toast 显示位置偏移 105 | ToastParams params = new ToastParams(); 106 | params.text = "我是自定义布局的 Toast(局部生效)"; 107 | params.style = new CustomViewToastStyle(R.layout.toast_custom_view, Gravity.CENTER, 10, 20); 108 | Toaster.show(params); 109 | ``` 110 | 111 | * 到此,大家可能有一个疑惑,为什么设置新的 Toast 样式只能传入布局 id 而不是 View 对象?因为框架每次显示 Toast 的时候,都会创建新的 Toast 对象和 View 对象,如果传入 View 对象将无法做到每次显示的时候都创建,至于框架为什么不复用这个 View 对象,这是因为如果复用了这个 View 对象,可能会触发以下异常: 112 | 113 | ```text 114 | java.lang.IllegalStateException: View android.widget.TextView{7ffea98 V.ED..... ......ID 0,0-396,153 #102000b android:id/message} 115 | has already been added to the window manager. 116 | at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:371) 117 | at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:131) 118 | at android.widget.Toast$TN.handleShow(Toast.java:501) 119 | at android.widget.Toast$TN$1.handleMessage(Toast.java:403) 120 | at android.os.Handler.dispatchMessage(Handler.java:112) 121 | at android.os.Looper.loop(Looper.java:216) 122 | at android.app.ActivityThread.main(ActivityThread.java:7625) 123 | at java.lang.reflect.Method.invoke(Native Method) 124 | at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524) 125 | at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987) 126 | ``` 127 | 128 | * 这是因为 WindowManager addView 的时候成功了,但是 removeView 的时候失败了,导致下一个 Toast 显示的时候,无法复用上一个 Toast 的 View 对象,虽然这种情况比较少见,但是仍然有人跟我反馈过这个问题,为了解决这一问题,所以决定不去复用 View 对象,具体对这块的调整可以查看发版记录:[Toaster/releases/tag/9.0](https://github.com/getActivity/Toaster/releases/tag/9.0) 129 | 130 | #### 怎么切换成 Toast 排队显示的策略 131 | 132 | * 只需要修改 Toast 框架的初始化方式,手动传入 Toast 策略类,这里使用框架已经封装好的 ToastStrategy 类即可, 133 | 134 | ```java 135 | // 初始化 Toast 框架 136 | // Toaster.init(this); 137 | Toaster.init(this, new ToastStrategy(ToastStrategy.SHOW_STRATEGY_TYPE_QUEUE)); 138 | ``` 139 | 140 | * 注意构造函数需要传入 `ToastStrategy.SHOW_STRATEGY_TYPE_QUEUE`,关于这个字段的介绍可以看下面的代码注释 141 | 142 | ```java 143 | public class ToastStrategy { 144 | 145 | /** 146 | * 即显即示模式(默认) 147 | * 148 | * 在发起多次 Toast 的显示请求情况下,显示下一个 Toast 之前 149 | * 会先立即取消上一个 Toast,保证当前显示 Toast 消息是最新的 150 | */ 151 | public static final int SHOW_STRATEGY_TYPE_IMMEDIATELY = 0; 152 | 153 | /** 154 | * 不丢消息模式 155 | * 156 | * 在发起多次 Toast 的显示请求情况下,等待上一个 Toast 显示 1 秒或者 1.5 秒后 157 | * 然后再显示下一个 Toast,不按照 Toast 的显示时长来,因为那样等待时间会很长 158 | * 这样既能保证用户能看到每一条 Toast 消息,又能保证用户不会等得太久,速战速决 159 | */ 160 | public static final int SHOW_STRATEGY_TYPE_QUEUE = 1; 161 | } 162 | ``` 163 | 164 | #### 框架无法满足我当前使用的场景怎么办 165 | 166 | * Toaster 框架意在解决一些的 Toast 需求,如果 Toaster 无法满足你的需求,你可以考虑使用 [EasyWindow](https://github.com/getActivity/EasyWindow) 悬浮窗框架来实现。 167 | 168 | #### 框架中的 Toast 如何实现跨页面显示 169 | 170 | * Toaster 中默认 Toast 是只在当前 Activity 上面展示的,如果遇到 Activity 切换,那么在 Toast 随着当前 Activity 销毁而不可见,导致无法在新 Activity 上面展示,框架在 [12.5](https://github.com/getActivity/Toaster/releases/tag/12.5) 版本新增支持了这一功能,具体调用示例如下: 171 | 172 | ```java 173 | ToastParams params = new ToastParams(); 174 | params.text = "我是一个能跨页面展示的 Toast"; 175 | // 表示这个 Toast 需要跨页面展示 176 | params.crossPageShow = true; 177 | Toaster.show(params); 178 | ``` 179 | 180 | #### 为什么框架优先使用 WindowManager 来实现 Toast 181 | 182 | * 系统 Toast 的坑太多了,主要问题表现如下: 183 | 184 | * 系统 Toast 会引发一些内存泄漏的问题 185 | 186 | * 系统 Toast 无法实现自定义显示动画、显示时长控制 187 | 188 | * Android 7.1 版本会主线程阻塞会出现 BadTokenException 的问题 189 | 190 | * Android 10.0 以下关闭通知栏权限会导致系统 Toast 显示不出来的问题 191 | 192 | * Android 11 及以上版本,无法自定义 Toast 样式(布局、位置重心、位置偏移) 193 | 194 | * 所以框架优先使用 WindowManager 来代替实现 Toast 显示,具体优缺点以下: 195 | 196 | * 优点 197 | 198 | * 不会出现内存泄漏,也不会有那么多奇奇怪怪的问题 199 | 200 | * 可定制程度高,支持自定义动画和自定义显示时长 201 | 202 | * 突破 Google 在新版本 Android 对 Toast 的一些限制 203 | 204 | * 缺点 205 | 206 | * WindowManager 无法在没有悬浮窗权限情况下在后台弹出
(框架的解决方案:如果是在后台的情况下显示,则使用系统的 Toast 来显示) 207 | 208 | * WindowManager 会和 Activity 绑定,会随 Activity 销毁而消失
(框架的解决方案:延迟 200 毫秒显示,由此等待最新的 Activity 创建出来才调用显示,这样 WindowManager 就和最新 Activity 绑定在一起,就不会出现和旧 Activity finish 时一起消失的问题) 209 | 210 | * 当然不是说用系统 Toast 就不好,用 WindowManger 一定就好,视具体的使用场景而定,我觉得最好的方式是:应用处于前台状态下使用 WindowManager 来显示,而处于后台状态下使用系统 Toast 来显示,两者相结合,优势互补才是最佳方案。 211 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, September 2018 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2018 Huang JinQun 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | # [English Doc](README-en.md) 2 | 3 | # Toast Framework 4 | 5 | * Project address: [Github](https://github.com/getActivity/Toaster) 6 | 7 | * [Click here to download demo apk directly](https://github.com/getActivity/Toaster/releases/download/12.8/Toaster.apk) 8 | 9 | ![](picture/en/demo_toast_activity.jpg) ![](picture/en/demo_toast_style_white.jpg) ![](picture/en/demo_toast_style_black.jpg) 10 | 11 | ![](picture/en/demo_toast_layout_info.jpg) ![](picture/en/demo_toast_layout_warn.jpg) ![](picture/en/demo_toast_layout_success.jpg) 12 | 13 | ![](picture/en/demo_toast_layout_error.jpg) ![](picture/en/demo_toast_layout_custom.jpg) 14 | 15 | #### Integration steps 16 | 17 | * If your project Gradle configuration is in `7.0` below, needs to be in `build.gradle` file added 18 | 19 | ```groovy 20 | allprojects { 21 | repositories { 22 | // JitPack remote repository:https://jitpack.io 23 | maven { url 'https://jitpack.io' } 24 | } 25 | } 26 | ``` 27 | 28 | * If your Gradle configuration is `7.0` or above, needs to be in `settings.gradle` file added 29 | 30 | ```groovy 31 | dependencyResolutionManagement { 32 | repositories { 33 | // JitPack remote repository:https://jitpack.io 34 | maven { url 'https://jitpack.io' } 35 | } 36 | } 37 | ``` 38 | 39 | * After configuring the remote warehouse, under the project app module `build.gradle` add remote dependencies to the file 40 | 41 | ```groovy 42 | android { 43 | // Support JDK 1.8 44 | compileOptions { 45 | targetCompatibility JavaVersion.VERSION_1_8 46 | sourceCompatibility JavaVersion.VERSION_1_8 47 | } 48 | } 49 | 50 | dependencies { 51 | // Toast framework:https://github.com/getActivity/Toaster 52 | implementation 'com.github.getActivity:Toaster:12.8' 53 | } 54 | ``` 55 | 56 | #### Initialize the framework 57 | 58 | ```java 59 | public class XxxApplication extends Application { 60 | 61 | @Override 62 | public void onCreate() { 63 | super.onCreate(); 64 | 65 | // Initialize the toast framework 66 | Toaster.init(this); 67 | } 68 | } 69 | ``` 70 | 71 | #### Framework API introduction 72 | 73 | ```java 74 | // Show toast 75 | Toaster.show(CharSequence text); 76 | Toaster.show(int id); 77 | Toaster.show(Object object); 78 | 79 | // Toast is displayed in debug mode 80 | Toaster.debugShow(CharSequence text); 81 | Toaster.debugShow(int id); 82 | Toaster.debugShow(Object object); 83 | 84 | // Delayed display of toast 85 | Toaster.delayedShow(CharSequence text, long delayMillis); 86 | Toaster.delayedShow(int id, long delayMillis); 87 | Toaster.delayedShow(Object object, long delayMillis); 88 | 89 | // Show short toast 90 | Toaster.showShort(CharSequence text); 91 | Toaster.showShort(int id); 92 | Toaster.showShort(Object object); 93 | 94 | // Show long toast 95 | Toaster.showLong(CharSequence text); 96 | Toaster.showLong(int id); 97 | Toaster.showLong(Object object); 98 | 99 | // Custom display toast 100 | Toaster.show(ToastParams params); 101 | 102 | // Cancel toast 103 | Toaster.cancel(); 104 | 105 | // Set toast layout (global effect) 106 | Toaster.setView(int id); 107 | 108 | // Set toast style (global effect) 109 | Toaster.setStyle(IToastStyle style); 110 | // Get toast style 111 | Toaster.getStyle() 112 | 113 | // Determine whether the current framework has been initialized 114 | Toaster.isInit(); 115 | 116 | // Set toast strategy (global effect) 117 | Toaster.setStrategy(IToastStrategy strategy); 118 | // Get toast strategy 119 | Toaster.getStrategy(); 120 | 121 | // Set toast center of gravity and offset 122 | Toaster.setGravity(int gravity); 123 | Toaster.setGravity(int gravity, int xOffset, int yOffset); 124 | 125 | // Set Toast interceptor (global effect) 126 | Toaster.setInterceptor(IToastInterceptor interceptor); 127 | // Get Toast interceptor 128 | Toaster.getInterceptor(); 129 | ``` 130 | 131 | ## [Please click here to view frequently asked questions](HelpDoc-en.md) 132 | 133 | #### Comparison between different Toast frameworks 134 | 135 | | Function or detail | [Toaster](https://github.com/getActivity/Toaster) |[ AndroidUtilCode-ToastUtils ](https://github.com/Blankj/AndroidUtilCode)| [Toasty](https://github.com/GrenderG/Toasty) | 136 | | :----: | :------: | :-----: | :-----: | 137 | | Corresponding version | 12.8 | 1.30.6 | 1.5.0 | 138 | | Number of issues | [![](https://img.shields.io/github/issues/getActivity/Toaster.svg)](https://github.com/getActivity/Toaster/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) | 139 | | Framework pack size | 31 KB | 500 KB | 50 KB | 140 | | Framework maintenance status| 维护中 | 停止维护 | 停止维护 | 141 | | Call code trace | ✅ | ❌ | ❌ | 142 | | Support calling display in sub-threads | ✅ | ✅ | ❌ | 143 | | Support setting partial Toast style | ✅ | ❌ | ❌ | 144 | | Support setting global Toast style | ✅ | ❌ | ❌ | 145 | | Support Toast Instant display | ✅ | ✅ | ❌ | 146 | | Support Toast queue display | ✅ | ❌ | ✅ | 147 | | Support Toast delayed display | ✅ | ❌ | ❌ | 148 | | Solve the problem that Toast crashes on Android 7.1 | ✅ | ✅ | ❌ | 149 | | Compatible with the problem that the Toast cannot be displayed after the permission of the notification bar is turned off | ✅ | ✅ | ❌ | 150 | | Adapted to Android 11, the problem that Toast cannot be displayed in the background | ✅ | ❌ | ❌ | 151 | 152 | #### Introduction to calling code positioning function 153 | 154 | * The framework will output the location of the code called by Toast in the log printing, so that developers can directly click Log to locate which class and line of code is called, which can greatly improve the efficiency of our troubleshooting, especially if the Toast content is returned by the server, I believe that no one will reject such a function. 155 | 156 | ![](picture/en/demo_logcat_code.jpg) 157 | 158 | #### Introduction to the problem of Toast crashing on Android 7.1 159 | 160 | * This problem is caused by the addition of the WindowToken verification mechanism in Android 7.1, and this WindowToken is generated by NotificationManagerService. This WindowToken has a certain timeliness, and when the main thread of the application is blocked, WindowManager will calibrate the WindowToken when addingView However, the WindowToken has expired, and addView will throw an exception at this time. 161 | 162 | * Google fixed this problem in Android 8.0. The repair method is very simple and rude, which is to directly capture this exception. The repair idea of ​​the framework is similar to that of Google, but the repair method is different, because the framework cannot directly modify the system source code, so it is directly Exceptions are caught by means of Hook. 163 | 164 | #### Introduction to the problem that Toast cannot be displayed after the notification bar permission is turned off 165 | 166 | * This problem occurs because the display of the native Toast needs to pass through NMS (NotificationManagerService) to addView to the Window, and there is a `static final boolean ENABLE_BLOCKED_TOASTS = true` field in NMS. When the constant value is true, it will Trigger NMS to check the application notification bar permission. If there is no notification bar permission, then this Toast will be intercepted by NMS and output `Suppressing toast from package` log information. Xiaomi phones do not have this problem because they are Change the value of the `ENABLE_BLOCKED_TOASTS` field to `false`, so the check on the permission of the notification bar will not be triggered, and why do I know this? Because I once confirmed this with a MIUI engineer. 167 | 168 | * There are two ways for the framework to handle this problem. First, determine whether the current application is in the foreground state. If so, use a custom WindowManager instead of Toast to display it. If the current application is in the background state, it will use the INotificationManager interface in Hook Toast. The package name parameter passed by the enqueueToast method is changed to `android` to deceive NotificationManagerService, because NotificationManagerService has whitelisted the application with `android` package name, the system automatically permits. one thing to note is that, this method has expired on Android 10 and has been included in the reflection blacklist by the system, but the good news is that after checking and comparing the source code of NotificationManagerService, this problem (the problem of not being able to play Toast in the foreground after closing the notification bar permission) It has been fixed on Android 10.0, so the framework only goes to Hook INotificationManager when Android 9.0 and below and the notification bar permission is turned off. In addition, I also found the official code submission record about this piece:[ Always allow toasts from foreground apps ](https://cs.android.com/android/_/android/platform/frameworks/base/+/58b2453ed69197d765c7254241d9966ee49a3efb), you can take a look if you are interested, there is another question, if you want to still display Toast in the background in Android 10 and later versions, please ensure the notification bar permission or floating window permission of the application It is turned on. If you must require 100% display of Toast in the background state, please ensure that the application has the floating window permission, because on some mobile phones, even if there is a notification bar permission, it cannot display Toast in the background. For example, I use The HarmonyOS 2.0 test will not work, so it depends on how the product is considered. 169 | 170 | #### Android 11 cannot display Toast in the background 171 | 172 | * When we change the targetSdkVersion to 30 and above, we will find a problem. If the application is in the background process, and the Toast style of our application happens to be customized, then calling the show method of Toast in these cases will Surprisingly, Toast is not displayed. Please note that this problem is not a bug, but Android 11 prohibits this behavior. It is also noted in [Toast | Android Developers](https://developer.android.com/reference/android/widget/Toast#setView(android.view.View)), and it is not recommended to customize the style of Toast, and also tagged the `Toast.setView` method as `deprecated api`. 173 | 174 | * So how do we solve this problem? Is it really impossible to use custom style Toast? My answer is: Google only said that it cannot display custom Toast in the background, but it does not mean that it cannot be done in the foreground. The idea of ​​adapting the framework is that in the case of Android 11, it will first judge the current Toast Whether the application is in the foreground or the background, if it is in the foreground, it will display a custom-style Toast, if it is in the background, it will display a system-style Toast (by discarding the custom style to ensure that the Toast can be displayed normally), This can not only meet the requirements of Android 11, but also maximize the benefits of customized Toast. 175 | 176 | * It is worth noting that Toaster is currently the first and only framework of its kind to adapt to this feature of Android 11. 177 | 178 | #### Framework highlights 179 | 180 | * Take the lead: the first toast framework adapted to Android 11, developer do not need to care about the adaptation process 181 | 182 | * No permissions required: Regardless of whether the notification bar permission is granted or not, it does not affect the pop-up of the toast 183 | 184 | * Strong compatibility: Deal with the historical legacy of native Toast crashes in Android 7.1 185 | 186 | * Powerful functions: Toast can be popped up regardless of primary and secondary threads, and resource id and int type can be automatically identified 187 | 188 | * Easy to use: just pass in the text, and the duration of the toast display will be automatically determined according to the length of the text 189 | 190 | * Best performance: use lazy loading mode, only create Toast when displaying, do not take up Application startup time 191 | 192 | * Best experience: Displaying the next Toast will cancel the display of the previous Toast, so that it can be displayed immediately 193 | 194 | * Global unity: You can initialize the Toast style in the Application to achieve a once-and-for-all effect 195 | 196 | #### How to replace the existing native Toast in the project 197 | 198 | * Right-click the pop-up menu in the project, Replace in path, check the Regex option, and click Replace 199 | 200 | ```text 201 | Toast\.makeText\([^,]+,\s*(.+),\s*[^,]+\)\.show\(\) 202 | ``` 203 | 204 | ```text 205 | Toaster.show($1) 206 | ``` 207 | 208 | * Replace the package name 209 | 210 | ```text 211 | import android.widget.Toast 212 | ``` 213 | 214 | ```text 215 | import com.hjq.toast.Toaster 216 | ``` 217 | 218 | * Then search globally and manually replace some that have not been replaced successfully 219 | 220 | ```text 221 | Toast.makeText 222 | new Toast 223 | ``` 224 | 225 | #### Author's other open source projects 226 | 227 | * Android middle office: [AndroidProject](https://github.com/getActivity/AndroidProject)![](https://img.shields.io/github/stars/getActivity/AndroidProject.svg)![](https://img.shields.io/github/forks/getActivity/AndroidProject.svg) 228 | 229 | * Android middle office kt version: [AndroidProject-Kotlin](https://github.com/getActivity/AndroidProject-Kotlin)![](https://img.shields.io/github/stars/getActivity/AndroidProject-Kotlin.svg)![](https://img.shields.io/github/forks/getActivity/AndroidProject-Kotlin.svg) 230 | 231 | * Permissions framework: [XXPermissions](https://github.com/getActivity/XXPermissions) ![](https://img.shields.io/github/stars/getActivity/XXPermissions.svg) ![](https://img.shields.io/github/forks/getActivity/XXPermissions.svg) 232 | 233 | * Network framework: [EasyHttp](https://github.com/getActivity/EasyHttp)![](https://img.shields.io/github/stars/getActivity/EasyHttp.svg)![](https://img.shields.io/github/forks/getActivity/EasyHttp.svg) 234 | 235 | * Title bar framework: [TitleBar](https://github.com/getActivity/TitleBar)![](https://img.shields.io/github/stars/getActivity/TitleBar.svg)![](https://img.shields.io/github/forks/getActivity/TitleBar.svg) 236 | 237 | * Floating window framework: [EasyWindow](https://github.com/getActivity/EasyWindow)![](https://img.shields.io/github/stars/getActivity/EasyWindow.svg)![](https://img.shields.io/github/forks/getActivity/EasyWindow.svg) 238 | 239 | * Shape view framework: [ShapeView](https://github.com/getActivity/ShapeView)![](https://img.shields.io/github/stars/getActivity/ShapeView.svg)![](https://img.shields.io/github/forks/getActivity/ShapeView.svg) 240 | 241 | * Shape drawable framework: [ShapeDrawable](https://github.com/getActivity/ShapeDrawable)![](https://img.shields.io/github/stars/getActivity/ShapeDrawable.svg)![](https://img.shields.io/github/forks/getActivity/ShapeDrawable.svg) 242 | 243 | * Language switching framework: [Multi Languages](https://github.com/getActivity/MultiLanguages)![](https://img.shields.io/github/stars/getActivity/MultiLanguages.svg)![](https://img.shields.io/github/forks/getActivity/MultiLanguages.svg) 244 | 245 | * Gson parsing fault tolerance: [GsonFactory](https://github.com/getActivity/GsonFactory)![](https://img.shields.io/github/stars/getActivity/GsonFactory.svg)![](https://img.shields.io/github/forks/getActivity/GsonFactory.svg) 246 | 247 | * Logcat viewing framework: [Logcat](https://github.com/getActivity/Logcat)![](https://img.shields.io/github/stars/getActivity/Logcat.svg)![](https://img.shields.io/github/forks/getActivity/Logcat.svg) 248 | 249 | * Nested scrolling layout framework:[NestedScrollLayout](https://github.com/getActivity/NestedScrollLayout) ![](https://img.shields.io/github/stars/getActivity/NestedScrollLayout.svg) ![](https://img.shields.io/github/forks/getActivity/NestedScrollLayout.svg) 250 | 251 | * Android version guide: [AndroidVersionAdapter](https://github.com/getActivity/AndroidVersionAdapter)![](https://img.shields.io/github/stars/getActivity/AndroidVersionAdapter.svg)![](https://img.shields.io/github/forks/getActivity/AndroidVersionAdapter.svg) 252 | 253 | * Android code standard: [AndroidCodeStandard](https://github.com/getActivity/AndroidCodeStandard)![](https://img.shields.io/github/stars/getActivity/AndroidCodeStandard.svg)![](https://img.shields.io/github/forks/getActivity/AndroidCodeStandard.svg) 254 | 255 | * Android resource summary:[AndroidIndex](https://github.com/getActivity/AndroidIndex) ![](https://img.shields.io/github/stars/getActivity/AndroidIndex.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidIndex.svg) 256 | 257 | * Android open source leaderboard: [AndroidGithubBoss](https://github.com/getActivity/AndroidGithubBoss)![](https://img.shields.io/github/stars/getActivity/AndroidGithubBoss.svg)![](https://img.shields.io/github/forks/getActivity/AndroidGithubBoss.svg) 258 | 259 | * Studio boutique plugins: [StudioPlugins](https://github.com/getActivity/StudioPlugins)![](https://img.shields.io/github/stars/getActivity/StudioPlugins.svg)![](https://img.shields.io/github/forks/getActivity/StudioPlugins.svg) 260 | 261 | * Emoji collection: [emoji pa c shadow](https://github.com/getActivity/EmojiPackage)![](https://img.shields.io/github/stars/getActivity/EmojiPackage.svg)![](https://img.shields.io/github/forks/getActivity/EmojiPackage.svg) 262 | 263 | * China provinces json: [ProvinceJson](https://github.com/getActivity/ProvinceJson)![](https://img.shields.io/github/stars/getActivity/ProvinceJson.svg)![](https://img.shields.io/github/forks/getActivity/ProvinceJson.svg) 264 | 265 | * Markdown documentation:[MarkdownDoc](https://github.com/getActivity/MarkdownDoc) ![](https://img.shields.io/github/stars/getActivity/MarkdownDoc.svg) ![](https://img.shields.io/github/forks/getActivity/MarkdownDoc.svg) 266 | 267 | ## License 268 | 269 | ```text 270 | Copyright 2018 Huang JinQun 271 | 272 | Licensed under the Apache License, Version 2.0 (the "License"); 273 | you may not use this file except in compliance with the License. 274 | You may obtain a copy of the License at 275 | 276 | http://www.apache.org/licenses/LICENSE-2.0 277 | 278 | Unless required by applicable law or agreed to in writing, software 279 | distributed under the License is distributed on an "AS IS" BASIS, 280 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 281 | See the License for the specific language governing permissions and 282 | limitations under the License. 283 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [English Doc](README-en.md) 2 | 3 | # 吐司框架 4 | 5 | * 项目地址:[Github](https://github.com/getActivity/Toaster) 6 | 7 | * 博客地址:[只需体验三分钟,你就会跟我一样,爱上这款 Toast](https://www.jianshu.com/p/9b174ee2c571) 8 | 9 | * 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/Toaster/releases/download/12.8/Toaster.apk) 10 | 11 | ![](picture/zh/download_demo_apk_qr_code.png) 12 | 13 | ![](picture/zh/demo_toast_activity.jpg) ![](picture/zh/demo_toast_style_white.jpg) ![](picture/zh/demo_toast_style_black.jpg) 14 | 15 | ![](picture/zh/demo_toast_layout_info.jpg) ![](picture/zh/demo_toast_layout_warn.jpg) ![](picture/zh/demo_toast_layout_success.jpg) 16 | 17 | ![](picture/zh/demo_toast_layout_error.jpg) ![](picture/zh/demo_toast_layout_custom.jpg) 18 | 19 | #### 集成步骤 20 | 21 | * 如果你的项目 Gradle 配置是在 `7.0 以下`,需要在 `build.gradle` 文件中加入 22 | 23 | ```groovy 24 | allprojects { 25 | repositories { 26 | // JitPack 远程仓库:https://jitpack.io 27 | maven { url 'https://jitpack.io' } 28 | } 29 | } 30 | ``` 31 | 32 | * 如果你的 Gradle 配置是 `7.0 及以上`,则需要在 `settings.gradle` 文件中加入 33 | 34 | ```groovy 35 | dependencyResolutionManagement { 36 | repositories { 37 | // JitPack 远程仓库:https://jitpack.io 38 | maven { url 'https://jitpack.io' } 39 | } 40 | } 41 | ``` 42 | 43 | * 配置完远程仓库后,在项目 app 模块下的 `build.gradle` 文件中加入远程依赖 44 | 45 | ```groovy 46 | android { 47 | // 支持 JDK 1.8 48 | compileOptions { 49 | targetCompatibility JavaVersion.VERSION_1_8 50 | sourceCompatibility JavaVersion.VERSION_1_8 51 | } 52 | } 53 | 54 | dependencies { 55 | // 吐司框架:https://github.com/getActivity/Toaster 56 | implementation 'com.github.getActivity:Toaster:12.8' 57 | } 58 | ``` 59 | 60 | #### 初始化框架 61 | 62 | ```java 63 | public class XxxApplication extends Application { 64 | 65 | @Override 66 | public void onCreate() { 67 | super.onCreate(); 68 | 69 | // 初始化 Toast 框架 70 | Toaster.init(this); 71 | } 72 | } 73 | ``` 74 | 75 | #### 框架 API 介绍 76 | 77 | ```java 78 | // 显示 Toast 79 | Toaster.show(CharSequence text); 80 | Toaster.show(int id); 81 | Toaster.show(Object object); 82 | 83 | // debug 模式下显示 Toast 84 | Toaster.debugShow(CharSequence text); 85 | Toaster.debugShow(int id); 86 | Toaster.debugShow(Object object); 87 | 88 | // 延迟显示 Toast 89 | Toaster.delayedShow(CharSequence text, long delayMillis); 90 | Toaster.delayedShow(int id, long delayMillis); 91 | Toaster.delayedShow(Object object, long delayMillis); 92 | 93 | // 显示短 Toast 94 | Toaster.showShort(CharSequence text); 95 | Toaster.showShort(int id); 96 | Toaster.showShort(Object object); 97 | 98 | // 显示长 Toast 99 | Toaster.showLong(CharSequence text); 100 | Toaster.showLong(int id); 101 | Toaster.showLong(Object object); 102 | 103 | // 自定义显示 Toast 104 | Toaster.show(ToastParams params); 105 | 106 | // 取消 Toast 107 | Toaster.cancel(); 108 | 109 | // 设置 Toast 布局(全局生效) 110 | Toaster.setView(int id); 111 | 112 | // 设置 Toast 样式(全局生效) 113 | Toaster.setStyle(IToastStyle style); 114 | // 获取 Toast 样式 115 | Toaster.getStyle() 116 | 117 | // 判断当前框架是否已经初始化 118 | Toaster.isInit(); 119 | 120 | // 设置 Toast 策略(全局生效) 121 | Toaster.setStrategy(IToastStrategy strategy); 122 | // 获取 Toast 策略 123 | Toaster.getStrategy(); 124 | 125 | // 设置 Toast 重心和偏移 126 | Toaster.setGravity(int gravity); 127 | Toaster.setGravity(int gravity, int xOffset, int yOffset); 128 | 129 | // 设置 Toast 拦截器(全局生效) 130 | Toaster.setInterceptor(IToastInterceptor interceptor); 131 | // 获取 Toast 拦截器 132 | Toaster.getInterceptor(); 133 | ``` 134 | 135 | ## [常见疑问请点击此处查看](HelpDoc-zh.md) 136 | 137 | #### 不同 Toast 框架之间的对比 138 | 139 | | 功能或细节 | [Toaster](https://github.com/getActivity/Toaster) | [AndroidUtilCode-ToastUtils](https://github.com/Blankj/AndroidUtilCode) | [Toasty](https://github.com/GrenderG/Toasty) | 140 | | :----: | :------: | :-----: | :-----: | 141 | | 对应版本 | 12.8 | 1.30.6 | 1.5.0 | 142 | | issues 数 | [![](https://img.shields.io/github/issues/getActivity/Toaster.svg)](https://github.com/getActivity/Toaster/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) | 143 | | 框架体积 | 32 KB | 500 KB | 50 KB | 144 | | 框架维护状态 | 维护中 | 停止维护 | 停止维护 | 145 | | 调用代码定位 | ✅ | ❌ | ❌ | 146 | | 支持在子线程中调用显示 | ✅ | ✅ | ❌ | 147 | | 支持设置局部 Toast 样式 | ✅ | ❌ | ❌ | 148 | | 支持设置全局 Toast 样式 | ✅ | ❌ | ❌ | 149 | | 支持 Toast 即显即示 | ✅ | ✅ | ❌ | 150 | | 支持 Toast 排队显示 | ✅ | ❌ | ✅ | 151 | | 支持 Toast 延迟显示 | ✅ | ❌ | ❌ | 152 | | 处理 Toast 在 Android 7.1 崩溃的问题 | ✅ | ✅ | ❌ | 153 | | 兼容通知栏权限关闭后 Toast 显示不出来的问题 | ✅ | ✅ | ❌ | 154 | | 适配 Android 11 不能在后台显示 Toast 的问题 | ✅ | ❌ | ❌ | 155 | 156 | #### 调用代码定位功能介绍 157 | 158 | * 框架会在日志打印中输出在 Toast 调用的代码位置,这样开发者可以直接通过点击 Log 来定位是在哪个类哪行代码调用的,这样可以极大提升我们排查问题的效率,特别是 Toast 的内容是由服务器返回的情况下,我相信没有任何一个人会拒绝这样的功能。 159 | 160 | ![](picture/zh/demo_logcat_code.jpg) 161 | 162 | #### Toast 在 Android 7.1 崩溃的问题介绍 163 | 164 | > [Toast 在 Android 7.1 崩溃排查及修复](https://www.jianshu.com/p/437f473017d6) 165 | 166 | * 这个问题是由于 Android 7.1 加入 WindowToken 校验机制导致的,而这个 WindowToken 是 NotificationManagerService 生成的,这个 WindowToken 是存在一定时效性的,而当应用的主线程被阻塞时,WindowManager 在 addView 时会对 WindowToken 进行校验,但是 WindowToken 已经过期了,这个时候 addView 就会抛出异常。 167 | 168 | * 谷歌在 Android 8.0 就修复了这个问题,修复方式十分简单粗暴,就是直接捕获这个异常,而框架的修复思路跟谷歌类似,只不过修复方式不太一样,因为框架无法直接修改系统源码,所以是直接通过 Hook 的方式对异常进行捕获。 169 | 170 | #### 通知栏权限关闭后 Toast 显示不出来的问题介绍 171 | 172 | > [Toast通知栏权限填坑指南](https://www.jianshu.com/p/1d64a5ccbc7c) 173 | 174 | * 这个问题的出现是因为原生 Toast 的显示要通过 NMS(NotificationManagerService) 才会 addView 到 Window 上面,而在 NMS 中有一个 `static final boolean ENABLE_BLOCKED_TOASTS = true` 的字段,当这个常量值为 true 时,会触发 NMS 对应用通知栏权限的检查,如果没有通知栏权限,那么这个 Toast 将会被 NMS 所拦截,并输出 `Suppressing toast from package` 日志信息,而小米手机没有这个问题是因为它是将 `ENABLE_BLOCKED_TOASTS` 字段值修改成 `false`,所以就不会触发对通知栏权限的检查,另外我为什么会知道有这个事情?因为我曾经和一名 MIUI 工程师一起确认过这个事情。 175 | 176 | * 框架处理这个问题的方式有两种,先判断当前应用是否处于前台状态,如果是则使用自定义的 WindowManager 代替 Toast 来显示,如果当前应用处于后台状态,则会通过 Hook Toast 中的 INotificationManager 接口,将 enqueueToast 方法传递的包名参数修改成 `android` 来欺骗 NotificationManagerService,因为 NotificationManagerService 已经将 `android` 包名的应用纳入白名单,系统会自动放行,需要注意的是,这种方式在 Android 10 上面已经失效了,已经被系统纳入反射黑名单,但是好消息是,通过查看和对比 NotificationManagerService 源码发现,这个问题(关闭通知栏权限后无法在前台弹 Toast 的问题)已经在 Android 10.0 的版本上面被修复了,所以框架只在 Android 9.0 及以下版本并且在关闭了通知栏权限的情况下才去 Hook INotificationManager,另外我还找到了官方关于这块的代码提交记录:[Always allow toasts from foreground apps](https://cs.android.com/android/_/android/platform/frameworks/base/+/58b2453ed69197d765c7254241d9966ee49a3efb),大家可以感兴趣可以看看,还有一个问题,如果你想在 Android 10 及之后的版本仍然能在后台显示 Toast,请保证应用的通知栏权限或者悬浮窗权限处于开启的状态,如果你一定要求在后台状态下要 100% 能显示 Toast,请保证应用有悬浮窗权限,因为在某些厂商的手机上,就算有通知栏权限也是无法在后台显示 Toast,例如我用 HarmonyOS 2.0 测试就不行,所以具体要看产品怎么斟酌。 177 | 178 | #### Android 11 不能在后台显示 Toast 的问题介绍 179 | 180 | * 当我们将 targetSdkVersion 改成 30 及以上的版本时,会发现一个问题,如果应用处于后台进程的情况下,而恰好我们的应用 Toast 样式是经过定制的,那么在这些情况下调用 Toast 的 show 方法会惊奇的发现,Toast 没有显示出来,请注意这个问题不是 Bug,而是 Android 11 禁止了这种行为,在 [Toast 官方文档](https://developer.android.google.cn/reference/android/widget/Toast#setView(android.view.View)) 中也有注明,不建议对 Toast 的样式做定制化,并且还对 `Toast.setView` 方法进行了标记过时处理。 181 | 182 | * 那么我们如何解决这一问题呢?难道真的不能用自定义样式的 Toast 了?我的答案是:不,凡事不能一刀切,谷歌只说不能在后台显示自定义的 Toast,并不能代表不能在前台那么做,框架的适配思路是,在 Android 11 的情况下,会先判断当前应用是处于前台还是后台,如果是在前台的情况下就显示自定义样式的 Toast,如果是在后台的情况下就显示系统样式的 Toast(通过舍弃自定义样式来保证 Toast 能够正常显示出来),这样既能符合 Android 11 要求,同时又能将定制化 Toast 的权益最大化。 183 | 184 | * 值得注意的是:Toaster 是目前同类框架第一款也是唯一一款适配 Android 11 这一特性的框架。 185 | 186 | #### 框架亮点 187 | 188 | * 一马当先:首款适配 Android 11 的吐司框架,开发者无需关心适配过程 189 | 190 | * 无需权限:[不管有没有授予通知栏权限都不影响吐司的弹出](https://www.jianshu.com/p/1d64a5ccbc7c) 191 | 192 | * 兼容性强:[处理原生 Toast 在 Android 7.1 产生崩溃的历史遗留问题](https://www.jianshu.com/p/437f473017d6) 193 | 194 | * 功能强大:不分主次线程都可以弹出Toast,自动识别资源 id 和 int 类型 195 | 196 | * 使用简单:只需传入文本,会自动根据文本长度决定吐司显示的时长 197 | 198 | * 性能最佳:使用懒加载模式,只在显示时创建 Toast,不占用 Application 启动时间 199 | 200 | * 体验最佳:显示下一个 Toast 会取消上一个 Toast 的显示,真正做到即显即示 201 | 202 | * 全局统一:可以在 Application 中初始化 Toast 样式,达到一劳永逸的效果 203 | 204 | #### 如何替换项目中已有的原生 Toast 205 | 206 | * 在项目中右击弹出菜单,Replace in path,勾选 Regex 选项,点击替换 207 | 208 | ```text 209 | Toast\.makeText\([^,]+,\s*(.+),\s*[^,]+\)\.show\(\) 210 | ``` 211 | 212 | ```text 213 | Toaster.show($1) 214 | ``` 215 | 216 | * 对导包进行替换 217 | 218 | ```text 219 | import android.widget.Toast 220 | ``` 221 | 222 | ```text 223 | import com.hjq.toast.Toaster 224 | ``` 225 | 226 | * 再全局搜索,手动更换一些没有替换成功的 227 | 228 | ```text 229 | Toast.makeText 230 | new Toast 231 | ``` 232 | 233 | #### 作者的其他开源项目 234 | 235 | * 安卓技术中台:[AndroidProject](https://github.com/getActivity/AndroidProject) ![](https://img.shields.io/github/stars/getActivity/AndroidProject.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidProject.svg) 236 | 237 | * 安卓技术中台 Kt 版:[AndroidProject-Kotlin](https://github.com/getActivity/AndroidProject-Kotlin) ![](https://img.shields.io/github/stars/getActivity/AndroidProject-Kotlin.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidProject-Kotlin.svg) 238 | 239 | * 权限框架:[XXPermissions](https://github.com/getActivity/XXPermissions) ![](https://img.shields.io/github/stars/getActivity/XXPermissions.svg) ![](https://img.shields.io/github/forks/getActivity/XXPermissions.svg) 240 | 241 | * 网络框架:[EasyHttp](https://github.com/getActivity/EasyHttp) ![](https://img.shields.io/github/stars/getActivity/EasyHttp.svg) ![](https://img.shields.io/github/forks/getActivity/EasyHttp.svg) 242 | 243 | * 标题栏框架:[TitleBar](https://github.com/getActivity/TitleBar) ![](https://img.shields.io/github/stars/getActivity/TitleBar.svg) ![](https://img.shields.io/github/forks/getActivity/TitleBar.svg) 244 | 245 | * 悬浮窗框架:[EasyWindow](https://github.com/getActivity/EasyWindow) ![](https://img.shields.io/github/stars/getActivity/EasyWindow.svg) ![](https://img.shields.io/github/forks/getActivity/EasyWindow.svg) 246 | 247 | * ShapeView 框架:[ShapeView](https://github.com/getActivity/ShapeView) ![](https://img.shields.io/github/stars/getActivity/ShapeView.svg) ![](https://img.shields.io/github/forks/getActivity/ShapeView.svg) 248 | 249 | * ShapeDrawable 框架:[ShapeDrawable](https://github.com/getActivity/ShapeDrawable) ![](https://img.shields.io/github/stars/getActivity/ShapeDrawable.svg) ![](https://img.shields.io/github/forks/getActivity/ShapeDrawable.svg) 250 | 251 | * 语种切换框架:[MultiLanguages](https://github.com/getActivity/MultiLanguages) ![](https://img.shields.io/github/stars/getActivity/MultiLanguages.svg) ![](https://img.shields.io/github/forks/getActivity/MultiLanguages.svg) 252 | 253 | * Gson 解析容错:[GsonFactory](https://github.com/getActivity/GsonFactory) ![](https://img.shields.io/github/stars/getActivity/GsonFactory.svg) ![](https://img.shields.io/github/forks/getActivity/GsonFactory.svg) 254 | 255 | * 日志查看框架:[Logcat](https://github.com/getActivity/Logcat) ![](https://img.shields.io/github/stars/getActivity/Logcat.svg) ![](https://img.shields.io/github/forks/getActivity/Logcat.svg) 256 | 257 | * 嵌套滚动布局框架:[NestedScrollLayout](https://github.com/getActivity/NestedScrollLayout) ![](https://img.shields.io/github/stars/getActivity/NestedScrollLayout.svg) ![](https://img.shields.io/github/forks/getActivity/NestedScrollLayout.svg) 258 | 259 | * Android 版本适配:[AndroidVersionAdapter](https://github.com/getActivity/AndroidVersionAdapter) ![](https://img.shields.io/github/stars/getActivity/AndroidVersionAdapter.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidVersionAdapter.svg) 260 | 261 | * Android 代码规范:[AndroidCodeStandard](https://github.com/getActivity/AndroidCodeStandard) ![](https://img.shields.io/github/stars/getActivity/AndroidCodeStandard.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidCodeStandard.svg) 262 | 263 | * Android 资源大汇总:[AndroidIndex](https://github.com/getActivity/AndroidIndex) ![](https://img.shields.io/github/stars/getActivity/AndroidIndex.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidIndex.svg) 264 | 265 | * Android 开源排行榜:[AndroidGithubBoss](https://github.com/getActivity/AndroidGithubBoss) ![](https://img.shields.io/github/stars/getActivity/AndroidGithubBoss.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidGithubBoss.svg) 266 | 267 | * Studio 精品插件:[StudioPlugins](https://github.com/getActivity/StudioPlugins) ![](https://img.shields.io/github/stars/getActivity/StudioPlugins.svg) ![](https://img.shields.io/github/forks/getActivity/StudioPlugins.svg) 268 | 269 | * 表情包大集合:[EmojiPackage](https://github.com/getActivity/EmojiPackage) ![](https://img.shields.io/github/stars/getActivity/EmojiPackage.svg) ![](https://img.shields.io/github/forks/getActivity/EmojiPackage.svg) 270 | 271 | * AI 资源大汇总:[AiIndex](https://github.com/getActivity/AiIndex) ![](https://img.shields.io/github/stars/getActivity/AiIndex.svg) ![](https://img.shields.io/github/forks/getActivity/AiIndex.svg) 272 | 273 | * 省市区 Json 数据:[ProvinceJson](https://github.com/getActivity/ProvinceJson) ![](https://img.shields.io/github/stars/getActivity/ProvinceJson.svg) ![](https://img.shields.io/github/forks/getActivity/ProvinceJson.svg) 274 | 275 | * Markdown 语法文档:[MarkdownDoc](https://github.com/getActivity/MarkdownDoc) ![](https://img.shields.io/github/stars/getActivity/MarkdownDoc.svg) ![](https://img.shields.io/github/forks/getActivity/MarkdownDoc.svg) 276 | 277 | #### 微信公众号:Android轮子哥 278 | 279 | ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/official_ccount.png) 280 | 281 | #### Android 技术 Q 群:10047167 282 | 283 | #### 如果您觉得我的开源库帮你节省了大量的开发时间,请扫描下方的二维码随意打赏,要是能打赏个 10.24 :monkey_face:就太:thumbsup:了。您的支持将鼓励我继续创作:octocat:([点击查看捐赠列表](https://github.com/getActivity/Donate)) 284 | 285 | ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_ali.png) ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_wechat.png) 286 | 287 | ## License 288 | 289 | ```text 290 | Copyright 2018 Huang JinQun 291 | 292 | Licensed under the Apache License, Version 2.0 (the "License"); 293 | you may not use this file except in compliance with the License. 294 | You may obtain a copy of the License at 295 | 296 | http://www.apache.org/licenses/LICENSE-2.0 297 | 298 | Unless required by applicable law or agreed to in writing, software 299 | distributed under the License is distributed on an "AS IS" BASIS, 300 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 301 | See the License for the specific language governing permissions and 302 | limitations under the License. 303 | ``` -------------------------------------------------------------------------------- /app/AppSignature.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getActivity/Toaster/96044bfb45887238bc57d849e570d8326c78eb31/app/AppSignature.jks -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 34 5 | 6 | defaultConfig { 7 | applicationId "com.hjq.toast.demo" 8 | minSdkVersion 16 9 | targetSdkVersion 34 10 | versionCode 1280 11 | versionName "12.8" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | 15 | // 使用 JDK 1.8 16 | compileOptions { 17 | targetCompatibility JavaVersion.VERSION_1_8 18 | sourceCompatibility JavaVersion.VERSION_1_8 19 | } 20 | 21 | // Apk 签名的那些事:https://www.jianshu.com/p/a1f8e5896aa2 22 | signingConfigs { 23 | config { 24 | storeFile file(StoreFile) 25 | storePassword StorePassword 26 | keyAlias KeyAlias 27 | keyPassword KeyPassword 28 | } 29 | } 30 | 31 | buildTypes { 32 | debug { 33 | minifyEnabled false 34 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 35 | signingConfig signingConfigs.config 36 | } 37 | 38 | release { 39 | minifyEnabled true 40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 41 | signingConfig signingConfigs.config 42 | } 43 | } 44 | 45 | applicationVariants.configureEach { variant -> 46 | // apk 输出文件名配置 47 | variant.outputs.configureEach { output -> 48 | outputFileName = rootProject.getName() + '.apk' 49 | } 50 | } 51 | } 52 | 53 | dependencies { 54 | // 依赖 libs 目录下所有的 jar 和 aar 包 55 | implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') 56 | 57 | implementation project(':library') 58 | 59 | // AndroidX 库:https://github.com/androidx/androidx 60 | implementation 'androidx.appcompat:appcompat:1.4.0' 61 | // Material 库:https://github.com/material-components/material-components-android 62 | implementation 'com.google.android.material:material:1.4.0' 63 | 64 | // 标题栏框架:https://github.com/getActivity/TitleBar 65 | implementation 'com.github.getActivity:TitleBar:10.5' 66 | 67 | // 权限请求框架:https://github.com/getActivity/XXPermissions 68 | implementation 'com.github.getActivity:XXPermissions:20.0' 69 | 70 | // 悬浮窗框架:https://github.com/getActivity/EasyWindow 71 | implementation 'com.github.getActivity:EasyWindow:10.3' 72 | 73 | // 内存泄漏捕捉:https://github.com/square/leakcanary 74 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' 75 | } -------------------------------------------------------------------------------- /app/gradle.properties: -------------------------------------------------------------------------------- 1 | StoreFile = AppSignature.jks 2 | StorePassword = AndroidProject 3 | KeyAlias = AndroidProject 4 | KeyPassword = AndroidProject -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.hjq.toast.** {*;} -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/hjq/toast/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hjq.toast.demo; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.util.TypedValue; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import com.google.android.material.snackbar.Snackbar; 12 | import com.hjq.bar.OnTitleBarListener; 13 | import com.hjq.bar.TitleBar; 14 | import com.hjq.permissions.Permission; 15 | import com.hjq.permissions.XXPermissions; 16 | import com.hjq.toast.ToastParams; 17 | import com.hjq.toast.ToastStrategy; 18 | import com.hjq.toast.Toaster; 19 | import com.hjq.toast.style.BlackToastStyle; 20 | import com.hjq.toast.style.CustomToastStyle; 21 | import com.hjq.toast.style.WhiteToastStyle; 22 | import com.hjq.window.EasyWindow; 23 | 24 | /** 25 | * author : Android 轮子哥 26 | * github : https://github.com/getActivity/Toaster 27 | * time : 2018/09/01 28 | * desc : Toaster 使用案例 29 | */ 30 | public final class MainActivity extends AppCompatActivity { 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_main); 36 | 37 | TitleBar titleBar = findViewById(R.id.tb_main_bar); 38 | titleBar.setOnTitleBarListener(new OnTitleBarListener() { 39 | @Override 40 | public void onTitleClick(TitleBar titleBar) { 41 | Intent intent = new Intent(Intent.ACTION_VIEW); 42 | intent.setData(Uri.parse(titleBar.getTitle().toString())); 43 | startActivity(intent); 44 | } 45 | }); 46 | } 47 | 48 | public void showToast(View v) { 49 | Toaster.show(R.string.demo_show_toast_result); 50 | } 51 | 52 | public void showShortToast(View v) { 53 | Toaster.showShort(R.string.demo_show_short_toast_result); 54 | } 55 | 56 | public void showLongToast(View v) { 57 | Toaster.showLong(R.string.demo_show_long_toast_result); 58 | } 59 | 60 | public void showCrossPageToast(View v) { 61 | ToastParams params = new ToastParams(); 62 | params.text = getString(R.string.demo_show_cross_page_toast_result); 63 | params.crossPageShow = true; 64 | Toaster.show(params); 65 | } 66 | 67 | public void delayShowToast(View v) { 68 | Toaster.delayedShow(R.string.demo_show_toast_with_two_second_delay_result, 2000); 69 | } 70 | 71 | @SuppressWarnings("AlibabaAvoidManuallyCreateThread") 72 | public void threadShowToast(View v) { 73 | new Thread(new Runnable() { 74 | 75 | @Override 76 | public void run() { 77 | Toaster.show(R.string.demo_show_toast_in_the_subthread_result); 78 | } 79 | }).start(); 80 | } 81 | 82 | public void switchToastStyleToWhite(View v) { 83 | ToastParams params = new ToastParams(); 84 | params.text = getString(R.string.demo_switch_to_white_style_result); 85 | params.style = new WhiteToastStyle(); 86 | Toaster.show(params); 87 | } 88 | 89 | public void switchToastStyleToBlack(View v) { 90 | ToastParams params = new ToastParams(); 91 | params.text = getString(R.string.demo_switch_to_black_style_result); 92 | params.style = new BlackToastStyle(); 93 | Toaster.show(params); 94 | } 95 | 96 | public void switchToastStyleToInfo(View v) { 97 | ToastParams params = new ToastParams(); 98 | params.text = getString(R.string.demo_switch_to_info_style_result); 99 | params.style = new CustomToastStyle(R.layout.toast_info); 100 | Toaster.show(params); 101 | } 102 | 103 | public void switchToastStyleToWarn(View v) { 104 | ToastParams params = new ToastParams(); 105 | params.text = getString(R.string.demo_switch_to_warn_style_result); 106 | params.style = new CustomToastStyle(R.layout.toast_warn); 107 | Toaster.show(params); 108 | } 109 | 110 | public void switchToastStyleToSuccess(View v) { 111 | ToastParams params = new ToastParams(); 112 | params.text = getString(R.string.demo_switch_to_success_style_result); 113 | params.style = new CustomToastStyle(R.layout.toast_success); 114 | Toaster.show(params); 115 | } 116 | 117 | public void switchToastStyleToError(View v) { 118 | ToastParams params = new ToastParams(); 119 | params.text = getString(R.string.demo_switch_to_error_style_result); 120 | params.style = new CustomToastStyle(R.layout.toast_error); 121 | Toaster.show(params); 122 | } 123 | 124 | public void customGlobalToastStyle(View v) { 125 | Toaster.setView(R.layout.toast_custom_view); 126 | Toaster.setGravity(Gravity.CENTER); 127 | Toaster.show(R.string.demo_custom_toast_layout_result); 128 | } 129 | 130 | public void switchToastStrategy(View v) { 131 | Toaster.setStrategy(new ToastStrategy(ToastStrategy.SHOW_STRATEGY_TYPE_QUEUE)); 132 | Toaster.show(R.string.demo_switch_to_toast_queuing_strategy_result); 133 | findViewById(R.id.tv_main_thrice_show).setVisibility(View.VISIBLE); 134 | } 135 | 136 | public void showThriceToast(View v) { 137 | for (int i = 0; i < 3; i++) { 138 | Toaster.show(String.format(getString(R.string.demo_show_three_toast_copywriting), i + 1)); 139 | } 140 | } 141 | 142 | public void toBackgroundShowToast(View v) { 143 | Snackbar.make(getWindow().getDecorView(), getString(R.string.demo_show_toast_in_background_state_hint), Snackbar.LENGTH_SHORT).show(); 144 | 145 | v.postDelayed(new Runnable() { 146 | @Override 147 | public void run() { 148 | Snackbar.make(getWindow().getDecorView(), getString(R.string.demo_show_toast_in_background_state_snack_bar), Snackbar.LENGTH_SHORT).show(); 149 | } 150 | }, 2000); 151 | 152 | v.postDelayed(new Runnable() { 153 | @Override 154 | public void run() { 155 | Intent intent = new Intent(Intent.ACTION_MAIN); 156 | intent.addCategory(Intent.CATEGORY_HOME); 157 | startActivity(intent); 158 | } 159 | }, 4000); 160 | 161 | v.postDelayed(new Runnable() { 162 | @Override 163 | public void run() { 164 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 165 | if (XXPermissions.isGranted(MainActivity.this, Permission.SYSTEM_ALERT_WINDOW)) { 166 | Toaster.show(R.string.demo_show_toast_in_background_state_result_1); 167 | } else { 168 | Toaster.show(R.string.demo_show_toast_in_background_state_result_2); 169 | } 170 | } else { 171 | Toaster.show(R.string.demo_show_toast_in_background_state_result_3); 172 | } 173 | } 174 | }, 5000); 175 | } 176 | 177 | public void combinationEasyWindowShow(View v) { 178 | new EasyWindow<>(this) 179 | .setDuration(1000) 180 | // 将 Toaster 中的 View 转移给 EasyWindow 来显示 181 | .setContentView(Toaster.getStyle().createView(getApplication())) 182 | .setAnimStyle(android.R.style.Animation_Translucent) 183 | .setText(android.R.id.message, R.string.demo_combining_window_framework_use_result) 184 | .setGravity(Gravity.BOTTOM) 185 | .setYOffset((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics())) 186 | .show(); 187 | } 188 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hjq/toast/demo/ToastApplication.java: -------------------------------------------------------------------------------- 1 | package com.hjq.toast.demo; 2 | 3 | import android.app.Application; 4 | 5 | import com.hjq.toast.Toaster; 6 | 7 | /** 8 | * author : Android 轮子哥 9 | * github : https://github.com/getActivity/Toaster 10 | * time : 2018/09/01 11 | * desc : Toaster 初始化 12 | */ 13 | public final class ToastApplication extends Application { 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | 19 | // 初始化 Toast 框架 20 | Toaster.init(this); 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_error_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_error_ic.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_hint_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_info_ic.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_success_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_success_ic.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_warn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toast_warn_ic.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 22 | 23 | 26 | 27 | 32 | 33 |