├── .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 ├── LICENSE ├── README.md ├── app ├── AppSignature.jks ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── hjq │ │ └── window │ │ └── demo │ │ ├── AppApplication.java │ │ ├── DemoAdapter.java │ │ ├── IOSToast.java │ │ └── MainActivity.java │ └── res │ ├── anim │ ├── window_bottom_in.xml │ ├── window_bottom_out.xml │ ├── window_ios_in.xml │ ├── window_ios_out.xml │ ├── window_left_in.xml │ ├── window_left_out.xml │ ├── window_right_in.xml │ ├── window_right_out.xml │ ├── window_scale_in.xml │ ├── window_scale_out.xml │ ├── window_top_in.xml │ └── window_top_out.xml │ ├── drawable-xhdpi │ ├── ic_close.png │ ├── ic_dialog_tip_error.png │ ├── ic_dialog_tip_finish.png │ ├── ic_dialog_tip_warning.png │ └── ic_float_bg.png │ ├── drawable │ └── dialog_toast_bg.xml │ ├── layout │ ├── activity_main.xml │ ├── window_hint.xml │ ├── window_input.xml │ ├── window_list.xml │ ├── window_list_item.xml │ ├── window_phone.xml │ └── window_web.xml │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v23 │ └── styles.xml │ ├── values-v28 │ └── styles.xml │ ├── values │ ├── colors.xml │ ├── integers.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── network_security_config.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── hjq │ └── window │ ├── EasyWindow.java │ ├── OnWindowLayoutInflateListener.java │ ├── OnWindowLifecycleCallback.java │ ├── OnWindowVIewTouchListener.java │ ├── OnWindowViewClickListener.java │ ├── OnWindowViewLongClickListener.java │ ├── ScreenOrientationMonitor.java │ ├── ViewClickListenerWrapper.java │ ├── ViewLongClickListenerWrapper.java │ ├── ViewTouchListenerWrapper.java │ ├── WindowLifecycleControl.java │ ├── WindowRootLayout.java │ └── draggable │ ├── AbstractWindowDraggableRule.java │ ├── MovingWindowDraggableRule.java │ └── SpringBackWindowDraggableRule.java ├── logo.png ├── picture ├── demo_code.png └── dynamic_figure.gif └── 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 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2019 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 2019 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.md: -------------------------------------------------------------------------------- 1 | # 悬浮窗框架 2 | 3 | * 项目地址:[Github](https://github.com/getActivity/EasyWindow) 4 | 5 | * 博客地址:[悬浮窗需求终结者](https://www.jianshu.com/p/247d705b87b6) 6 | 7 | * 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/EasyWindow/releases/download/12.0/EasyWindow.apk) 8 | 9 | ![](picture/demo_code.png) 10 | 11 | ![](picture/dynamic_figure.gif) 12 | 13 | #### 本框架意在解决一些悬浮窗的需求,如果是普通的 Toast 封装推荐使用 [Toaster](https://github.com/getActivity/Toaster) 14 | 15 | #### 集成步骤 16 | 17 | * 如果你的项目 Gradle 配置是在 `7.0 以下`,需要在 `build.gradle` 文件中加入 18 | 19 | ```groovy 20 | allprojects { 21 | repositories { 22 | // JitPack 远程仓库:https://jitpack.io 23 | maven { url 'https://jitpack.io' } 24 | } 25 | } 26 | ``` 27 | 28 | * 如果你的 Gradle 配置是 `7.0 及以上`,则需要在 `settings.gradle` 文件中加入 29 | 30 | ```groovy 31 | dependencyResolutionManagement { 32 | repositories { 33 | // JitPack 远程仓库:https://jitpack.io 34 | maven { url 'https://jitpack.io' } 35 | } 36 | } 37 | ``` 38 | 39 | * 配置完远程仓库后,在项目 app 模块下的 `build.gradle` 文件中加入远程依赖 40 | 41 | ```groovy 42 | android { 43 | // 支持 JDK 1.8 44 | compileOptions { 45 | targetCompatibility JavaVersion.VERSION_1_8 46 | sourceCompatibility JavaVersion.VERSION_1_8 47 | } 48 | } 49 | 50 | dependencies { 51 | // 悬浮窗框架:https://github.com/getActivity/EasyWindow 52 | implementation 'com.github.getActivity:EasyWindow:12.0' 53 | } 54 | ``` 55 | 56 | #### AndroidX 兼容 57 | 58 | * 如果项目是基于 **AndroidX** 包,请在项目 `gradle.properties` 文件中加入 59 | 60 | ```text 61 | # 表示将第三方库迁移到 AndroidX 62 | android.enableJetifier = true 63 | ``` 64 | 65 | #### 使用案例 66 | 67 | * Java 用法 68 | 69 | ```java 70 | // 传入 Activity 对象表示设置成局部的,不需要有悬浮窗权限 71 | // 传入 Application 对象表示设置成全局的,但需要有悬浮窗权限 72 | EasyWindow.with(this) 73 | .setContentView(R.layout.toast_hint) 74 | // 设置成可拖拽的 75 | //.setWindowDraggableRule() 76 | // 设置显示时长 77 | .setWindowDuration(1000) 78 | // 设置动画样式 79 | //.setAnimStyle(android.R.style.Animation_Translucent) 80 | // 设置外层是否能被触摸 81 | //.setOutsideTouchable(false) 82 | // 设置窗口背景阴影强度 83 | //.setBackgroundDimAmount(0.5f) 84 | .setImageDrawableByImageView(android.R.id.icon, R.mipmap.ic_dialog_tip_finish) 85 | .setTextByTextView(android.R.id.message, "点我消失") 86 | .setOnClickListenerByView(android.R.id.message, new OnWindowViewClickListener() { 87 | 88 | @Override 89 | public void onClick(@NonNull EasyWindow easyWindow, @NonNull TextView view) { 90 | // 有两种方式取消弹窗: 91 | // 1. easyWindow.cancel:顾名思义,取消显示 92 | // 2. easyWindow.recycle:在取消显示的基础上,加上了回收 93 | // 这两种区别在于,cancel 之后还能 show,但是 recycle 之后不能再 show 94 | // 通常情况下,如果你创建的 EasyWindow 对象在 cancel 之后永远不会再显示,取消弹窗建议直接用 recycle 方法,否则用 cancel 方法 95 | easyWindow.recycle(); 96 | // 跳转到某个Activity 97 | // easyWindow.startActivity(intent); 98 | } 99 | }) 100 | .show(); 101 | ``` 102 | 103 | * Kotlin 用法(二选一) 104 | 105 | ```kotlin 106 | EasyWindow.with(activity).apply { 107 | setContentView(R.layout.toast_hint) 108 | // 设置成可拖拽的 109 | //setWindowDraggableRule() 110 | // 设置显示时长 111 | setWindowDuration(1000) 112 | // 设置动画样式 113 | //setAnimStyle(android.R.style.Animation_Translucent) 114 | // 设置外层是否能被触摸 115 | //setOutsideTouchable(false) 116 | // 设置窗口背景阴影强度 117 | //setBackgroundDimAmount(0.5f) 118 | setImageDrawableByImageView(android.R.id.icon, R.mipmap.ic_dialog_tip_finish) 119 | setTextByTextView(android.R.id.message, "点我消失") 120 | setOnClickListenerByView(android.R.id.message, OnWindowViewClickListener { easyWindow: EasyWindow<*>, view: TextView -> 121 | // 有两种方式取消弹窗: 122 | // 1. easyWindow.cancel:顾名思义,取消显示 123 | // 2. easyWindow.recycle:在取消显示的基础上,加上了回收 124 | // 这两种区别在于,cancel 之后还能 show,但是 recycle 之后不能再 show 125 | // 通常情况下,如果你创建的 EasyWindow 对象在 cancel 之后永远不会再显示,取消弹窗建议直接用 recycle 方法,否则用 cancel 方法 126 | easyWindow.recycle() 127 | // 跳转到某个Activity 128 | // easyWindow.startActivity(intent) 129 | }) 130 | }.show() 131 | ``` 132 | 133 | ```kotlin 134 | EasyWindow.with(activity) 135 | .setContentView(R.layout.toast_hint) 136 | // 设置成可拖拽的 137 | //.setWindowDraggableRule() 138 | // 设置显示时长 139 | .setWindowDuration(1000) 140 | // 设置动画样式 141 | //.setAnimStyle(android.R.style.Animation_Translucent) 142 | // 设置外层是否能被触摸 143 | //.setOutsideTouchable(false) 144 | // 设置窗口背景阴影强度 145 | //.setBackgroundDimAmount(0.5f) 146 | .setImageDrawableByImageView(android.R.id.icon, R.mipmap.ic_dialog_tip_finish) 147 | .setTextByTextView(android.R.id.message, "点我消失") 148 | .setOnClickListenerByView(android.R.id.message, OnWindowViewClickListener { easyWindow: EasyWindow<*>, view: TextView -> 149 | // 有两种方式取消弹窗: 150 | // 1. easyWindow.cancel:顾名思义,取消显示 151 | // 2. easyWindow.recycle:在取消显示的基础上,加上了回收 152 | // 这两种区别在于,cancel 之后还能 show,但是 recycle 之后不能再 show 153 | // 通常情况下,如果你创建的 EasyWindow 对象在 cancel 之后永远不会再显示,取消弹窗建议直接用 recycle 方法,否则用 cancel 方法 154 | easyWindow.recycle() 155 | // 跳转到某个Activity 156 | // easyWindow.startActivity(intent) 157 | }) 158 | .show() 159 | ``` 160 | 161 | #### 没有悬浮窗权限如何全局显示? 162 | 163 | * 没有悬浮窗权限是不能全局显示在其他应用上的,但是全局显示在自己的应用上是可以实现的 164 | 165 | * 但是当前 Activity 创建的悬浮窗只能在当前 Activity 上面显示,如果想在所有的 Activity 都显示需要做特殊处理 166 | 167 | * 我们可以通过 Application 来监听所有 Activity 的生命周期方法,然后在每个 Activity.onCreate 时创建悬浮窗 168 | 169 | ```java 170 | public final class WindowLifecycleControl implements Application.ActivityLifecycleCallbacks { 171 | 172 | static void with(Application application) { 173 | application.registerActivityLifecycleCallbacks(new FloatingLifecycle()); 174 | } 175 | 176 | @Override 177 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 178 | EasyWindow.with(activity) 179 | .setView(R.layout.xxx) 180 | .show(); 181 | } 182 | 183 | @Override 184 | public void onActivityStarted(Activity activity) {} 185 | 186 | @Override 187 | public void onActivityResumed(Activity activity) {} 188 | 189 | @Override 190 | public void onActivityPaused(Activity activity) {} 191 | 192 | @Override 193 | public void onActivityStopped(Activity activity) {} 194 | 195 | @Override 196 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} 197 | 198 | @Override 199 | public void onActivityDestroyed(Activity activity) {} 200 | } 201 | ``` 202 | 203 | #### LeakCanary 一直报内存泄漏怎么办? 204 | 205 | * 这个问题是 EasyWindow 取消显示之后,没有回收资源才会出现的,目前比较好的解决方案是增加一个取消监听,然后在里面调用 `recycle` 方法即可 206 | 207 | ```java 208 | EasyWindow.with(this) 209 | .setOnWindowLifecycleCallback(new OnWindowLifecycleCallback() { 210 | 211 | @Override 212 | public void onWindowCancel(@NonNull EasyWindow easyWindow) { 213 | // 在窗口消失的时候回收资源,避免 LeakCanary 一直报内存泄漏 214 | easyWindow.recycle(); 215 | } 216 | }) 217 | .show(); 218 | ``` 219 | 220 | * 到这里你可能会有一个疑惑,这样的代码为什么框架不内部进行处理?而是交给外层开发者去处理,这难道不是脱裤子放屁?在这里我觉得有必要解释一下,原因有以下两点: 221 | 222 | 1. 不是框架不会处理,而是不能处理,因为窗口取消的时候,直接帮你回收窗口资源,如果你在外层持有了 `easyWindow` 对象,并且调用了相关的方法,会导致不会奏效,例如你取消窗口只是暂时的,等下还要恢复显示,你如果复用了 `easyWindow` 对象并且调用了 `easyWindow.show` 方法后窗口是不会显示,这是因为你前面调用了 `easyWindow.recycle`,窗口所有的资源已经被回收了,不能再次展示了,你调用了也是无效,至于要不要在窗口取消的会调用中调用回收窗口资源的办法,这个要取决你在外层有没有复用 `easyWindow` 对象去做什么事,如果没有就可以放心大胆调用 `easyWindow.recycle`,如果有的话,则不行。 223 | 224 | 2. 框架内部其实有做对窗口资源回收的动作,框架内部会通过 `registerActivityLifecycleCallbacks` 监听 `Activity` 生命周期,发现有 `Activity` 销毁后,会查找这个上下文相关的窗口对象,然后进行调用 `easyWindow.recycle` 对窗口资源进行回收,那这样为什么 `LeakCanary` 还会报内存泄漏?这是因为 `LeakCanary` 在 `Activity` 还没有结束的时候就急不可耐地去检查对象是否出现了内存泄漏,所以才出现了这个问题,作为框架作者我表示很受伤,至于处理方案你其实可以参考一下第一点。 225 | 226 | #### 框架的 API 介绍 227 | 228 | * 对象方法 229 | 230 | ```java 231 | // 显示悬浮窗 232 | easyWindow.show(); 233 | // 将悬浮窗显示在指定 View 的旁边 234 | easyWindow.showAsDropDown(@NonNull View anchorView, int showGravity, int xOff, int yOff); 235 | // 取消显示悬浮窗 236 | easyWindow.cancel(); 237 | // 取消显示并回收悬浮窗 238 | easyWindow.recycle(); 239 | // 更新悬浮窗(在更新了悬浮窗参数才需要调用) 240 | easyWindow.update(); 241 | // 延迟更新悬浮窗(可在子线程中调用,不怕频繁调用) 242 | easyWindow.postUpdate(); 243 | // 当前悬浮窗是否正在显示 244 | easyWindow.isShowing(); 245 | 246 | // 设置窗口生命周期回调监听 247 | easyWindow.setOnWindowLifecycleCallback(@Nullable OnWindowLifecycleCallback callback); 248 | // 设置悬浮窗拖拽规则(框架内部提供了两种拖拽规则,MovingWindowDraggableRule 和 SpringBackWindowDraggableRule ) 249 | easyWindow.setWindowDraggableRule(@Nullable AbstractWindowDraggableRule draggableRule); 250 | // 设置悬浮窗拖拽规则(可能为空) 251 | easyWindow.getWindowDraggableRule(); 252 | 253 | // 设置悬浮窗内容布局 254 | easyWindow.setContentView(@LayoutRes int layoutId); 255 | easyWindow.setContentView(@LayoutRes int layoutId, @Nullable OnWindowLayoutInflateListener listener); 256 | easyWindow.setContentView(@NonNull View view); 257 | // 获取内容布局(可能为空) 258 | easyWindow.getContentView(); 259 | // 限定悬浮窗显示时长 260 | easyWindow.setWindowDuration(@IntRange(from = 0) int delayMillis); 261 | // 设置悬浮窗 tag 262 | easyWindow.setTag(@Nullable String tag); 263 | // 获取悬浮窗 tag 264 | easyWindow.getTag(); 265 | // 设置悬浮窗宽度 266 | easyWindow.setWidth(int width); 267 | // 设置悬浮窗高度 268 | easyWindow.setHeight(int height); 269 | 270 | // 设置悬浮窗显示的重心 271 | easyWindow.setGravity(int gravity); 272 | // 设置水平偏移量 273 | easyWindow.setXOffset(int px); 274 | // 设置垂直偏移量 275 | easyWindow.setYOffset(int px); 276 | 277 | // 设置悬浮窗外层是否可触摸 278 | easyWindow.setOutsideTouchable(boolean touchable); 279 | // 设置悬浮窗背景阴影强度 280 | easyWindow.setBackgroundDimAmount(@FloatRange(from = 0.0, to = 1.0) float amount); 281 | 282 | // 添加窗口标记 283 | easyWindow.addWindowFlags(int flags); 284 | // 移除窗口标记 285 | easyWindow.removeWindowFlags(int flags); 286 | // 设置窗口标记 287 | easyWindow.setWindowFlags(int flags); 288 | // 是否存在某个窗口标记 289 | easyWindow.hasWindowFlags(int flags); 290 | // 设置悬浮窗的显示类型 291 | easyWindow.setWindowType(int type); 292 | 293 | // 设置悬浮窗动画样式 294 | easyWindow.setAnimStyle(int id); 295 | // 设置悬浮窗软键盘模式 296 | easyWindow.setSoftInputMode(int softInputMode); 297 | // 设置悬浮窗 Token 298 | easyWindow.setWindowToken(@Nullable IBinder token); 299 | 300 | // 设置悬浮窗透明度 301 | easyWindow.setWindowAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha); 302 | // 设置容器和窗口小部件之间的垂直边距 303 | easyWindow.setVerticalMargin(float verticalMargin); 304 | // 设置容器和窗口小部件之间的水平边距 305 | easyWindow.setHorizontalMargin(float horizontalMargin); 306 | // 设置位图格式 307 | easyWindow.setBitmapFormat(int format); 308 | // 设置状态栏的可见性 309 | easyWindow.setSystemUiVisibility(int systemUiVisibility); 310 | // 设置垂直权重 311 | easyWindow.setVerticalWeight(float verticalWeight); 312 | // 设置挖孔屏下的显示模式 313 | easyWindow.setLayoutInDisplayCutoutMode(int layoutInDisplayCutoutMode); 314 | // 设置悬浮窗在哪个显示屏上显示 315 | easyWindow.setPreferredDisplayModeId(int preferredDisplayModeId); 316 | // 设置悬浮窗标题 317 | easyWindow.setWindowTitle(@Nullable CharSequence title); 318 | // 设置屏幕的亮度 319 | easyWindow.setScreenBrightness(@FloatRange(from = -1.0, to = 1.0) float screenBrightness); 320 | // 设置键盘背光的亮度 321 | easyWindow.setButtonBrightness(@FloatRange(from = -1.0, to = 1.0) float buttonBrightness); 322 | // 设置悬浮窗的刷新率 323 | easyWindow.setPreferredRefreshRate(float preferredRefreshRate); 324 | // 设置悬浮窗的颜色模式 325 | easyWindow.setColorMode(int colorMode); 326 | // 设置悬浮窗背后的高斯模糊半径大小(Android 12 及以上才支持) 327 | easyWindow.setBlurBehindRadius(@IntRange(from = 0) int blurBehindRadius); 328 | // 设置悬浮窗屏幕方向 329 | easyWindow.setScreenOrientation(int screenOrientation); 330 | 331 | // 设置悬浮窗可见性 332 | easyWindow.setWindowVisibility(int visibility); 333 | // 获取悬浮窗可见性 334 | easyWindow.getWindowVisibility(); 335 | // 设置悬浮窗根布局(一般情况下推荐使用 {@link #setContentView} 方法来填充布局) 336 | easyWindow.setWindowRootLayout(@NonNull ViewGroup viewGroup); 337 | // 重新设置 WindowManager 参数集 338 | easyWindow.setWindowParams(@NonNull WindowManager.LayoutParams params); 339 | // 重新设置 WindowManager 对象 340 | easyWindow.setWindowManager(@NonNull WindowManager windowManager); 341 | 342 | // 获取当前窗口内容宽度 343 | easyWindow.getWindowContentWidth(); 344 | // 获取当前窗口内容高度 345 | easyWindow.getWindowContentHeight(); 346 | 347 | // 设置可见性状态给 View 348 | easyWindow.setVisibilityByView(@IdRes int viewId, int visibility); 349 | // 设置背景 Drawable 给 View 350 | easyWindow.setBackgroundDrawableByView(@IdRes int viewId, @DrawableRes int drawableId); 351 | easyWindow.setBackgroundDrawableByView(@IdRes int viewId, @Nullable Drawable drawable); 352 | // 设置文本给 TextView 353 | easyWindow.setTextByTextView(@IdRes int viewId, @StringRes int stringId); 354 | easyWindow.setTextByTextView(@IdRes int viewId, @Nullable CharSequence text); 355 | // 设置字体颜色给 TextView 356 | easyWindow.setTextColorByTextView(@IdRes int viewId, @ColorInt int colorValue); 357 | // 设置字体大小给 TextView 358 | easyWindow.setTextSizeByTextView(@IdRes int viewId, float textSize); 359 | easyWindow.setTextSizeByTextView(@IdRes int viewId, int unit, float textSize); 360 | // 设置提示文本给 TextView 361 | easyWindow.setHintTextByTextView(@IdRes int viewId, @StringRes int stringId); 362 | easyWindow.setHintTextByTextView(@IdRes int viewId, @Nullable CharSequence text); 363 | // 设置提示文本颜色给 TextView 364 | easyWindow.setHintTextColorByTextView(@IdRes int viewId, @ColorInt int colorValue); 365 | // 设置图片 Drawable 给 ImageView 366 | easyWindow.setImageDrawableByImageView(@IdRes int viewId, @DrawableRes int drawableId); 367 | easyWindow.setImageDrawableByImageView(@IdRes int viewId, @Nullable Drawable drawable); 368 | 369 | // 跳转 Activity 370 | easyWindow.startActivity(@Nullable Class clazz); 371 | easyWindow.startActivity(@Nullable Intent intent); 372 | 373 | // 延迟执行任务 374 | easyWindow.post(Runnable runnable); 375 | // 延迟一段时间执行任务 376 | easyWindow.postDelayed(@NonNull Runnable runnable, long delayMillis); 377 | // 在指定的时间执行任务 378 | easyWindow.postAtTime(@NonNull Runnable runnable, long uptimeMillis); 379 | // 移除指定的任务 380 | easyWindow.removeRunnable(@NonNull Runnable runnable); 381 | // 移除所有的任务 382 | easyWindow.removeAllRunnable(); 383 | ``` 384 | 385 | * 静态方法 386 | 387 | ```java 388 | // 取消所有正在显示的悬浮窗 389 | EasyWindow.cancelAllWindow(); 390 | // 取消特定类名的悬浮窗 391 | EasyWindow.cancelWindowByClass(@Nullable Class> clazz); 392 | // 取消特定标记的悬浮窗 393 | EasyWindow.cancelWindowByTag(@Nullable String tag); 394 | 395 | // 显示所有已取消但未回收的悬浮窗 396 | EasyWindow.showAllWindow(); 397 | // 显示特定类名已取消但未回收的悬浮窗 398 | EasyWindow.showWindowByClass(@Nullable Class> clazz); 399 | // 显示特定标记已取消但未回收的悬浮窗 400 | EasyWindow.showWindowByTag(@Nullable String tag); 401 | 402 | // 回收所有正在显示的悬浮窗 403 | EasyWindow.recycleAllWindow(); 404 | // 回收特定类名的悬浮窗 405 | EasyWindow.recycleWindowByClass(@Nullable Class> clazz); 406 | // 回收特定标记的悬浮窗 407 | EasyWindow.recycleWindowByTag(@Nullable String tag); 408 | 409 | // 判断当前是否有悬浮窗正在显示 410 | EasyWindow.existAnyWindowShowing(); 411 | // 判断当前是否有特定类名的悬浮窗正在显示 412 | EasyWindow.existWindowShowingByClass(@Nullable Class> clazz); 413 | // 判断当前是否有特定标记的悬浮窗正在显示 414 | EasyWindow.existWindowShowingByTag(@Nullable String tag); 415 | 416 | // 获取所有的悬浮窗 417 | EasyWindow.getAllWindowInstance(); 418 | // 获取特定类名的悬浮窗 419 | EasyWindow.getWindowInstanceByClass(@Nullable Class> clazz); 420 | // 获取特定标记的悬浮窗 421 | EasyWindow.getWindowInstanceByTag(@Nullable String tag); 422 | ``` 423 | 424 | #### 作者的其他开源项目 425 | 426 | * 安卓技术中台:[AndroidProject](https://github.com/getActivity/AndroidProject) ![](https://img.shields.io/github/stars/getActivity/AndroidProject.svg) ![](https://img.shields.io/github/forks/getActivity/AndroidProject.svg) 427 | 428 | * 安卓技术中台 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) 429 | 430 | * 权限框架:[XXPermissions](https://github.com/getActivity/XXPermissions) ![](https://img.shields.io/github/stars/getActivity/XXPermissions.svg) ![](https://img.shields.io/github/forks/getActivity/XXPermissions.svg) 431 | 432 | * 吐司框架:[Toaster](https://github.com/getActivity/Toaster) ![](https://img.shields.io/github/stars/getActivity/Toaster.svg) ![](https://img.shields.io/github/forks/getActivity/Toaster.svg) 433 | 434 | * 网络框架:[EasyHttp](https://github.com/getActivity/EasyHttp) ![](https://img.shields.io/github/stars/getActivity/EasyHttp.svg) ![](https://img.shields.io/github/forks/getActivity/EasyHttp.svg) 435 | 436 | * 标题栏框架:[TitleBar](https://github.com/getActivity/TitleBar) ![](https://img.shields.io/github/stars/getActivity/TitleBar.svg) ![](https://img.shields.io/github/forks/getActivity/TitleBar.svg) 437 | 438 | * 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) 439 | 440 | * 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) 441 | 442 | * 语种切换框架:[MultiLanguages](https://github.com/getActivity/MultiLanguages) ![](https://img.shields.io/github/stars/getActivity/MultiLanguages.svg) ![](https://img.shields.io/github/forks/getActivity/MultiLanguages.svg) 443 | 444 | * 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) 445 | 446 | * 日志查看框架:[Logcat](https://github.com/getActivity/Logcat) ![](https://img.shields.io/github/stars/getActivity/Logcat.svg) ![](https://img.shields.io/github/forks/getActivity/Logcat.svg) 447 | 448 | * 嵌套滚动布局框架:[NestedScrollLayout](https://github.com/getActivity/NestedScrollLayout) ![](https://img.shields.io/github/stars/getActivity/NestedScrollLayout.svg) ![](https://img.shields.io/github/forks/getActivity/NestedScrollLayout.svg) 449 | 450 | * 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) 451 | 452 | * 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) 453 | 454 | * 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) 455 | 456 | * 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) 457 | 458 | * 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) 459 | 460 | * 表情包大集合:[EmojiPackage](https://github.com/getActivity/EmojiPackage) ![](https://img.shields.io/github/stars/getActivity/EmojiPackage.svg) ![](https://img.shields.io/github/forks/getActivity/EmojiPackage.svg) 461 | 462 | * 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) 463 | 464 | * 省市区 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) 465 | 466 | * 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) 467 | 468 | #### 微信公众号:Android轮子哥 469 | 470 | ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/official_ccount.png) 471 | 472 | #### Android 技术 Q 群:10047167 473 | 474 | #### 如果您觉得我的开源库帮你节省了大量的开发时间,请扫描下方的二维码随意打赏,要是能打赏个 10.24 :monkey_face:就太:thumbsup:了。您的支持将鼓励我继续创作:octocat:([点击查看捐赠列表](https://github.com/getActivity/Donate)) 475 | 476 | ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_ali.png) ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_wechat.png) 477 | 478 | ## License 479 | 480 | ```text 481 | Copyright 2019 Huang JinQun 482 | 483 | Licensed under the Apache License, Version 2.0 (the "License"); 484 | you may not use this file except in compliance with the License. 485 | You may obtain a copy of the License at 486 | 487 | http://www.apache.org/licenses/LICENSE-2.0 488 | 489 | Unless required by applicable law or agreed to in writing, software 490 | distributed under the License is distributed on an "AS IS" BASIS, 491 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 492 | See the License for the specific language governing permissions and 493 | limitations under the License. 494 | ``` -------------------------------------------------------------------------------- /app/AppSignature.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getActivity/EasyWindow/7a92473074c2feaa5f2bfeef43d2a43146f90562/app/AppSignature.jks -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 33 5 | 6 | defaultConfig { 7 | applicationId "com.hjq.window.demo" 8 | minSdkVersion 16 9 | targetSdkVersion 28 10 | versionCode 1200 11 | versionName "12.0" 12 | } 13 | 14 | // 支持 Java JDK 8 15 | compileOptions { 16 | targetCompatibility JavaVersion.VERSION_1_8 17 | sourceCompatibility JavaVersion.VERSION_1_8 18 | } 19 | 20 | // Apk 签名的那些事:https://www.jianshu.com/p/a1f8e5896aa2 21 | signingConfigs { 22 | config { 23 | storeFile file(StoreFile) 24 | storePassword StorePassword 25 | keyAlias KeyAlias 26 | keyPassword KeyPassword 27 | } 28 | } 29 | 30 | buildTypes { 31 | debug { 32 | minifyEnabled false 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | signingConfig signingConfigs.config 35 | } 36 | 37 | release { 38 | minifyEnabled true 39 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 40 | signingConfig signingConfigs.config 41 | } 42 | } 43 | 44 | applicationVariants.configureEach { variant -> 45 | // apk 输出文件名配置 46 | variant.outputs.configureEach { output -> 47 | outputFileName = rootProject.getName() + '.apk' 48 | } 49 | } 50 | } 51 | 52 | dependencies { 53 | // 依赖 libs 目录下所有的 jar 和 aar 包 54 | implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') 55 | 56 | implementation project(':library') 57 | 58 | implementation 'com.android.support:appcompat-v7:28.0.0' 59 | implementation 'com.android.support:design:28.0.0' 60 | 61 | // 权限请求框架:https://github.com/getActivity/XXPermissions 62 | implementation 'com.github.getActivity:XXPermissions:21.3' 63 | 64 | // 标题栏框架:https://github.com/getActivity/TitleBar 65 | implementation 'com.github.getActivity:TitleBar:10.6' 66 | 67 | // 吐司框架:https://github.com/getActivity/Toaster 68 | implementation 'com.github.getActivity:Toaster:12.8' 69 | 70 | // 内存泄漏捕捉:https://github.com/square/leakcanary 71 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' 72 | } -------------------------------------------------------------------------------- /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.window.** {*;} -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/hjq/window/demo/AppApplication.java: -------------------------------------------------------------------------------- 1 | package com.hjq.window.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/EasyWindow 10 | * time : 2021/01/24 11 | * desc : 应用入口 12 | */ 13 | public final class AppApplication extends Application { 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | Toaster.init(this); 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hjq/window/demo/DemoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hjq.window.demo; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.View.OnClickListener; 9 | import android.view.View.OnLongClickListener; 10 | import android.view.ViewGroup; 11 | import android.widget.TextView; 12 | import com.hjq.window.demo.DemoAdapter.DemoViewHolder; 13 | import java.util.List; 14 | 15 | public class DemoAdapter extends RecyclerView.Adapter { 16 | 17 | private final List mDataList; 18 | 19 | /** 条目点击监听器 */ 20 | @Nullable 21 | private OnItemClickListener mItemClickListener; 22 | 23 | /** 条目长按监听器 */ 24 | @Nullable 25 | private OnItemLongClickListener mItemLongClickListener; 26 | 27 | public DemoAdapter(List dataList) { 28 | mDataList = dataList; 29 | } 30 | 31 | @NonNull 32 | @Override 33 | public DemoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 34 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.window_list_item, parent, false); 35 | return new DemoViewHolder(view); 36 | } 37 | 38 | @Override 39 | public void onBindViewHolder(@NonNull DemoViewHolder holder, int position) { 40 | String data = mDataList.get(position); 41 | holder.mTextview.setText(data); 42 | } 43 | 44 | @Override 45 | public int getItemCount() { 46 | return mDataList.size(); 47 | } 48 | 49 | public class DemoViewHolder extends RecyclerView.ViewHolder 50 | implements OnClickListener, OnLongClickListener { 51 | 52 | private final TextView mTextview; 53 | 54 | public DemoViewHolder(@NonNull View itemView) { 55 | super(itemView); 56 | mTextview = itemView.findViewById(R.id.tv_window_list_item_text); 57 | itemView.setOnClickListener(this); 58 | itemView.setOnLongClickListener(this); 59 | } 60 | 61 | /** 62 | * {@link View.OnClickListener} 63 | */ 64 | 65 | @Override 66 | public void onClick(View view) { 67 | int position = getLayoutPosition(); 68 | if (position < 0 || position >= getItemCount()) { 69 | return; 70 | } 71 | 72 | if (view == getItemView()) { 73 | if (mItemClickListener != null) { 74 | mItemClickListener.onItemClick(view, position); 75 | } 76 | } 77 | } 78 | 79 | /** 80 | * {@link View.OnLongClickListener} 81 | */ 82 | @Override 83 | public boolean onLongClick(View view) { 84 | int position = getLayoutPosition(); 85 | if (position < 0 || position >= getItemCount()) { 86 | return false; 87 | } 88 | 89 | if (view == getItemView()) { 90 | if (mItemLongClickListener != null) { 91 | return mItemLongClickListener.onItemLongClick(view, position); 92 | } 93 | return false; 94 | } 95 | 96 | return false; 97 | } 98 | 99 | 100 | public final View getItemView() { 101 | return itemView; 102 | } 103 | } 104 | 105 | /** 106 | * 设置 RecyclerView 条目点击监听 107 | */ 108 | public void setOnItemClickListener(@Nullable OnItemClickListener listener) { 109 | mItemClickListener = listener; 110 | } 111 | 112 | /** 113 | * 设置 RecyclerView 条目长按监听 114 | */ 115 | public void setOnItemLongClickListener(@Nullable OnItemLongClickListener listener) { 116 | mItemLongClickListener = listener; 117 | } 118 | 119 | /** 120 | * RecyclerView 条目点击监听类 121 | */ 122 | public interface OnItemClickListener{ 123 | 124 | /** 125 | * 当 RecyclerView 某个条目被点击时回调 126 | * 127 | * @param itemView 被点击的条目对象 128 | * @param position 被点击的条目位置 129 | */ 130 | void onItemClick(View itemView, int position); 131 | } 132 | 133 | /** 134 | * RecyclerView 条目长按监听类 135 | */ 136 | public interface OnItemLongClickListener { 137 | 138 | /** 139 | * 当 RecyclerView 某个条目被长按时回调 140 | * 141 | * @param itemView 被点击的条目对象 142 | * @param position 被点击的条目位置 143 | * @return 是否拦截事件 144 | */ 145 | boolean onItemLongClick(View itemView, int position); 146 | } 147 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hjq/window/demo/IOSToast.java: -------------------------------------------------------------------------------- 1 | package com.hjq.window.demo; 2 | 3 | import android.app.Activity; 4 | 5 | import com.hjq.window.EasyWindow; 6 | 7 | /** 8 | * author : Android 轮子哥 9 | * github : https://github.com/getActivity/EasyWindow 10 | * time : 2019/01/04 11 | * desc : 仿 IOS 弹框 12 | */ 13 | public final class IOSToast { 14 | 15 | private static final int TIME = 3000; 16 | 17 | public static void showSucceed(Activity activity, CharSequence text) { 18 | EasyWindow.with(activity) 19 | .setWindowDuration(TIME) 20 | .setContentView(R.layout.window_hint) 21 | .setAnimStyle(android.R.style.Animation_Translucent) 22 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_finish) 23 | .setTextByTextView(android.R.id.message, text) 24 | .show(); 25 | } 26 | 27 | public static void showFail(Activity activity, CharSequence text) { 28 | EasyWindow.with(activity) 29 | .setWindowDuration(TIME) 30 | .setContentView(R.layout.window_hint) 31 | .setAnimStyle(android.R.style.Animation_Activity) 32 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_error) 33 | .setTextByTextView(android.R.id.message, text) 34 | .show(); 35 | } 36 | 37 | public static void showWarn(Activity activity, CharSequence text) { 38 | EasyWindow.with(activity) 39 | .setWindowDuration(TIME) 40 | .setContentView(R.layout.window_hint) 41 | .setAnimStyle(android.R.style.Animation_Dialog) 42 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_warning) 43 | .setTextByTextView(android.R.id.message, text) 44 | .show(); 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hjq/window/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hjq.window.demo; 2 | 3 | import android.animation.Animator; 4 | import android.app.Application; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.annotation.Nullable; 11 | import android.support.design.widget.Snackbar; 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.support.v7.widget.LinearLayoutManager; 14 | import android.support.v7.widget.RecyclerView; 15 | import android.util.Log; 16 | import android.view.Gravity; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | import android.view.WindowManager; 20 | import android.webkit.WebSettings; 21 | import android.webkit.WebView; 22 | import android.webkit.WebViewClient; 23 | import android.widget.ImageView; 24 | import android.widget.TextView; 25 | import com.hjq.bar.OnTitleBarListener; 26 | import com.hjq.bar.TitleBar; 27 | import com.hjq.permissions.OnPermissionCallback; 28 | import com.hjq.permissions.Permission; 29 | import com.hjq.permissions.XXPermissions; 30 | import com.hjq.toast.Toaster; 31 | import com.hjq.window.EasyWindow; 32 | import com.hjq.window.OnWindowLayoutInflateListener; 33 | import com.hjq.window.OnWindowLifecycleCallback; 34 | import com.hjq.window.OnWindowViewClickListener; 35 | import com.hjq.window.OnWindowViewLongClickListener; 36 | import com.hjq.window.demo.DemoAdapter.OnItemClickListener; 37 | import com.hjq.window.demo.DemoAdapter.OnItemLongClickListener; 38 | import com.hjq.window.draggable.AbstractWindowDraggableRule.OnWindowDraggingListener; 39 | import com.hjq.window.draggable.MovingWindowDraggableRule; 40 | import com.hjq.window.draggable.SpringBackWindowDraggableRule; 41 | import com.hjq.window.draggable.SpringBackWindowDraggableRule.SpringBackAnimCallback; 42 | import java.util.ArrayList; 43 | import java.util.List; 44 | 45 | /** 46 | * author : Android 轮子哥 47 | * github : https://github.com/getActivity/EasyWindow 48 | * time : 2019/01/04 49 | * desc : Demo 使用案例 50 | */ 51 | public final class MainActivity extends AppCompatActivity implements View.OnClickListener { 52 | 53 | private static final String TAG = "EasyWindow"; 54 | 55 | @Override 56 | protected void onCreate(Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | setContentView(R.layout.activity_main); 59 | 60 | findViewById(R.id.btn_main_anim).setOnClickListener(this); 61 | findViewById(R.id.btn_main_duration).setOnClickListener(this); 62 | findViewById(R.id.btn_main_overlay).setOnClickListener(this); 63 | findViewById(R.id.btn_main_lifecycle).setOnClickListener(this); 64 | findViewById(R.id.btn_main_view).setOnClickListener(this); 65 | findViewById(R.id.btn_main_input).setOnClickListener(this); 66 | findViewById(R.id.btn_main_web).setOnClickListener(this); 67 | findViewById(R.id.btn_main_list).setOnClickListener(this); 68 | findViewById(R.id.btn_main_draggable).setOnClickListener(this); 69 | findViewById(R.id.btn_main_global).setOnClickListener(this); 70 | findViewById(R.id.btn_main_utils).setOnClickListener(this); 71 | findViewById(R.id.btn_main_cancel_all).setOnClickListener(this); 72 | 73 | TitleBar titleBar = findViewById(R.id.tb_main_bar); 74 | titleBar.setOnTitleBarListener(new OnTitleBarListener() { 75 | @Override 76 | public void onTitleClick(TitleBar titleBar) { 77 | Intent intent = new Intent(Intent.ACTION_VIEW); 78 | intent.setData(Uri.parse(titleBar.getTitle().toString())); 79 | startActivity(intent); 80 | } 81 | }); 82 | } 83 | 84 | @Override 85 | public void onBackPressed() { 86 | // 在某些手机上面无法通过返回键销毁当前 Activity 对象,从而无法触发 LeakCanary 回收对象 87 | finish(); 88 | } 89 | 90 | /** @noinspection unchecked*/ 91 | @Override 92 | public void onClick(View v) { 93 | int viewId = v.getId(); 94 | if (viewId == R.id.btn_main_anim) { 95 | 96 | EasyWindow.with(this) 97 | .setWindowDuration(1000) 98 | .setContentView(R.layout.window_hint) 99 | .setAnimStyle(R.style.TopAnimStyle) 100 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_finish) 101 | .setTextByTextView(android.R.id.message, "这个动画是不是很骚") 102 | .setOnWindowLifecycleCallback(new OnWindowLifecycleCallback() { 103 | 104 | @Override 105 | public void onWindowCancel(@NonNull EasyWindow easyWindow) { 106 | // 在窗口消失的时候回收资源,避免 LeakCanary 一直报内存泄漏,关于这点框架文档有介绍,详情请看文档 107 | easyWindow.recycle(); 108 | } 109 | }) 110 | .show(); 111 | 112 | } else if (viewId == R.id.btn_main_duration) { 113 | 114 | EasyWindow.with(this) 115 | .setWindowDuration(1000) 116 | .setContentView(R.layout.window_hint) 117 | .setAnimStyle(R.style.IOSAnimStyle) 118 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_error) 119 | .setTextByTextView(android.R.id.message, "一秒后自动消失") 120 | .setOnWindowLifecycleCallback(new OnWindowLifecycleCallback() { 121 | 122 | @Override 123 | public void onWindowCancel(@NonNull EasyWindow easyWindow) { 124 | // 在窗口消失的时候回收资源,避免 LeakCanary 一直报内存泄漏,关于这点框架文档有介绍,详情请看文档 125 | easyWindow.recycle(); 126 | } 127 | }) 128 | .show(); 129 | 130 | } else if (viewId == R.id.btn_main_overlay) { 131 | 132 | EasyWindow.with(this) 133 | .setContentView(R.layout.window_hint) 134 | .setAnimStyle(R.style.IOSAnimStyle) 135 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_finish) 136 | .setTextByTextView(android.R.id.message, "点我消失") 137 | // 设置外层是否能被触摸 138 | .setOutsideTouchable(false) 139 | // 设置窗口背景阴影强度 140 | .setBackgroundDimAmount(0.5f) 141 | .setOnClickListenerByView(android.R.id.message, new OnWindowViewClickListener() { 142 | 143 | @Override 144 | public void onClick(@NonNull EasyWindow easyWindow, @NonNull TextView view) { 145 | cancelAndRecycleEasyWindow(easyWindow); 146 | } 147 | }) 148 | .show(); 149 | 150 | } else if (viewId == R.id.btn_main_lifecycle) { 151 | 152 | EasyWindow.with(this) 153 | .setWindowDuration(3000) 154 | .setContentView(R.layout.window_hint) 155 | .setAnimStyle(R.style.IOSAnimStyle) 156 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_warning) 157 | .setTextByTextView(android.R.id.message, "请注意下方 Snackbar") 158 | .setOnWindowLifecycleCallback(new OnWindowLifecycleCallback() { 159 | 160 | @Override 161 | public void onWindowShow(@NonNull EasyWindow easyWindow) { 162 | Snackbar.make(getWindow().getDecorView(), "显示回调", Snackbar.LENGTH_SHORT).show(); 163 | } 164 | 165 | @Override 166 | public void onWindowCancel(@NonNull EasyWindow easyWindow) { 167 | Snackbar.make(getWindow().getDecorView(), "消失回调", Snackbar.LENGTH_SHORT).show(); 168 | // 在窗口消失的时候回收资源,避免 LeakCanary 一直报内存泄漏,关于这点框架文档有介绍,详情请看文档 169 | easyWindow.recycle(); 170 | } 171 | }) 172 | .show(); 173 | 174 | } else if (viewId == R.id.btn_main_view) { 175 | 176 | EasyWindow.with(this) 177 | .setContentView(R.layout.window_hint) 178 | .setAnimStyle(R.style.RightAnimStyle) 179 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_finish) 180 | .setWindowDuration(2000) 181 | .setTextByTextView(android.R.id.message, "位置算得准不准") 182 | .setOnClickListenerByView(android.R.id.message, new OnWindowViewClickListener() { 183 | 184 | @Override 185 | public void onClick(final @NonNull EasyWindow easyWindow, @NonNull TextView view) { 186 | cancelAndRecycleEasyWindow(easyWindow); 187 | } 188 | }) 189 | .showAsDropDown(v, Gravity.BOTTOM); 190 | 191 | } else if (viewId == R.id.btn_main_input) { 192 | 193 | EasyWindow.with(this) 194 | .setContentView(R.layout.window_input) 195 | .setAnimStyle(R.style.BottomAnimStyle) 196 | .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) 197 | .setOnClickListenerByView(R.id.tv_window_close, new OnWindowViewClickListener() { 198 | 199 | @Override 200 | public void onClick(final @NonNull EasyWindow easyWindow, @NonNull TextView view) { 201 | cancelAndRecycleEasyWindow(easyWindow); 202 | } 203 | }) 204 | .show(); 205 | 206 | } else if (viewId == R.id.btn_main_web) { 207 | 208 | EasyWindow.with(this) 209 | .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) 210 | .setContentView(R.layout.window_web, new OnWindowLayoutInflateListener() { 211 | 212 | @Override 213 | public void onWindowLayoutInflateFinished(@NonNull EasyWindow easyWindow, @Nullable View view, int layoutId, @NonNull ViewGroup parentView) { 214 | WebView webView = view.findViewById(R.id.wv_window_web_content); 215 | WebSettings settings = webView.getSettings(); 216 | // 允许文件访问 217 | settings.setAllowFileAccess(true); 218 | // 允许网页定位 219 | settings.setGeolocationEnabled(true); 220 | // 允许保存密码 221 | //settings.setSavePassword(true); 222 | // 开启 JavaScript 223 | settings.setJavaScriptEnabled(true); 224 | // 允许网页弹对话框 225 | settings.setJavaScriptCanOpenWindowsAutomatically(true); 226 | // 加快网页加载完成的速度,等页面完成再加载图片 227 | settings.setLoadsImagesAutomatically(true); 228 | // 本地 DOM 存储(解决加载某些网页出现白板现象) 229 | settings.setDomStorageEnabled(true); 230 | // 解决 Android 5.0 上 WebView 默认不允许加载 Http 与 Https 混合内容 231 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 232 | settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); 233 | } 234 | 235 | webView.setWebViewClient(new WebViewClient() { 236 | 237 | @Override 238 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 239 | view.loadUrl(url); 240 | return true; 241 | } 242 | }); 243 | 244 | webView.loadUrl("https://github.com/getActivity/EasyWindow"); 245 | } 246 | }) 247 | .setAnimStyle(R.style.IOSAnimStyle) 248 | // 设置成可拖拽的 249 | .setWindowDraggableRule(new MovingWindowDraggableRule()) 250 | .setOnClickListenerByView(R.id.iv_window_web_close, new OnWindowViewClickListener() { 251 | 252 | @Override 253 | public void onClick(@NonNull EasyWindow easyWindow, @NonNull ImageView view) { 254 | cancelAndRecycleEasyWindow(easyWindow); 255 | } 256 | }) 257 | .setOnLongClickListenerByView(R.id.iv_window_web_close, new OnWindowViewLongClickListener() { 258 | @Override 259 | public boolean onLongClick(@NonNull EasyWindow easyWindow, @NonNull View view) { 260 | Toaster.show("关闭按钮被长按了"); 261 | return false; 262 | } 263 | }) 264 | .show(); 265 | 266 | } else if (viewId == R.id.btn_main_list) { 267 | 268 | EasyWindow.with(this) 269 | .setContentView(R.layout.window_list, new OnWindowLayoutInflateListener() { 270 | @Override 271 | public void onWindowLayoutInflateFinished(@NonNull EasyWindow easyWindow, @Nullable View view, int layoutId, @NonNull ViewGroup parentView) { 272 | RecyclerView recyclerView = view.findViewById(R.id.rv_window_list_view); 273 | recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext())); 274 | 275 | List dataList = new ArrayList<>(); 276 | for (int i = 1; i <= 20; i++) { 277 | dataList.add("我是条目 " + i); 278 | } 279 | 280 | DemoAdapter adapter = new DemoAdapter(dataList); 281 | adapter.setOnItemClickListener(new OnItemClickListener() { 282 | 283 | @Override 284 | public void onItemClick(View itemView, int position) { 285 | Toaster.show("条目 " + (position + 1) + " 被点击了"); 286 | } 287 | }); 288 | adapter.setOnItemLongClickListener(new OnItemLongClickListener() { 289 | @Override 290 | public boolean onItemLongClick(View itemView, int position) { 291 | Toaster.show("条目 " + (position + 1) + " 被长按了"); 292 | return false; 293 | } 294 | }); 295 | recyclerView.setAdapter(adapter); 296 | } 297 | }) 298 | .setAnimStyle(R.style.IOSAnimStyle) 299 | // 设置成可拖拽的 300 | .setWindowDraggableRule(new MovingWindowDraggableRule()) 301 | .setOnClickListenerByView(R.id.iv_window_list_close, new OnWindowViewClickListener() { 302 | 303 | @Override 304 | public void onClick(@NonNull EasyWindow easyWindow, @NonNull ImageView view) { 305 | cancelAndRecycleEasyWindow(easyWindow); 306 | } 307 | }) 308 | .setOnLongClickListenerByView(R.id.iv_window_list_close, new OnWindowViewLongClickListener() { 309 | @Override 310 | public boolean onLongClick(@NonNull EasyWindow easyWindow, @NonNull View view) { 311 | Toaster.show("关闭按钮被长按了"); 312 | return false; 313 | } 314 | }) 315 | .show(); 316 | 317 | } else if (viewId == R.id.btn_main_draggable) { 318 | 319 | EasyWindow.with(this) 320 | .setContentView(R.layout.window_hint) 321 | .setAnimStyle(R.style.IOSAnimStyle) 322 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_finish) 323 | .setTextByTextView(android.R.id.message, "点我消失") 324 | // 设置成可拖拽的 325 | .setWindowDraggableRule(new MovingWindowDraggableRule()) 326 | .setOnClickListenerByView(android.R.id.message, new OnWindowViewClickListener() { 327 | 328 | @Override 329 | public void onClick(@NonNull EasyWindow easyWindow, @NonNull TextView view) { 330 | cancelAndRecycleEasyWindow(easyWindow); 331 | } 332 | }) 333 | .show(); 334 | 335 | } else if (viewId == R.id.btn_main_global) { 336 | 337 | XXPermissions.with(MainActivity.this) 338 | .permission(Permission.SYSTEM_ALERT_WINDOW) 339 | .request(new OnPermissionCallback() { 340 | 341 | @Override 342 | public void onGranted(@NonNull List permissions, boolean allGranted) { 343 | // 这里最好要做一下延迟显示,因为在某些手机(华为鸿蒙 3.0)上面立即显示会导致显示效果有一些瑕疵 344 | runOnUiThread(() -> showGlobalWindow(getApplication())); 345 | } 346 | 347 | @Override 348 | public void onDenied(@NonNull List permissions, boolean doNotAskAgain) { 349 | EasyWindow.with(MainActivity.this) 350 | .setWindowDuration(1000) 351 | .setContentView(R.layout.window_hint) 352 | .setImageDrawableByImageView(android.R.id.icon, R.drawable.ic_dialog_tip_error) 353 | .setTextByTextView(android.R.id.message, "请先授予悬浮窗权限") 354 | .show(); 355 | } 356 | }); 357 | 358 | } else if (viewId == R.id.btn_main_cancel_all) { 359 | 360 | // 关闭当前正在显示的悬浮窗 361 | // EasyWindow.cancelAll(); 362 | // 回收当前正在显示的悬浮窗 363 | EasyWindow.recycleAllWindow(); 364 | 365 | } else if (viewId == R.id.btn_main_utils) { 366 | 367 | EasyWindow.with(this) 368 | .setWindowDuration(1000) 369 | // 将 Toaster 中的 View 转移给 EasyWindow 来显示 370 | .setContentView(Toaster.getStyle().createView(this)) 371 | .setAnimStyle(R.style.ScaleAnimStyle) 372 | .setTextByTextView(android.R.id.message, "就问你溜不溜") 373 | .setGravity(Gravity.BOTTOM) 374 | .setYOffset(100) 375 | .show(); 376 | } 377 | } 378 | 379 | /** 380 | * 显示全局弹窗 381 | * @noinspection unchecked 382 | */ 383 | public static void showGlobalWindow(Application application) { 384 | SpringBackWindowDraggableRule springBackWindowDraggableRule = new SpringBackWindowDraggableRule( 385 | SpringBackWindowDraggableRule.ORIENTATION_HORIZONTAL); 386 | springBackWindowDraggableRule.setAllowMoveToScreenNotch(false); 387 | springBackWindowDraggableRule.setSpringBackAnimCallback(new SpringBackAnimCallback() { 388 | 389 | @Override 390 | public void onSpringBackAnimationStart(@NonNull EasyWindow easyWindow, @NonNull Animator animator) { 391 | Log.i(TAG, "onSpringBackAnimationStart"); 392 | } 393 | 394 | @Override 395 | public void onSpringBackAnimationEnd(@NonNull EasyWindow easyWindow, @NonNull Animator animator) { 396 | Log.i(TAG, "onSpringBackAnimationEnd"); 397 | } 398 | }); 399 | springBackWindowDraggableRule.setWindowDraggingListener(new OnWindowDraggingListener() { 400 | 401 | @Override 402 | public void onWindowDraggingStart(@NonNull EasyWindow easyWindow) { 403 | Log.i(TAG, "onWindowDraggingStart"); 404 | } 405 | 406 | @Override 407 | public void onWindowDraggingNow(@NonNull EasyWindow easyWindow) { 408 | Log.i(TAG, "onWindowDraggingNow"); 409 | } 410 | 411 | @Override 412 | public void onWindowDraggingStop(@NonNull EasyWindow easyWindow) { 413 | Log.i(TAG, "onWindowDraggingStop"); 414 | } 415 | }); 416 | // 传入 Application 表示这个是一个全局的 Toast 417 | EasyWindow.with(application) 418 | .setContentView(R.layout.window_phone) 419 | .setGravity(Gravity.END | Gravity.BOTTOM) 420 | .setYOffset(200) 421 | // 设置指定的拖拽规则 422 | .setWindowDraggableRule(springBackWindowDraggableRule) 423 | .setOnClickListenerByView(android.R.id.icon, new OnWindowViewClickListener() { 424 | 425 | @Override 426 | public void onClick(@NonNull EasyWindow easyWindow, @NonNull ImageView view) { 427 | Toaster.show("我被点击了"); 428 | // 点击后跳转到拨打电话界面 429 | // Intent intent = new Intent(Intent.ACTION_DIAL); 430 | // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 431 | // toast.startActivity(intent); 432 | // 安卓 10 在后台跳转 Activity 需要额外适配 433 | // https://developer.android.google.cn/about/versions/10/privacy/changes#background-activity-starts 434 | } 435 | }) 436 | .setOnLongClickListenerByView(android.R.id.icon, new OnWindowViewLongClickListener() { 437 | @Override 438 | public boolean onLongClick(@NonNull EasyWindow easyWindow, @NonNull ImageView view) { 439 | Toaster.show("我被长按了"); 440 | return false; 441 | } 442 | }) 443 | .setOnWindowLifecycleCallback(new OnWindowLifecycleCallback() { 444 | 445 | @Override 446 | public void onWindowCancel(@NonNull EasyWindow easyWindow) { 447 | // 在窗口消失的时候回收资源,避免 LeakCanary 一直报内存泄漏,关于这点框架文档有介绍,详情请看文档 448 | easyWindow.recycle(); 449 | } 450 | }) 451 | .show(); 452 | } 453 | 454 | private void cancelAndRecycleEasyWindow(@NonNull EasyWindow easyWindow) { 455 | // 有两种方式取消弹窗: 456 | // 1. easyWindow.cancel:顾名思义,取消显示 457 | // 2. easyWindow.recycle:在取消显示的基础上,加上了回收 458 | // 这两种区别在于,cancel 之后还能 show,但是 recycle 之后不能再 show 459 | // 通常情况下,如果你创建的 EasyWindow 对象在 cancel 之后永远不会再显示,取消弹窗建议直接用 recycle 方法,否则用 cancel 方法 460 | easyWindow.recycle(); 461 | } 462 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/window_bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_ios_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_ios_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_left_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_left_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_right_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_right_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_scale_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 22 | 23 | 32 | 33 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_scale_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_top_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/window_top_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getActivity/EasyWindow/7a92473074c2feaa5f2bfeef43d2a43146f90562/app/src/main/res/drawable-xhdpi/ic_close.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_dialog_tip_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getActivity/EasyWindow/7a92473074c2feaa5f2bfeef43d2a43146f90562/app/src/main/res/drawable-xhdpi/ic_dialog_tip_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_dialog_tip_finish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getActivity/EasyWindow/7a92473074c2feaa5f2bfeef43d2a43146f90562/app/src/main/res/drawable-xhdpi/ic_dialog_tip_finish.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_dialog_tip_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getActivity/EasyWindow/7a92473074c2feaa5f2bfeef43d2a43146f90562/app/src/main/res/drawable-xhdpi/ic_dialog_tip_warning.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_float_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getActivity/EasyWindow/7a92473074c2feaa5f2bfeef43d2a43146f90562/app/src/main/res/drawable-xhdpi/ic_float_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/dialog_toast_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 22 | 23 | 26 | 27 | 32 | 33 |