├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── YangDialog ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── setruth │ └── yangdialog │ └── YangDialog.kt ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── setruth │ │ └── yangdialog │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── setruth │ │ │ └── yangdialog │ │ │ ├── MainActivity.kt │ │ │ └── ui │ │ │ ├── components │ │ │ ├── AnimateDialog.kt │ │ │ ├── BottomDIYDialog.kt │ │ │ ├── CommonDialog.kt │ │ │ └── DIYContentDialog.kt │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── setruth │ └── yangdialog │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imgResource ├── allState.gif ├── commonDialog.gif ├── diyBottom.gif ├── diyContent.gif └── example.gif └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | YangDialog -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - [JetPack Compose Material3的加载动画弹窗](#jetpack-compose-material3的加载动画弹窗) 2 | - [介绍](#介绍) 3 | - [代码示例](#代码示例) 4 | - [特点](#特点) 5 | - [版本环境要求](#版本环境要求) 6 | - [引入(Gradle KotlinDSL举例)](#引入gradle-kotlindsl举例) 7 | - [1. 设置仓库源](#1-设置仓库源) 8 | - [2. 引入依赖](#2-引入依赖) 9 | - [使用](#使用) 10 | - [简单使用](#简单使用) 11 | - [自定义显示内容](#自定义显示内容) 12 | - [自定义底部显示和隐藏](#自定义底部显示和隐藏) 13 | - [展示所有动画](#展示所有动画) 14 | 15 | # JetPack Compose Material3的加载动画弹窗 16 | 17 | ## 介绍 18 | 19 | > YangDialog是一个适合Compose Material3的弹窗UI组件,由于官方的DIalog组件功能很少,所以基于基础组件Dialog进行了修改和封装, 20 | > 哔哩哔哩教程地址 https://www.bilibili.com/video/BV1ZN411k7B1/?share_source=copy_web&vd_source=2cbd021570b82989d2171c3061a31b48 21 | 22 | ### 代码示例 23 | 24 | 例子 25 | 26 | 27 | 28 | 29 | 30 | ### 特点 31 | 32 | - 弹窗一个带有加载动画和基本弹窗结构的UI组件,加载动画包含了常用的三个类型,一个Success显示成功的图标,Error显示错误的图标,Loading为加载中的状态,并且加载状态底部提供文本按钮给用户进行加载状态的操作。 33 | - 虽然是一个封装过的弹窗框架,但是提供了许多自定义的部分,比如自定义背景色,内容颜色,标题颜色,底部是否显示确认或者取消按钮等,根据google官方Material3控件属性的设置习惯,使用单例来配置颜色内容等属性,遮罩的点击回调,尽可能减少过多的学习成本。 34 | - 弹窗的中间主要内容是提供组合函数让用户自定义展示使用的,并不会干扰弹窗的主要展示部分,可以自定义自己想显示的内容 35 | - 所有的动画都是已经设置好的,无需过多管理,使用AnimatedVisibility实现动画的各种情况,尽可能的让体验更加的接近原生,而且是根据最基础的Dialog组件进行扩展,所以不会出现太多过多封装造成的性能问题。 36 | 37 | ## 版本环境要求 38 | 39 | - Jetpack Compose Material3 40 | - Gradle 8.1.0以上(建议) 41 | - AGP 8.0.2以上(建议) 42 | - 最小SDK 26 43 | 44 | ## 引入(Gradle KotlinDSL举例) 45 | 46 | ### 1. 设置仓库源 47 | 48 | > settings.gradle.kts中设置仓库源 49 | 50 | ```groovy 51 | dependencyResolutionManagement { 52 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 53 | repositories { 54 | google() 55 | mavenCentral() 56 | maven("https://jitpack.io")//添加仓库源 57 | } 58 | } 59 | ``` 60 | 61 | ### 2. 引入依赖 62 | 63 | > APP模块下的build.gradle.kts 64 | 65 | ```groovy 66 | dependencies { 67 | //请去仓库的releases中使用最新的版本即可(建议一直最新版本当前文档版本1.0.4) 68 | implementation("com.github.setruth:ComposeM3YangDialog:$releases") 69 | ... 70 | } 71 | ``` 72 | 73 | ## 使用 74 | 75 | ### 简单使用 76 | 77 | ```kotlin 78 | var dialogShow by remember { 79 | mutableStateOf(true) 80 | } 81 | YangDialog( 82 | title = "这是普通弹窗", 83 | isShow = dialogShow,//通过isShow展示或者隐藏dialog 84 | onCancel = {//取消选项的回调 85 | dialogShow = false 86 | }, 87 | //底部设置 88 | bottomConfig = YangDialogDefaults.bottomConfig( 89 | confirmActive = false //不激活确认选项 90 | ), 91 | onDismissRequest = { //遮罩层点击的回调 92 | dialogShow = false 93 | }, 94 | ) { 95 | Text(text = "hello") 96 | } 97 | ``` 98 | 99 | 例子 100 | 101 | 102 | 103 | 104 | 105 | ### 自定义显示内容 106 | 107 | ```kotlin 108 | var dialogShow by remember { 109 | mutableStateOf(true) 110 | } 111 | var inputContent by remember { 112 | mutableStateOf("") 113 | } 114 | YangDialog( 115 | title = "这是自定义内容弹窗", 116 | isShow = dialogShow, 117 | onCancel = { 118 | dialogShow = false 119 | }, 120 | onConfirm = { 121 | dialogShow = false 122 | }, 123 | ) { 124 | OutlinedTextField(value = inputContent, onValueChange = { inputContent = it })//自定义内容 125 | } 126 | ``` 127 | 128 | 例子 129 | 130 | ### 自定义底部显示和隐藏 131 | 132 | ```kotlin 133 | var dialogShow by remember { 134 | mutableStateOf(true) 135 | } 136 | YangDialog( 137 | title = "自定义底部选项的弹窗", 138 | isShow = dialogShow, 139 | onCancel = { 140 | dialogShow = false 141 | }, 142 | onConfirm = { 143 | dialogShow = false 144 | }, 145 | //通过YangDialogDefaults.bottomConfig设置底部选项的配置 146 | bottomConfig = YangDialogDefaults.bottomConfig( 147 | showCancel = false, 148 | confirmTip = "确认将会关闭弹窗" 149 | ) 150 | ) { 151 | Text(text = "hello") 152 | } 153 | ``` 154 | 155 | 例子 156 | 157 | ### 展示所有动画 158 | 159 | ```kotlin 160 | var dialogShow by remember { 161 | mutableStateOf(true) 162 | } 163 | var dialogType by remember { 164 | mutableStateOf(YangDialogLoadingType.NOT_LOADING) 165 | } 166 | var loadingTip by remember { 167 | mutableStateOf("") 168 | } 169 | var startLoading by remember { 170 | mutableStateOf(false) 171 | } 172 | LaunchedEffect(startLoading){ 173 | if (startLoading){ 174 | loadingTip="加载" 175 | dialogType=YangDialogLoadingType.LOADING 176 | delay(1500) 177 | dialogType=YangDialogLoadingType.ERROR 178 | loadingTip="失败" 179 | delay(1500) 180 | dialogType=YangDialogLoadingType.SUCCESS 181 | loadingTip="成功" 182 | } 183 | } 184 | YangDialog( 185 | loadingState = dialogType,//通过改变loadingState来显示对应的动画 186 | loadingTip = loadingTip,//加载动画下面的提示文本,为空不显示 187 | title = "这是自定义内容弹窗", 188 | isShow = dialogShow, 189 | onCancel = { 190 | dialogShow = false 191 | dialogHide() 192 | }, 193 | //加载动画的底部文本的点击回调,返回当前加载的状态 194 | onLoadingTipClick = { 195 | if (it==YangDialogLoadingType.SUCCESS) { 196 | dialogType=YangDialogLoadingType.NOT_LOADING 197 | } 198 | }, 199 | onConfirm = { 200 | startLoading=true 201 | }, 202 | bottomConfig = YangDialogDefaults.bottomConfig( 203 | confirmTip = "开始加载动画" 204 | ) 205 | ) 206 | ``` 207 | 208 | 例子 209 | 210 | -------------------------------------------------------------------------------- /YangDialog/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /YangDialog/build.gradle.kts: -------------------------------------------------------------------------------- 1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed 2 | plugins { 3 | alias(libs.plugins.androidLibrary) 4 | id("org.jetbrains.kotlin.android") version "1.8.10" 5 | `maven-publish` 6 | } 7 | 8 | android { 9 | namespace = "com.setruth.yangdialog" 10 | compileSdk = 33 11 | 12 | defaultConfig { 13 | minSdk = 26 14 | 15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles("consumer-rules.pro") 17 | } 18 | 19 | buildTypes { 20 | release { 21 | isMinifyEnabled = false 22 | proguardFiles( 23 | getDefaultProguardFile("proguard-android-optimize.txt"), 24 | "proguard-rules.pro" 25 | ) 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility = JavaVersion.VERSION_17 30 | targetCompatibility = JavaVersion.VERSION_17 31 | } 32 | kotlinOptions { 33 | jvmTarget = JavaVersion.VERSION_17.toString() 34 | } 35 | buildFeatures{ 36 | compose=true 37 | } 38 | composeOptions { 39 | kotlinCompilerExtensionVersion = "1.4.3" 40 | } 41 | } 42 | 43 | dependencies { 44 | implementation("androidx.core:core-ktx:1.9.0") 45 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") 46 | implementation("androidx.activity:activity-compose:1.7.0") 47 | implementation(platform("androidx.compose:compose-bom:2023.03.00")) 48 | implementation("androidx.compose.ui:ui") 49 | implementation("androidx.compose.ui:ui-graphics") 50 | implementation("androidx.compose.material3:material3:1.1.0") 51 | androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00")) 52 | debugImplementation("androidx.compose.ui:ui-tooling") 53 | } 54 | publishing{ 55 | publications { 56 | register("release"){ 57 | groupId="com.setruth.compose.m3" 58 | artifactId="yangDialog" 59 | version="1.0.0" 60 | afterEvaluate{ 61 | from(components["release"]) 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /YangDialog/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/YangDialog/consumer-rules.pro -------------------------------------------------------------------------------- /YangDialog/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /YangDialog/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /YangDialog/src/main/java/com/setruth/yangdialog/YangDialog.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog 2 | 3 | 4 | import androidx.compose.animation.AnimatedVisibility 5 | import androidx.compose.animation.ExperimentalAnimationApi 6 | import androidx.compose.animation.animateContentSize 7 | import androidx.compose.animation.fadeIn 8 | import androidx.compose.animation.fadeOut 9 | import androidx.compose.animation.scaleIn 10 | import androidx.compose.animation.scaleOut 11 | import androidx.compose.foundation.layout.Arrangement 12 | import androidx.compose.foundation.layout.Box 13 | import androidx.compose.foundation.layout.Column 14 | import androidx.compose.foundation.layout.Row 15 | import androidx.compose.foundation.layout.Spacer 16 | import androidx.compose.foundation.layout.fillMaxWidth 17 | import androidx.compose.foundation.layout.height 18 | import androidx.compose.foundation.layout.padding 19 | import androidx.compose.foundation.layout.size 20 | import androidx.compose.foundation.layout.wrapContentHeight 21 | import androidx.compose.material.icons.Icons 22 | import androidx.compose.material.icons.filled.Check 23 | import androidx.compose.material.icons.filled.Close 24 | import androidx.compose.material3.Card 25 | import androidx.compose.material3.CardDefaults 26 | import androidx.compose.material3.CircularProgressIndicator 27 | import androidx.compose.material3.Icon 28 | import androidx.compose.material3.MaterialTheme 29 | import androidx.compose.material3.Text 30 | import androidx.compose.material3.TextButton 31 | import androidx.compose.runtime.Composable 32 | import androidx.compose.runtime.Immutable 33 | import androidx.compose.runtime.State 34 | import androidx.compose.runtime.rememberUpdatedState 35 | import androidx.compose.ui.Alignment 36 | import androidx.compose.ui.Modifier 37 | import androidx.compose.ui.graphics.Color 38 | import androidx.compose.ui.text.font.FontWeight 39 | import androidx.compose.ui.unit.dp 40 | import androidx.compose.ui.window.Dialog 41 | import androidx.compose.ui.window.DialogProperties 42 | 43 | /** 44 | * @author setruth 45 | * @date 2023/6/12 46 | * @time 19:31 47 | */ 48 | 49 | /** 50 | * TODO 更多的动画加载和自定义内容弹窗 51 | * @param isShow Boolean 是否显示(必填) 52 | * @param title String 标题内容 53 | * @param loadingState YangDialogLoadingType 在[YangDialogLoadingState]中对应 54 | * 了四种状态,包括不加载,不加载时就会显示弹窗内容,加载分为成功,失败,加载中 55 | * @param loadingTip String 加载状态下的文字提示,可以点击,点击的回调函数是loadingTipClick 56 | * @param onLoadingTipClick Function1<[YangDialogLoadingState], Unit> 传递当前加载的状态 57 | * @param onCancel Function0 点击取消按钮的回调 58 | * @param onConfirm Function0 点击确认按钮的回调 59 | * @param onDismissRequest Function0 点击弹窗外空白区域的回调 60 | * @param bottomConfig [YangDialogBottomConfig] 底部配置,包括底部确认取消按钮显示,显示的文字内容等 61 | * @param colorConfig [YangDialogColorConfig] 颜色配置,包括背景色,标题颜色,内容颜色等 62 | * @param dialogContent [@androidx.compose.runtime.Composable] Function0 弹窗显示的内容 63 | */ 64 | @OptIn(ExperimentalAnimationApi::class) 65 | @Composable 66 | fun YangDialog( 67 | isShow: Boolean, 68 | title: String = "标题", 69 | loadingState: YangDialogLoadingState = YangDialogLoadingState.NOT_LOADING, 70 | loadingTip: String = "加载中", 71 | onLoadingTipClick: (YangDialogLoadingState) -> Unit = {}, 72 | onCancel: () -> Unit = {}, 73 | onConfirm: () -> Unit = {}, 74 | onDismissRequest: () -> Unit = {}, 75 | bottomConfig: YangDialogBottomConfig = YangDialogDefaults.bottomConfig(), 76 | colorConfig: YangDialogColorConfig = YangDialogDefaults.colorConfig(), 77 | dialogContent: @Composable () -> Unit = {}, 78 | ) { 79 | if (isShow) { 80 | Dialog( 81 | onDismissRequest = { onDismissRequest() }, 82 | properties = DialogProperties( 83 | usePlatformDefaultWidth = false 84 | ) 85 | ) { 86 | Card( 87 | modifier = Modifier 88 | .fillMaxWidth() 89 | .wrapContentHeight() 90 | .padding(horizontal = 24.dp, vertical = 20.dp), 91 | shape = MaterialTheme.shapes.large, 92 | colors = CardDefaults.cardColors( 93 | containerColor = colorConfig.containerColor().value, 94 | contentColor = colorConfig.contentColor().value 95 | ), 96 | ) { 97 | Column( 98 | Modifier 99 | .fillMaxWidth() 100 | .padding(15.dp)) { 101 | Text( 102 | modifier = Modifier.padding(horizontal = 8.dp), 103 | text = title, 104 | fontWeight = FontWeight.Bold, 105 | color = colorConfig.titleColor().value 106 | ) 107 | Box( 108 | modifier = Modifier 109 | .fillMaxWidth() 110 | .padding(top = 16.dp) 111 | .animateContentSize(), 112 | ) { 113 | //内容 114 | Column { 115 | AnimatedVisibility( 116 | visible = loadingState == YangDialogLoadingState.NOT_LOADING, 117 | enter = fadeIn(), 118 | exit = fadeOut() 119 | ) { 120 | Column { 121 | Box(Modifier.padding(horizontal = 8.dp)) { 122 | dialogContent() 123 | } 124 | Spacer(modifier = Modifier.height(24.dp)) 125 | Row( 126 | modifier = Modifier.fillMaxWidth(), 127 | horizontalArrangement = Arrangement.SpaceBetween 128 | ) { 129 | if (bottomConfig.showCancel().value) { 130 | TextButton( 131 | onClick = { 132 | onCancel() 133 | }, 134 | enabled = bottomConfig.cancelActive().value 135 | ) { 136 | Text(bottomConfig.cancelTip().value) 137 | } 138 | } 139 | Spacer( 140 | modifier = Modifier 141 | .fillMaxWidth() 142 | .weight(1f) 143 | ) 144 | if (bottomConfig.showConfirm().value) { 145 | TextButton( 146 | onClick = { 147 | onConfirm() 148 | }, 149 | enabled = bottomConfig.confirmActive().value 150 | ) { 151 | Text(bottomConfig.confirmTip().value) 152 | } 153 | } 154 | } 155 | 156 | 157 | 158 | } 159 | } 160 | } 161 | //加载动画部分 162 | Column { 163 | AnimatedVisibility( 164 | visible = loadingState != YangDialogLoadingState.NOT_LOADING, 165 | enter = scaleIn(), 166 | exit = scaleOut() 167 | ) { 168 | Column( 169 | horizontalAlignment = Alignment.CenterHorizontally, 170 | verticalArrangement = Arrangement.Center 171 | ) { 172 | Row { 173 | AnimatedVisibility( 174 | visible = loadingState == YangDialogLoadingState.LOADING, 175 | enter = scaleIn(), 176 | exit = scaleOut() 177 | ) { 178 | Column( 179 | modifier = Modifier 180 | .fillMaxWidth(), 181 | horizontalAlignment = Alignment.CenterHorizontally 182 | ) { 183 | CircularProgressIndicator() 184 | } 185 | } 186 | AnimatedVisibility( 187 | visible = loadingState == YangDialogLoadingState.ERROR, 188 | enter = scaleIn(), 189 | exit = scaleOut() 190 | ) { 191 | Column( 192 | modifier = Modifier 193 | .fillMaxWidth(), 194 | horizontalAlignment = Alignment.CenterHorizontally 195 | ) { 196 | Icon( 197 | modifier = Modifier.size(50.dp), 198 | imageVector = Icons.Default.Close, 199 | contentDescription = loadingTip, 200 | tint = MaterialTheme.colorScheme.error 201 | ) 202 | } 203 | } 204 | AnimatedVisibility( 205 | visible = loadingState == YangDialogLoadingState.SUCCESS, 206 | enter = scaleIn(), 207 | exit = scaleOut() 208 | ) { 209 | Column( 210 | modifier = Modifier 211 | .fillMaxWidth(), 212 | horizontalAlignment = Alignment.CenterHorizontally 213 | ) { 214 | Icon( 215 | modifier = Modifier.size(50.dp), 216 | imageVector = Icons.Default.Check, 217 | contentDescription = loadingTip, 218 | tint = MaterialTheme.colorScheme.primary 219 | ) 220 | } 221 | } 222 | } 223 | TextButton( 224 | enabled = loadingTip != "", 225 | onClick = { onLoadingTipClick(loadingState) }, 226 | ) { 227 | val textColor = when (loadingState) { 228 | YangDialogLoadingState.LOADING -> MaterialTheme.colorScheme.onSurface 229 | YangDialogLoadingState.SUCCESS -> MaterialTheme.colorScheme.primary 230 | YangDialogLoadingState.ERROR -> MaterialTheme.colorScheme.error 231 | YangDialogLoadingState.NOT_LOADING -> MaterialTheme.colorScheme.onSurface 232 | } 233 | Text( 234 | text = loadingTip, 235 | color = textColor 236 | ) 237 | } 238 | } 239 | } 240 | } 241 | } 242 | } 243 | } 244 | } 245 | } 246 | } 247 | 248 | 249 | /** 250 | * 弹窗的配置内容 251 | */ 252 | object YangDialogDefaults { 253 | /** 254 | * 弹窗的底部配置 255 | * @param showCancel Boolean 是否显示取消按钮 256 | * @param showConfirm Boolean 是否显示确认按钮 257 | * @param confirmTip String 取消按钮的文字内容 258 | * @param cancelTip String 确认按钮的文字内容 259 | * @return YangDialogBottomConfig 弹窗底部的配置定义[YangDialogBottomConfig] 260 | */ 261 | @Composable 262 | fun bottomConfig( 263 | showCancel: Boolean = true, 264 | showConfirm: Boolean = true, 265 | confirmTip: String = "确认", 266 | cancelTip: String = "取消", 267 | cancelActive: Boolean = true, 268 | confirmActive: Boolean = true, 269 | ): YangDialogBottomConfig = 270 | YangDialogBottomConfig( 271 | showCancel, 272 | showConfirm, 273 | confirmTip, 274 | cancelTip, 275 | cancelActive, 276 | confirmActive 277 | ) 278 | 279 | /** 280 | * 弹窗的颜色配置 281 | * @param containerColor Color 背景色 282 | * @param titleColor Color 标题颜色 283 | * @param contentColor Color 内容颜色 284 | * @return YangDialogColorConfig 弹窗颜色配置定义 [YangDialogColorConfig] 285 | */ 286 | @Composable 287 | fun colorConfig( 288 | containerColor: Color = MaterialTheme.colorScheme.surface, 289 | titleColor: Color = MaterialTheme.colorScheme.primary, 290 | contentColor: Color = MaterialTheme.colorScheme.onSurface, 291 | ): YangDialogColorConfig = 292 | YangDialogColorConfig( 293 | containerColor = containerColor, 294 | titleColor = titleColor, 295 | contentColor = contentColor 296 | ) 297 | } 298 | 299 | /** 300 | * 弹窗底部的配置定义 301 | * @property showCancel Boolean 是否显示取消按钮 302 | * @property showConfirm Boolean 是否显示确认按钮 303 | * @property confirmTip String 取消按钮的文字内容 304 | * @property cancelTip String 确认按钮的文字内容 305 | * @constructor 306 | */ 307 | @Immutable 308 | class YangDialogBottomConfig internal constructor( 309 | private val showCancel: Boolean, 310 | private val showConfirm: Boolean, 311 | private val confirmTip: String, 312 | private val cancelTip: String, 313 | private val cancelActive: Boolean, 314 | private val confirmActive: Boolean, 315 | ) { 316 | @Composable 317 | internal fun showConfirm(): State { 318 | return rememberUpdatedState(showConfirm) 319 | } 320 | 321 | @Composable 322 | internal fun showCancel(): State { 323 | return rememberUpdatedState(showCancel) 324 | } 325 | 326 | @Composable 327 | internal fun confirmTip(): State { 328 | return rememberUpdatedState(confirmTip) 329 | } 330 | 331 | @Composable 332 | internal fun cancelTip(): State { 333 | return rememberUpdatedState(cancelTip) 334 | } 335 | 336 | @Composable 337 | internal fun cancelActive(): State { 338 | return rememberUpdatedState(cancelActive) 339 | } 340 | 341 | @Composable 342 | internal fun confirmActive(): State { 343 | return rememberUpdatedState(confirmActive) 344 | } 345 | } 346 | 347 | /** 348 | * 弹窗颜色配置定义 349 | * @property containerColor Color 背景色 350 | * @property titleColor Color 标题颜色 351 | * @property contentColor Color 内容颜色 352 | * @constructor 353 | */ 354 | @Immutable 355 | class YangDialogColorConfig internal constructor( 356 | private val containerColor: Color, 357 | private val titleColor: Color, 358 | private val contentColor: Color 359 | ) { 360 | @Composable 361 | internal fun containerColor(): State { 362 | return rememberUpdatedState(containerColor) 363 | } 364 | 365 | @Composable 366 | internal fun contentColor(): State { 367 | return rememberUpdatedState(contentColor) 368 | } 369 | 370 | @Composable 371 | internal fun titleColor(): State { 372 | return rememberUpdatedState(titleColor) 373 | } 374 | } 375 | 376 | /** 377 | * 弹窗当前的状态 378 | */ 379 | enum class YangDialogLoadingState { 380 | LOADING, //加载中 381 | SUCCESS,//通过 382 | ERROR,//错误 383 | NOT_LOADING //没有处于加载状态 384 | } -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed 2 | plugins { 3 | alias(libs.plugins.androidApplication) 4 | alias(libs.plugins.kotlinAndroid) 5 | } 6 | 7 | android { 8 | namespace = "com.setruth.yangdialog" 9 | compileSdk = 33 10 | 11 | defaultConfig { 12 | applicationId = "com.setruth.yangdialog" 13 | minSdk = 31 14 | targetSdk = 33 15 | versionCode = 1 16 | versionName = "1.0" 17 | 18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 19 | vectorDrawables { 20 | useSupportLibrary = true 21 | } 22 | } 23 | 24 | buildTypes { 25 | release { 26 | isMinifyEnabled = false 27 | proguardFiles( 28 | getDefaultProguardFile("proguard-android-optimize.txt"), 29 | "proguard-rules.pro" 30 | ) 31 | } 32 | } 33 | compileOptions { 34 | sourceCompatibility = JavaVersion.VERSION_17 35 | targetCompatibility = JavaVersion.VERSION_17 36 | } 37 | kotlinOptions { 38 | jvmTarget = JavaVersion.VERSION_17.toString() 39 | } 40 | buildFeatures { 41 | compose = true 42 | } 43 | composeOptions { 44 | kotlinCompilerExtensionVersion = "1.4.3" 45 | } 46 | packaging { 47 | resources { 48 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 49 | } 50 | } 51 | } 52 | 53 | dependencies { 54 | implementation(project(":YangDialog")) 55 | implementation(libs.core.ktx) 56 | implementation(libs.lifecycle.runtime.ktx) 57 | implementation(libs.activity.compose) 58 | implementation(platform(libs.compose.bom)) 59 | implementation(libs.ui) 60 | implementation(libs.ui.graphics) 61 | implementation(libs.material3) 62 | androidTestImplementation(libs.espresso.core) 63 | androidTestImplementation(platform(libs.compose.bom)) 64 | debugImplementation(libs.ui.tooling) 65 | testImplementation(libs.junit) 66 | implementation(libs.ui.tooling.preview) 67 | debugImplementation(libs.ui.test.manifest) 68 | androidTestImplementation(libs.ui.test.junit4) 69 | androidTestImplementation(libs.androidx.test.ext.junit) 70 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/setruth/yangdialog/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.setruth.yangdialog", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import androidx.activity.ComponentActivity 6 | import androidx.activity.compose.setContent 7 | import androidx.compose.foundation.layout.Arrangement 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.Spacer 10 | import androidx.compose.foundation.layout.fillMaxSize 11 | import androidx.compose.foundation.layout.height 12 | import androidx.compose.foundation.layout.padding 13 | import androidx.compose.foundation.layout.wrapContentHeight 14 | import androidx.compose.foundation.layout.wrapContentWidth 15 | import androidx.compose.material3.AlertDialog 16 | import androidx.compose.material3.AlertDialogDefaults 17 | import androidx.compose.material3.Button 18 | import androidx.compose.material3.ExperimentalMaterial3Api 19 | import androidx.compose.material3.MaterialTheme 20 | import androidx.compose.material3.Surface 21 | import androidx.compose.material3.Text 22 | import androidx.compose.material3.TextButton 23 | import androidx.compose.runtime.Composable 24 | import androidx.compose.runtime.getValue 25 | import androidx.compose.runtime.mutableStateOf 26 | import androidx.compose.runtime.remember 27 | import androidx.compose.runtime.setValue 28 | import androidx.compose.ui.Alignment 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.tooling.preview.Preview 31 | import androidx.compose.ui.unit.dp 32 | import androidx.compose.ui.window.Dialog 33 | import com.setruth.yangdialog.ui.components.AnimateDialog 34 | import com.setruth.yangdialog.ui.components.BottomDIYDialog 35 | import com.setruth.yangdialog.ui.components.CommonDialog 36 | import com.setruth.yangdialog.ui.components.DIYContentDialog 37 | import com.setruth.yangdialog.ui.theme.YangDialogTheme 38 | 39 | class MainActivity : ComponentActivity() { 40 | override fun onCreate(savedInstanceState: Bundle?) { 41 | super.onCreate(savedInstanceState) 42 | setContent { 43 | var showDialogState by remember { 44 | mutableStateOf(ShowDialogType.No) 45 | } 46 | var show by remember { 47 | mutableStateOf(false) 48 | } 49 | when (showDialogState) { 50 | ShowDialogType.Common -> CommonDialog { 51 | showDialogState = ShowDialogType.No 52 | } 53 | 54 | ShowDialogType.DIYContent -> DIYContentDialog { 55 | showDialogState = ShowDialogType.No 56 | } 57 | ShowDialogType.BottomHide -> BottomDIYDialog { 58 | showDialogState = ShowDialogType.No 59 | } 60 | 61 | ShowDialogType.AllLoading -> AnimateDialog { 62 | showDialogState = ShowDialogType.No 63 | } 64 | 65 | ShowDialogType.No -> {} 66 | } 67 | 68 | YangDialogTheme { 69 | Column( 70 | Modifier.fillMaxSize(), 71 | horizontalAlignment = Alignment.CenterHorizontally, 72 | verticalArrangement = Arrangement.Center 73 | ) { 74 | Button(onClick = { showDialogState = ShowDialogType.Common }) { 75 | Text(text = "普通弹窗") 76 | } 77 | Spacer(modifier = Modifier.padding(vertical = 10.dp)) 78 | Button(onClick = { showDialogState = ShowDialogType.DIYContent }) { 79 | Text(text = "自定义内容") 80 | } 81 | Spacer(modifier = Modifier.padding(vertical = 10.dp)) 82 | Button(onClick = { showDialogState = ShowDialogType.BottomHide }) { 83 | Text(text = "底部隐藏") 84 | } 85 | Spacer(modifier = Modifier.padding(vertical = 10.dp)) 86 | Button(onClick = { showDialogState = ShowDialogType.AllLoading }) { 87 | Text(text = "动画加载") 88 | } 89 | } 90 | } 91 | 92 | 93 | } 94 | } 95 | } 96 | 97 | enum class ShowDialogType { 98 | Common, 99 | DIYContent, 100 | BottomHide, 101 | AllLoading, 102 | No 103 | } 104 | 105 | 106 | @Preview(showBackground = true) 107 | @Composable 108 | fun GreetingPreview() { 109 | YangDialogTheme { 110 | 111 | } 112 | } -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/ui/components/AnimateDialog.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog.ui.components 2 | 3 | import androidx.compose.foundation.layout.height 4 | import androidx.compose.foundation.layout.heightIn 5 | import androidx.compose.material3.OutlinedTextField 6 | import androidx.compose.material3.Text 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.runtime.LaunchedEffect 9 | import androidx.compose.runtime.getValue 10 | import androidx.compose.runtime.mutableStateOf 11 | import androidx.compose.runtime.remember 12 | import androidx.compose.runtime.setValue 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.unit.dp 15 | import com.setruth.yangdialog.YangDialog 16 | import com.setruth.yangdialog.YangDialogDefaults 17 | import com.setruth.yangdialog.YangDialogLoadingState 18 | import kotlinx.coroutines.delay 19 | 20 | @Composable 21 | fun AnimateDialog(dialogHide: () -> Unit) { 22 | var dialogShow by remember { 23 | mutableStateOf(true) 24 | } 25 | var dialogType by remember { 26 | mutableStateOf(YangDialogLoadingState.NOT_LOADING) 27 | } 28 | var loadingTip by remember { 29 | mutableStateOf("") 30 | } 31 | var startLoading by remember { 32 | mutableStateOf(false) 33 | } 34 | LaunchedEffect(startLoading){ 35 | if (startLoading){ 36 | loadingTip="加载" 37 | dialogType=YangDialogLoadingState.LOADING 38 | delay(1500) 39 | dialogType=YangDialogLoadingState.ERROR 40 | loadingTip="失败" 41 | delay(1500) 42 | dialogType=YangDialogLoadingState.SUCCESS 43 | loadingTip="成功" 44 | } 45 | } 46 | YangDialog( 47 | loadingState = dialogType, 48 | loadingTip = loadingTip, 49 | title = "这是自定义内容弹窗", 50 | isShow = dialogShow, 51 | onCancel = { 52 | dialogShow = false 53 | dialogHide() 54 | }, 55 | onLoadingTipClick = { 56 | if (it==YangDialogLoadingState.SUCCESS) { 57 | dialogType=YangDialogLoadingState.NOT_LOADING 58 | } 59 | }, 60 | onConfirm = { 61 | startLoading=true 62 | }, 63 | bottomConfig = YangDialogDefaults.bottomConfig( 64 | confirmTip = "开始加载动画" 65 | ) 66 | ) 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/ui/components/BottomDIYDialog.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog.ui.components 2 | 3 | import androidx.compose.material3.Text 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.getValue 6 | import androidx.compose.runtime.mutableStateOf 7 | import androidx.compose.runtime.remember 8 | import androidx.compose.runtime.setValue 9 | import com.setruth.yangdialog.YangDialog 10 | import com.setruth.yangdialog.YangDialogDefaults 11 | 12 | @Composable 13 | fun BottomDIYDialog(dialogHide: () -> Unit) { 14 | var dialogShow by remember { 15 | mutableStateOf(true) 16 | } 17 | YangDialog( 18 | title = "自定义底部选项的弹窗", 19 | isShow = dialogShow, 20 | onCancel = { 21 | dialogShow = false 22 | dialogHide() 23 | }, 24 | onConfirm = { 25 | dialogShow = false 26 | dialogHide() 27 | }, 28 | bottomConfig = YangDialogDefaults.bottomConfig( 29 | showCancel = false, 30 | confirmTip = "确认将会关闭弹窗" 31 | ) 32 | ) { 33 | Text(text = "hello") 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/ui/components/CommonDialog.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog.ui.components 2 | 3 | import android.util.Log 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.getValue 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.runtime.setValue 10 | import com.setruth.yangdialog.YangDialog 11 | import com.setruth.yangdialog.YangDialogDefaults 12 | 13 | 14 | @Composable 15 | fun CommonDialog(dialogHide: () -> Unit) { 16 | var dialogShow by remember { 17 | mutableStateOf(true) 18 | } 19 | YangDialog( 20 | title = "这是普通弹窗", 21 | isShow = dialogShow, 22 | onCancel = { 23 | dialogShow = false 24 | dialogHide() 25 | }, 26 | bottomConfig = YangDialogDefaults.bottomConfig( 27 | confirmActive = false 28 | ), 29 | onDismissRequest = { 30 | dialogShow = false 31 | dialogHide() 32 | }, 33 | ) { 34 | Text(text = "hello") 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/ui/components/DIYContentDialog.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog.ui.components 2 | 3 | import androidx.compose.foundation.layout.height 4 | import androidx.compose.material3.OutlinedTextField 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.getValue 8 | import androidx.compose.runtime.mutableStateOf 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.runtime.setValue 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.unit.dp 13 | import com.setruth.yangdialog.YangDialog 14 | 15 | @Composable 16 | fun DIYContentDialog(dialogHide: () -> Unit) { 17 | var dialogShow by remember { 18 | mutableStateOf(true) 19 | } 20 | var inputContent by remember { 21 | mutableStateOf("") 22 | } 23 | YangDialog( 24 | title = "这是自定义内容弹窗", 25 | isShow = dialogShow, 26 | onCancel = { 27 | dialogShow = false 28 | dialogHide() 29 | }, 30 | onConfirm = { 31 | dialogShow = false 32 | dialogHide() 33 | }, 34 | ) { 35 | OutlinedTextField(value = inputContent, onValueChange = { inputContent = it }) 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Purple80, 20 | secondary = PurpleGrey80, 21 | tertiary = Pink80 22 | ) 23 | 24 | private val LightColorScheme = lightColorScheme( 25 | primary = Purple40, 26 | secondary = PurpleGrey40, 27 | tertiary = Pink40 28 | 29 | ) 30 | 31 | @Composable 32 | fun YangDialogTheme( 33 | darkTheme: Boolean = isSystemInDarkTheme(), 34 | // Dynamic color is available on Android 12+ 35 | dynamicColor: Boolean = true, 36 | content: @Composable () -> Unit 37 | ) { 38 | val colorScheme = when { 39 | darkTheme -> DarkColorScheme 40 | else -> LightColorScheme 41 | } 42 | val view = LocalView.current 43 | SideEffect { 44 | val window = (view.context as Activity).window 45 | window.statusBarColor = colorScheme.primary.toArgb() 46 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 47 | } 48 | 49 | MaterialTheme( 50 | colorScheme = colorScheme, 51 | typography = Typography, 52 | content = content 53 | ) 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/setruth/yangdialog/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.setruth.yangdialog.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setruth/ComposeM3YangDialog/299889aec9804cf0cd04a9266815f1c8395ce4be/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | YangDialog 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |