├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── compiler.xml ├── dbnavigator.xml ├── gradle.xml ├── jarRepositories.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── atman.jks ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lihang │ │ └── mysmartloadingview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lihang │ │ │ └── mysmartloadingview │ │ │ ├── MainActivity.kt │ │ │ ├── MainJavaActivity.java │ │ │ └── SecondActivity.java │ └── res │ │ ├── anim │ │ ├── scale_test2.xml │ │ └── scale_test_home.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── cb_select.xml │ │ ├── ic_launcher_background.xml │ │ ├── shape_button.xml │ │ └── shape_for_login_edit.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_second.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── check_off_1.png │ │ ├── check_on.png │ │ ├── girl_head.jpg │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ ├── login_password.png │ │ ├── login_phone.png │ │ └── main_bg.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── lihang │ └── mysmartloadingview │ └── ExampleUnitTest.java ├── build.gradle ├── gif ├── SmartLoadingView_.png ├── fullScreen.gif ├── fullScreen_fail_show.gif ├── fullScreen_fail_toast.gif ├── smartButton.gif ├── smartButton_fail.gif ├── smartButton_noAnimal.gif ├── smartTick.gif ├── smartTick_hide.gif └── smartTick_hide_center.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── smartloadview ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── lihang │ ├── SmartLoadingView.java │ └── help │ ├── CirclBigView.java │ ├── OkView.java │ └── UIUtil.java └── res ├── anim ├── alpha_hide.xml ├── scale_test2.xml └── scale_test_home.xml └── values ├── attrs.xml ├── colors.xml ├── dimens.xml └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 50 | 51 | 52 | 53 | 54 |
55 | 56 | 57 | 58 | xmlns:android 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | xmlns:.* 70 | 71 | ^$ 72 | 73 | 74 | BY_NAME 75 | 76 |
77 |
78 | 79 | 80 | 81 | .*:id 82 | 83 | http://schemas.android.com/apk/res/android 84 | 85 | 86 | 87 |
88 |
89 | 90 | 91 | 92 | .*:name 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | 98 |
99 |
100 | 101 | 102 | 103 | name 104 | 105 | ^$ 106 | 107 | 108 | 109 |
110 |
111 | 112 | 113 | 114 | style 115 | 116 | ^$ 117 | 118 | 119 | 120 |
121 |
122 | 123 | 124 | 125 | .* 126 | 127 | ^$ 128 | 129 | 130 | BY_NAME 131 | 132 |
133 |
134 | 135 | 136 | 137 | .* 138 | 139 | http://schemas.android.com/apk/res/android 140 | 141 | 142 | ANDROID_ATTRIBUTE_ORDER 143 | 144 |
145 |
146 | 147 | 148 | 149 | .* 150 | 151 | .* 152 | 153 | 154 | BY_NAME 155 | 156 |
157 |
158 |
159 |
160 |
161 |
-------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/dbnavigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 leo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmartLoadingView - 一个自带loading的联网请求按钮 2 | [![](https://jitpack.io/v/lihangleo2/SmartLoadingView.svg)](https://jitpack.io/#lihangleo2/SmartLoadingView) 3 | [![](https://img.shields.io/badge/license-MIT-green)](https://github.com/lihangleo2/SmartLoadingView/blob/master/LICENSE) 4 | 5 | 6 | ## 特点功能 7 | 想让你的按钮动起来吗,想让你的app具备活力吗。那来试试它吧。具体介绍如下: 8 | ``` 9 | 1. 支持在任何布局上使用 10 | 2. smart_full_screen模式:全屏扩散跳转动画,让你的按钮和跳转页面更有活力 11 | 3. smart_tick模式:仿抖音,关注按钮及动画 12 | 4. smart_tick_hide模式:打勾动画及隐藏 13 | 5. smart_tick_center_hide模式:打勾隐藏,及移动到屏幕中间提醒模式 14 | 6. smart_button模式:正常关注和取消关注 15 | ``` 16 | 17 | 18 | ## SmartLoadingView动态 19 | * [SmartLoadingView成长史](https://github.com/lihangleo2/SmartLoadingView/wiki) 20 | 21 | 22 | ## Demo 23 | 为录制流畅,截图分辨率比较模糊。可在下方扫描二维码下载apk,查看真机效果。 24 | 25 | ![](https://github.com/lihangleo2/SmartLoadingView/blob/master/gif/SmartLoadingView_.png) 26 |
27 | 28 | ## 效果展示 29 | 为录制流畅,截图分辨率模糊。可下载apk查看真机效果 30 | * ### smart_full_screen模式:全屏扩散动画(不支持关注) 31 | |全屏扩散页面跳转|加载失败|失败文案在按钮上| 32 | |:---:|:---:|:---:| 33 | |Sample|Sample|Sample 34 |
35 | 36 | * ### smart_button模式:正常按钮动画(支持关注) 37 | |关注并加载成功|关注失败|不带动画关注| 38 | |:---:|:---:|:---:| 39 | |Sample|Sample|Sample 40 |
41 | 42 | * ### smart_tick模式:仿抖音打勾关注(支持关注);smart_tick_hide模式:打勾隐藏(支持关注);smart_tick_center_hide模式:打勾隐藏,并移至中间提醒(支持关注)。 43 | |仿抖音打勾关注|打勾隐藏|打勾隐藏,移至中间| 44 | |:---:|:---:|:---:| 45 | |Sample|Sample|Sample 46 |
47 | 48 | 49 | ## 添加依赖 50 | 51 | - 项目build.gradle添加如下 52 | ```java 53 | allprojects { 54 | repositories { 55 | maven { url 'https://jitpack.io' } 56 | } 57 | } 58 | ``` 59 | - app build.gradle添加如下 60 | ```java 61 | dependencies { 62 | 63 | implementation 'com.github.lihangleo2:SmartLoadingView:3.0.0' 64 | 65 | } 66 | ``` 67 |
68 | 69 | ## 基本使用 70 | #### 一、简单使用 71 | xml设置如下,注意app:hl_button_type="smart_button" 72 | ```xml 73 | 84 | ``` 85 |
86 | 87 | 使用如下:没看错就这么简单(5种buttonType均如此) 88 | ```java 89 | //1.1 设置点击事件,点击调用加载loading 90 | smartLoadingView.startLoading() 91 | 92 | //1.2 联网成功,关注成功调用如下代码 93 | smartLoadingView.finishLoading() 94 | 95 | //1.3 联网失败,调用如下代码 96 | smartLoadingView.finishLoading(false) 97 | 98 | ``` 99 |
100 | 101 | #### 二、全屏模式:smart_full_screen(注意:此模式不支持关注) 102 | ##### 2.1 全屏扩散及页面跳转:smartLoadingView.finishLoadingWithFullScreen(Activity activity, Class clazz) 103 | xml如下: 104 | ```xml 105 | 115 | ``` 116 |
117 | 118 | 当点击按钮时,开启加载loading 119 |
120 | 121 | ```java 122 | smartLoadingView.startLoading() 123 | ``` 124 |
125 | 126 | 当得到联网结果为success,且需要跳转页面时调用如下: 127 | ```java 128 | smartLoadingView.finishLoadingWithFullScreen(this, SecondActivity::class.java) 129 | ``` 130 |
131 | 132 | 如果你不想用封装的api,想再扩散动画结束后自己操作,你可以使用如下方法: 133 | ```java 134 | //kotlin使用如下: 135 | smartLoadingView.finishLoading(true) { 136 | //处理自定义逻辑 137 | } 138 | 139 | //java使用如下: 140 | smartLoadingView.finishLoading(true, success -> { 141 | //处理自定义逻辑 142 | }); 143 | ``` 144 |
145 | 146 | ##### 2.2 如果联网结果失败fail时 147 | 执行完,就会平滑回到初始状态 148 | ```java 149 | //kotlin使用如下: 150 | smartLoadingView.finishLoading(false) { 151 | ToastUtils.showShort("加载失败") 152 | } 153 | 154 | //java使用如下: 155 | smartLoadingView.finishLoading(false, success -> { 156 | ToastUtils.showShort("加载失败") 157 | }); 158 | ``` 159 |
160 | 161 | ##### 2.3 如果联网结果失败,你想将错误信息显示在按钮上可以这样 162 | xml如下: 163 | ```xml 164 | 177 | ``` 178 |
179 | 180 | 调用如下即可。 181 | ```java 182 | //如果想自定义错误文案,在调用finishLoading前,设置文案 183 | //smartLoadingView.setAnimaledText("我是自定义错误文案") 184 | smartLoadingView.finishLoading(false) 185 | 186 | ``` 187 |
188 | 189 | #### 三、正常模式详细讲解:smart_button(支持关注) 190 | 特别说明:smart_button、smart_tick、smart_tick_hide、smart_tick_center_hide 这四种模式,用法一致。所以这里以smart_button 讲解为主 191 | ##### 3.1 这里我们用一个关注功能来说:如果我们有个按钮,初始状态显示"点击关注",点击按钮进行网络请求,成功了显示"关注成功";此时再点击,进行网络请求,成功后再显示"点击关注" 192 | 当点击按钮时, 我们要判断当前是什么状态,来进行接下来的逻辑: 193 | ```java 194 | if (!smartLoadingView.isFinished) { 195 | //当前不时结束,状态,开启加载loading 196 | smartLoadingView.startLoading() 197 | } else { 198 | //当前结束时,再次点击回到初始状态 199 | //kotlin使用 200 | smartLoadingView.isFinished = false 201 | //java使用 202 | //smartLoadingView.setFinished(false); 203 | } 204 | ``` 205 |
206 | 207 | ##### 3.2 联网出结果,成功 or 失败同样调用如下 208 | ```java 209 | //true则走成功,false则走失败 210 | //kotlin使用 211 | smartLoadingView.finishLoading(true) { 212 | 213 | } 214 | 215 | //java使用 216 | smartLoadingView.finishLoading(true, success -> { 217 | 218 | }); 219 | ``` 220 |
221 | 222 | ##### 3.3 如果你不想使用动画,可以调用如下api。 223 | ```java 224 | //kotlin使用 225 | smartLoadingView.isFinished = true 226 | 227 | //java使用 228 | smartLoadingView.setFinished(true); 229 | ``` 230 |
231 | 232 | ## 属性表格(Attributes) 233 | 因为SmartLoadingView就是TextView的拓展控件。部分属性延用了系统属性 234 | 235 | |name|format|description|系统api| 236 | |:---:|:---:|:---:|:---:| 237 | |android:text|string|文案内容|是| 238 | |app:hl_animaled_text|string|动画结束文案,默认为text|否| 239 | |android:textColor|color|文案颜色|是| 240 | |app:hl_animaled_textColor|color|动画结束文字颜色,默认为textColor|否| 241 | |android:textSize|dimension|文案字体大小|是| 242 | |android:background|color|背景色|是| 243 | |app:hl_animaled_background|color|动画结束背景色,默认为background颜色值|否| 244 | |app:hl_corners_radius|dimension|圆角属性|否| 245 | |android:enabled|boolean|是否可被点击|是| 246 | |app:hl_unEnabled_background|color|不可点击状态下背景色|否| 247 | |app:hl_ellipsize|enum|reverse:来回滚动;marquee:跑马灯。需文字大于控件宽度生效|否| 248 | |app:hl_ellipsize_speed|integer|文字滚动速度|否| 249 | |app:hl_button_type|enum|5种buttonType样式|否| 250 |
251 | 252 | 253 | ## 方法表格(Method) 254 | |name|format|description| 255 | |:---:|:---:|:---:| 256 | |startLoading()|void|开启加载loading| 257 | |setFinished()|boolean|不带动画设置控件结束状态| 258 | |finishLoading()|boolean|带动画设置控件结束状态| 259 |
260 | 261 | ## 赞赏 262 | 263 | 如果你喜欢 SmartLoadingView 的功能,感觉 SmartLoadingView 帮助到了你,可以点右上角 "Star" 支持一下 谢谢! ^_^ 264 | 你也还可以扫描下面的二维码~ 请作者喝一杯咖啡。或者遇到工作中比较难实现的需求请作者帮忙。 265 | 266 | ![](https://github.com/lihangleo2/ShadowLayout/blob/master/showImages/pay_ali.png) ![](https://github.com/lihangleo2/ShadowLayout/blob/master/showImages/pay_wx.png) 267 | 268 | 269 | 如果在捐赠留言中备注名称,将会被记录到列表中~ 如果你也是github开源作者,捐赠时可以留下github项目地址或者个人主页地址,链接将会被添加到列表中 270 | ### [捐赠列表](https://github.com/lihangleo2/ShadowLayout/blob/master/showImages/friend.md) 271 |
272 | 273 | 274 | ## 其他作品 275 | [万能ViewPager2适配器SmartViewPager2Adapter](https://github.com/lihangleo2/ViewPager2Demo) 276 | [RichEditTextCopyToutiao](https://github.com/lihangleo2/RichEditTextCopyToutiao) 277 | [ShadowLayout](https://github.com/lihangleo2/ShadowLayout) 278 | 279 |
280 | 281 | 282 | 283 | ## 关于作者。 284 | Android工作多年了。前进的道路上是孤独的。如果你在学习的路上也感觉孤独,请和我一起。让我们在学习道路上少些孤独 285 | 286 | * QQ群: 209010674 android交流群(点击图标,可以直接加入) 287 |
288 | 289 | ## LICENSE 290 | 291 | ``` 292 | MIT License 293 | 294 | Copyright (c) 2019 leo 295 | 296 | Permission is hereby granted, free of charge, to any person obtaining a copy 297 | of this software and associated documentation files (the "Software"), to deal 298 | in the Software without restriction, including without limitation the rights 299 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 300 | copies of the Software, and to permit persons to whom the Software is 301 | furnished to do so, subject to the following conditions: 302 | 303 | The above copyright notice and this permission notice shall be included in all 304 | copies or substantial portions of the Software. 305 | ``` 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/atman.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/atman.jks -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'org.jetbrains.kotlin.android' 3 | 4 | android { 5 | compileSdkVersion 28 6 | defaultConfig { 7 | applicationId "com.lihang.mysmartloadingview" 8 | minSdkVersion 15 9 | targetSdkVersion 28 10 | versionCode 3 11 | versionName "2.0.2" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | dataBinding { 15 | enabled = true 16 | } 17 | 18 | //签名 (需要签名文件) 19 | signingConfigs { 20 | key { 21 | storeFile file("atman.jks")//签名文件 22 | storePassword "5888062" 23 | keyAlias "fastMedical" 24 | keyPassword "5888062"//签名密码 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 32 | signingConfig signingConfigs.key 33 | } 34 | } 35 | 36 | //多渠道打包 37 | flavorDimensions "mark" //相当于一个标记 38 | productFlavors { 39 | medical { dimension "mark" } 40 | } 41 | 42 | compileOptions { 43 | sourceCompatibility = 1.8 44 | targetCompatibility = 1.8 45 | } 46 | 47 | 48 | productFlavors.all { 49 | flavor -> flavor.manifestPlaceholders = [ATMAN_CHANNEL_VALUE: name] 50 | } 51 | 52 | //自定义打包时apk名字 53 | android.applicationVariants.all { variant -> 54 | variant.outputs.all { 55 | // abc_渠道名_版本名.apk 还可以拼接其他app内容:variant.versionCode variant.buildType.name 56 | outputFileName = "ShadowLayout_${variant.versionName}_${variant.buildType.name}_${new Date().format("yyyy-MM-dd_HH_mm")}.apk" 57 | 58 | } 59 | } 60 | } 61 | 62 | dependencies { 63 | implementation fileTree(include: ['*.jar'], dir: 'libs') 64 | implementation 'com.android.support:appcompat-v7:28.0.0' 65 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 66 | testImplementation 'junit:junit:4.12' 67 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 68 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 69 | implementation project(':smartloadview') 70 | implementation 'com.blankj:utilcode:1.28.0' 71 | } 72 | -------------------------------------------------------------------------------- /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 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/lihang/mysmartloadingview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.lihang.mysmartloadingview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.lihang.mysmartloadingview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/lihang/mysmartloadingview/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.lihang.mysmartloadingview 2 | 3 | import android.databinding.DataBindingUtil 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import android.view.Gravity 7 | import android.view.View 8 | import android.widget.Toast 9 | import com.blankj.utilcode.util.ActivityUtils 10 | import com.blankj.utilcode.util.ToastUtils 11 | import com.lihang.SmartLoadingView 12 | import com.lihang.SmartLoadingView.LoadingListener 13 | import com.lihang.mysmartloadingview.databinding.ActivityMainBinding 14 | 15 | class MainActivity : AppCompatActivity() { 16 | private lateinit var mBinding: ActivityMainBinding 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) 20 | supportActionBar?.title = "SmartLoadingView的使用" 21 | ToastUtils.setGravity(Gravity.CENTER,0,0) 22 | // 23 | mBinding.run { 24 | /** 25 | * 1.smart_full_screen:全屏 26 | * 不支持关注模式 27 | * */ 28 | 29 | //finishLoadingWithFullScreen 的使用 30 | smartFullscreenAuto.setOnClickListener { 31 | //1.1、开始加载 32 | smartFullscreenAuto.startLoading() 33 | 34 | //1.2、模拟2s后,加载成功并跳转(finishLoadingWithFullScreen会自动跳转页面并关闭当前页面) 35 | it.postDelayed({ 36 | smartFullscreenAuto.finishLoadingWithFullScreen(this@MainActivity, SecondActivity::class.java) 37 | //也可以自己处理动画回调 38 | //smartFullscreenAuto.finishLoading(true) {} 39 | }, 2000) 40 | } 41 | 42 | //模拟失败 43 | smartFullscreenFail.setOnClickListener { 44 | smartFullscreenFail.startLoading() 45 | it.postDelayed({ 46 | smartFullscreenFail.finishLoading(false) { 47 | ToastUtils.showShort("加载失败") 48 | } 49 | }, 2000) 50 | } 51 | 52 | 53 | //模拟失败,并展示失败文案 54 | smartFullscreenFailtxt.setOnClickListener { 55 | smartFullscreenFailtxt.startLoading() 56 | it.postDelayed({ 57 | //smartFullscreenFailtxt.setAnimaledText("我是自定义错误文案") 58 | smartFullscreenFailtxt.finishLoading(false) 59 | }, 2000) 60 | } 61 | 62 | 63 | /** 64 | * 2.smart_button 65 | * 支持关注模式 66 | * */ 67 | //正常情况下的:"点击关注" 和 "取消关注" 68 | smartButtonSuccess.setOnClickListener { 69 | if (!smartButtonSuccess.isFinished) { 70 | smartButtonSuccess.startLoading() 71 | it.postDelayed({ 72 | //2.1、加载成功--带loading动画的加载成功 73 | smartButtonSuccess.finishLoading(true) { 74 | ToastUtils.showShort("关注成功") 75 | } 76 | }, 2000) 77 | } else { 78 | //2.2、再次点击,取消关注。通过此方法设置不带动画 79 | smartButtonSuccess.isFinished = false 80 | } 81 | } 82 | 83 | //模拟关注失败 84 | smartButtonFail.setOnClickListener { 85 | smartButtonFail.startLoading() 86 | it.postDelayed({ 87 | smartButtonFail.finishLoading(false) { 88 | ToastUtils.showShort("关注失败,请稍后再试~") 89 | } 90 | }, 2000) 91 | } 92 | 93 | //不带动画的 ”关注“ 和 ”取消关注“ 94 | smartButtonNoanimal.setOnClickListener { 95 | smartButtonNoanimal.isFinished = !smartButtonNoanimal.isFinished 96 | } 97 | 98 | 99 | /** 100 | * 3.smart_tick 101 | * 支持关注模式 102 | * */ 103 | //这里的用法和 smart_button 类似 104 | smartTickDemo.setOnClickListener { 105 | if (!smartTickDemo.isFinished) { 106 | smartTickDemo.startLoading() 107 | it.postDelayed({ 108 | //2.1、加载成功--带loading动画的加载成功 109 | smartTickDemo.finishLoading(true) { 110 | ToastUtils.showShort("关注成功") 111 | } 112 | }, 2000) 113 | } else { 114 | //2.2、再次点击,取消关注。通过此方法设置不带动画 115 | smartTickDemo.isFinished = false 116 | } 117 | } 118 | 119 | 120 | /** 121 | * 4.smart_tick_hide 122 | * 支持关注模式 123 | * */ 124 | //这里的用法和 smart_button 类似 125 | smartTickHideDemo.setOnClickListener { 126 | if (!smartTickHideDemo.isFinished) { 127 | smartTickHideDemo.startLoading() 128 | it.postDelayed({ 129 | //2.1、加载成功--带loading动画的加载成功 130 | smartTickHideDemo.finishLoading(true) { 131 | ToastUtils.showShort("关注成功") 132 | } 133 | }, 2000) 134 | } else { 135 | //2.2、再次点击,取消关注。通过此方法设置不带动画 136 | smartTickHideDemo.isFinished = false 137 | } 138 | } 139 | 140 | 141 | /** 142 | * 5.smart_tick_center_hide 143 | * 支持关注模式 144 | * */ 145 | //这里的用法和 smart_button 类似 146 | smartTickCenterHideDemo.setOnClickListener { 147 | if (!smartTickCenterHideDemo.isFinished) { 148 | smartTickCenterHideDemo.startLoading() 149 | it.postDelayed({ 150 | //2.1、加载成功--带loading动画的加载成功 151 | smartTickCenterHideDemo.finishLoading(true) { 152 | ToastUtils.showShort("关注成功") 153 | } 154 | }, 2000) 155 | } else { 156 | //2.2、再次点击,取消关注。通过此方法设置不带动画 157 | smartTickCenterHideDemo.isFinished = false 158 | } 159 | } 160 | 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lihang/mysmartloadingview/MainJavaActivity.java: -------------------------------------------------------------------------------- 1 | package com.lihang.mysmartloadingview; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.os.Bundle; 5 | import android.os.PersistableBundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | 11 | import com.blankj.utilcode.util.ToastUtils; 12 | import com.lihang.SmartLoadingView; 13 | import com.lihang.mysmartloadingview.databinding.ActivityMainBinding; 14 | 15 | /** 16 | * @Author leo 17 | * @Address https://github.com/lihangleo2 18 | * @Date 2024/7/22 19 | */ 20 | public class MainJavaActivity extends AppCompatActivity { 21 | private ActivityMainBinding mBinding; 22 | 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); 27 | ToastUtils.setGravity(Gravity.CENTER,0,0); 28 | getSupportActionBar().setTitle("SmartLoadingView的使用"); 29 | /** 30 | * 1.smart_full_screen:全屏 31 | * 不支持关注模式 32 | * */ 33 | 34 | //finishLoadingWithFullScreen 的使用 35 | mBinding.smartFullscreenAuto.setOnClickListener(it -> { 36 | //1.1、开始加载 37 | mBinding.smartFullscreenAuto.startLoading(); 38 | 39 | //1.2、模拟2s后,加载成功并跳转(finishLoadingWithFullScreen会自动跳转页面并关闭当前页面) 40 | it.postDelayed(() -> { 41 | mBinding.smartFullscreenAuto.finishLoadingWithFullScreen(MainJavaActivity.this, SecondActivity.class); 42 | //也可以自己处理动画回调 43 | //mBinding.smartFullscreenAuto.finishLoading(true, success -> {}); 44 | }, 2000); 45 | 46 | }); 47 | 48 | //模拟失败 49 | mBinding.smartFullscreenFail.setOnClickListener(it -> { 50 | mBinding.smartFullscreenFail.startLoading(); 51 | it.postDelayed(() -> { 52 | mBinding.smartFullscreenFail.finishLoading(false); 53 | ToastUtils.showShort("加载失败"); 54 | }, 2000); 55 | }); 56 | 57 | //模拟失败,并展示失败文案 58 | mBinding.smartFullscreenFailtxt.setOnClickListener(it -> { 59 | mBinding.smartFullscreenFailtxt.startLoading(); 60 | it.postDelayed(() -> { 61 | //mBinding.smartFullscreenFailtxt.setAnimaledText("我是自定义错误文案"); 62 | mBinding.smartFullscreenFailtxt.finishLoading(false); 63 | }, 2000); 64 | }); 65 | 66 | 67 | /** 68 | * 2.smart_button 69 | * 支持关注模式 70 | * */ 71 | //正常情况下的:"点击关注" 和 "取消关注" 72 | mBinding.smartButtonSuccess.setOnClickListener(it->{ 73 | if (!mBinding.smartButtonSuccess.isFinished()){ 74 | mBinding.smartButtonSuccess.startLoading(); 75 | it.postDelayed(()->{ 76 | //2.1、加载成功--带loading动画的加载成功 77 | mBinding.smartButtonSuccess.finishLoading(true,success -> { 78 | ToastUtils.showShort("关注成功"); 79 | }); 80 | },2000); 81 | }else { 82 | //2.2、再次点击,取消关注。通过此方法设置不带动画 83 | mBinding.smartButtonSuccess.setFinished(false); 84 | } 85 | }); 86 | 87 | //模拟关注失败 88 | mBinding.smartButtonFail.setOnClickListener(it->{ 89 | mBinding.smartButtonFail.startLoading(); 90 | it.postDelayed(()->{ 91 | mBinding.smartButtonFail.finishLoading(false,success -> { 92 | ToastUtils.showShort("关注失败,请稍后再试~"); 93 | }); 94 | },2000); 95 | }); 96 | 97 | 98 | //不带动画的 ”关注“ 和 ”取消关注“ 99 | mBinding.smartButtonNoanimal.setOnClickListener(it->{ 100 | if (!mBinding.smartButtonNoanimal.isFinished()){ 101 | mBinding.smartButtonNoanimal.setFinished(true); 102 | }else { 103 | mBinding.smartButtonNoanimal.setFinished(false); 104 | } 105 | }); 106 | 107 | 108 | /** 109 | * 3.smart_tick 110 | * 支持关注模式 111 | * */ 112 | //这里的用法和 smart_button 类似 113 | mBinding.smartTickDemo.setOnClickListener(it->{ 114 | if (!mBinding.smartTickDemo.isFinished()){ 115 | mBinding.smartTickDemo.startLoading(); 116 | it.postDelayed(()->{ 117 | mBinding.smartTickDemo.finishLoading(true,success -> { 118 | ToastUtils.showShort("关注成功"); 119 | }); 120 | },2000); 121 | }else { 122 | mBinding.smartTickDemo.setFinished(false); 123 | } 124 | }); 125 | 126 | 127 | /** 128 | * 4.smart_tick_hide 129 | * 支持关注模式 130 | * */ 131 | //这里的用法和 smart_button 类似 132 | mBinding.smartTickHideDemo.setOnClickListener(it->{ 133 | if (!mBinding.smartTickHideDemo.isFinished()){ 134 | mBinding.smartTickHideDemo.startLoading(); 135 | it.postDelayed(()->{ 136 | mBinding.smartTickHideDemo.finishLoading(true,success -> { 137 | ToastUtils.showShort("关注成功"); 138 | }); 139 | },2000); 140 | }else { 141 | mBinding.smartTickHideDemo.setFinished(false); 142 | } 143 | }); 144 | 145 | 146 | /** 147 | * 5.smart_tick_center_hide 148 | * 支持关注模式 149 | * */ 150 | //这里的用法和 smart_button 类似 151 | mBinding.smartTickCenterHideDemo.setOnClickListener(it->{ 152 | if (!mBinding.smartTickCenterHideDemo.isFinished()){ 153 | mBinding.smartTickCenterHideDemo.startLoading(); 154 | it.postDelayed(()->{ 155 | mBinding.smartTickCenterHideDemo.finishLoading(true,success -> { 156 | ToastUtils.showShort("关注成功"); 157 | }); 158 | },2000); 159 | }else { 160 | mBinding.smartTickCenterHideDemo.setFinished(false); 161 | } 162 | }); 163 | 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/lihang/mysmartloadingview/SecondActivity.java: -------------------------------------------------------------------------------- 1 | package com.lihang.mysmartloadingview; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * Created by leo 9 | * on 2019/5/23. 10 | */ 11 | public class SecondActivity extends AppCompatActivity { 12 | @Override 13 | protected void onCreate(@Nullable Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_second); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/anim/scale_test2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/scale_test_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cb_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /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/shape_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_for_login_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 18 | 19 | 22 | 23 | 34 | 35 | 49 | 50 | 64 | 65 | 82 | 83 | 84 | 96 | 97 | 114 | 115 | 116 | 133 | 134 | 151 | 152 | 153 | 165 | 166 | 167 | 181 | 182 | 194 | 195 | 209 | 210 | 222 | 223 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_second.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/check_off_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/check_off_1.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/check_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/check_on.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/girl_head.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/girl_head.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/login_password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/login_password.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/login_phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/login_phone.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/main_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xhdpi/main_bg.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | #000000 7 | #dbdbdb 8 | #00000000 9 | #ffffff 10 | #484848 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MySmartLoadingView 3 | 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | 18 | 24 | 25 | 28 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/test/java/com/lihang/mysmartloadingview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.lihang.mysmartloadingview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | ext { 6 | kotlin_version = '1.6.0' 7 | } 8 | repositories { 9 | google() 10 | jcenter() 11 | } 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:4.0.0' 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | 16 | 17 | // NOTE: Do not place your application dependencies here; they belong 18 | // in the individual module build.gradle files 19 | } 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | google() 25 | jcenter() 26 | } 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /gif/SmartLoadingView_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/SmartLoadingView_.png -------------------------------------------------------------------------------- /gif/fullScreen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/fullScreen.gif -------------------------------------------------------------------------------- /gif/fullScreen_fail_show.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/fullScreen_fail_show.gif -------------------------------------------------------------------------------- /gif/fullScreen_fail_toast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/fullScreen_fail_toast.gif -------------------------------------------------------------------------------- /gif/smartButton.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/smartButton.gif -------------------------------------------------------------------------------- /gif/smartButton_fail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/smartButton_fail.gif -------------------------------------------------------------------------------- /gif/smartButton_noAnimal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/smartButton_noAnimal.gif -------------------------------------------------------------------------------- /gif/smartTick.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/smartTick.gif -------------------------------------------------------------------------------- /gif/smartTick_hide.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/smartTick_hide.gif -------------------------------------------------------------------------------- /gif/smartTick_hide_center.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gif/smartTick_hide_center.gif -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihangleo2/SmartLoadingView/774085bfede245944df8d5b81fd371752d34af54/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':smartloadview' 2 | -------------------------------------------------------------------------------- /smartloadview/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /smartloadview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'org.jetbrains.kotlin.android' 3 | 4 | android { 5 | compileSdkVersion 28 6 | 7 | 8 | 9 | defaultConfig { 10 | minSdkVersion 15 11 | targetSdkVersion 28 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: 'libs', include: ['*.jar']) 30 | implementation 'com.android.support:appcompat-v7:28.0.0' 31 | // testImplementation 'junit:junit:4.12' 32 | // androidTestImplementation 'com.android.support.test:runner:1.0.2' 33 | // androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 34 | } 35 | -------------------------------------------------------------------------------- /smartloadview/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 22 | -------------------------------------------------------------------------------- /smartloadview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /smartloadview/src/main/java/com/lihang/SmartLoadingView.java: -------------------------------------------------------------------------------- 1 | package com.lihang; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorSet; 5 | import android.animation.ObjectAnimator; 6 | import android.animation.ValueAnimator; 7 | import android.app.Activity; 8 | import android.content.Context; 9 | import android.content.res.ColorStateList; 10 | import android.content.res.TypedArray; 11 | import android.graphics.Canvas; 12 | import android.graphics.Color; 13 | import android.graphics.DashPathEffect; 14 | import android.graphics.Paint; 15 | import android.graphics.Path; 16 | import android.graphics.PathEffect; 17 | import android.graphics.PathMeasure; 18 | import android.graphics.PorterDuff; 19 | import android.graphics.PorterDuffXfermode; 20 | import android.graphics.Rect; 21 | import android.graphics.RectF; 22 | import android.graphics.drawable.ColorDrawable; 23 | import android.graphics.drawable.Drawable; 24 | import android.support.v7.widget.AppCompatTextView; 25 | import android.text.TextUtils; 26 | import android.util.AttributeSet; 27 | import android.util.Log; 28 | import android.view.Gravity; 29 | import android.view.View; 30 | import android.view.ViewGroup; 31 | import android.view.animation.Animation; 32 | import android.view.animation.AnimationUtils; 33 | import android.view.animation.AnticipateInterpolator; 34 | import android.view.animation.LinearInterpolator; 35 | import android.widget.FrameLayout; 36 | 37 | import com.lihang.help.CirclBigView; 38 | import com.lihang.help.OkView; 39 | import com.lihang.smartloadview.R; 40 | import com.lihang.help.UIUtil; 41 | 42 | 43 | /** 44 | * @Author leo 45 | * @Address https://github.com/lihangleo2 46 | * @Date 2024/6/21 重构 47 | */ 48 | 49 | public class SmartLoadingView extends AppCompatTextView { 50 | private static int SMART_TICK = 1; 51 | private static int SMART_TICK_HIDE = 2; 52 | private static int SMART_TICK_CENTER_HIDE = 3; 53 | private static int SMART_BUTTON = 4; 54 | private static int SMART_FULL_SCREEN = 5; 55 | 56 | 57 | //view的宽度 58 | private int width; 59 | 60 | //View的高度 61 | private int height; 62 | 63 | //圆角半径 64 | private int circleAngle; 65 | 66 | //矩形2边需要缩短的距离 67 | private int default_all_distance; 68 | 69 | //当前矩形在x轴left的位置 70 | private int current_left; 71 | 72 | //动画执行时间 73 | private int duration = 500; 74 | 75 | //圆角矩形画笔 76 | private Paint paint; 77 | 78 | 79 | //对勾(√)画笔 80 | private Paint okPaint; 81 | 82 | //文字绘制所在矩形 83 | private Rect textRect = new Rect(); 84 | 85 | //根据view的大小设置成矩形 86 | private RectF rectf = new RectF(); 87 | 88 | /** 89 | * 动画集 90 | */ 91 | //这是开始的启动 92 | private AnimatorSet animatorSet = new AnimatorSet(); 93 | //这是网络错误的 94 | private AnimatorSet animatorNetfail = new AnimatorSet(); 95 | 96 | //矩形到正方形过度的动画 97 | private ValueAnimator animator_rect_to_square; 98 | 99 | //正方形到矩形动画 100 | private ValueAnimator animator_squareToRect; 101 | 102 | //矩形到圆角矩形过度的动画 103 | private ValueAnimator animator_rect_to_angle; 104 | 105 | //圆角矩形到矩形的动画 106 | private ValueAnimator animator_angle_to_rect; 107 | 108 | 109 | /** 110 | * 以下是绘制对勾动画 111 | */ 112 | 113 | //是否开始绘制对勾 114 | private boolean startDrawOk = false; 115 | 116 | //绘制对勾(√)的动画 117 | private ValueAnimator animator_draw_ok; 118 | 119 | //对路径处理实现绘制动画效果 120 | private PathEffect effect; 121 | 122 | //路径--用来获取对勾的路径 123 | private Path path = new Path(); 124 | 125 | //取路径的长度 126 | private PathMeasure pathMeasure; 127 | 128 | /** 129 | * 加载loading动画相关 130 | */ 131 | //是否开始绘画,加载转圈动画 132 | private boolean isDrawLoading = false; 133 | //是否处于加载状态,注意,这里和上面是2个概念,只要点击按钮,没有走错误和走正确的都视为在加载状态下 134 | private boolean isLoading = false; 135 | private int startAngle = 0; 136 | private int progAngle = 30; 137 | private boolean isAdd = true; 138 | 139 | /** 140 | * 文字画笔相关 141 | */ 142 | //文字画笔 143 | private Paint textPaint; 144 | //文字超过一行时,进行的文字滚动动画 145 | private ValueAnimator animator_text_scroll;//这只是模式之一 146 | private ValueAnimator animator_marque; 147 | private int drawTextStart; 148 | private int drawMarqueTextStart; 149 | 150 | 151 | /** 152 | * 以下是自定义属性 153 | */ 154 | 155 | //按钮背景色 156 | private int backgroundColor; 157 | 158 | //不可点击的背景颜色 159 | private int unEnabled_backgroundColor; 160 | 161 | //从用户获得的圆角 162 | private int corners_radius; 163 | 164 | //文字省略模模式;reverse--1:来回滚动 marquee--2:跑马灯效果 165 | private int ellipsize = 1; 166 | 167 | //文字省略模式下,文字的滚动速度(只有文字滚动时生效) 168 | private int ellipsize_speed; 169 | 170 | 171 | //动画结束背景色 172 | private int animaled_backgroundColor; 173 | 174 | //动画结束文字颜色 175 | private int animaled_textColor; 176 | 177 | //按钮文字 178 | private String normalString = ""; 179 | private String mAnimaledText = ""; 180 | private String currentString;//当前要绘画的TextStr 181 | 182 | //当前字体颜色值 183 | private int textColor; 184 | //当前字体颜色值初始颜色 185 | private int textColorOriginal; 186 | //当前字体透明度 187 | private int textAlpha; 188 | 189 | //这是全屏动画 190 | private CirclBigView circlBigView; 191 | 192 | //SmartLoadingView模式 193 | private int mButtonType = SMART_FULL_SCREEN;//关注的样式,默认是正常样式 194 | 195 | 196 | public SmartLoadingView(Context context) { 197 | this(context, null); 198 | } 199 | 200 | public SmartLoadingView(Context context, AttributeSet attrs) { 201 | this(context, attrs, 0); 202 | } 203 | 204 | public SmartLoadingView(Context context, AttributeSet attrs, int defStyleAttr) { 205 | super(context, attrs, defStyleAttr); 206 | circlBigView = new CirclBigView(getContext()); 207 | init(attrs); 208 | initPaint(); 209 | } 210 | 211 | 212 | /** 213 | * init 214 | * ********************************************************************************************* 215 | */ 216 | 217 | private void init(AttributeSet attrs) { 218 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SmartLoadingView); 219 | // 设置title 220 | if (TextUtils.isEmpty(getText())) { 221 | currentString = normalString; 222 | } else { 223 | normalString = (String) getText(); 224 | currentString = normalString; 225 | } 226 | mAnimaledText = currentString; 227 | 228 | String animaledText = typedArray.getString(R.styleable.SmartLoadingView_hl_animaled_text); 229 | if (!TextUtils.isEmpty(animaledText)) { 230 | mAnimaledText = animaledText; 231 | } 232 | unEnabled_backgroundColor = typedArray.getColor(R.styleable.SmartLoadingView_hl_unEnabled_background, getResources().getColor(R.color.blackbb)); 233 | //赋予背景色默认颜色值 234 | backgroundColor = getResources().getColor(R.color.guide_anim); 235 | Drawable background = getBackground(); 236 | if (background instanceof ColorDrawable) { 237 | backgroundColor = ((ColorDrawable) background).getColor(); 238 | } 239 | animaled_backgroundColor = typedArray.getColor(R.styleable.SmartLoadingView_hl_animaled_background, backgroundColor); 240 | 241 | //获取文字颜色值 242 | ColorStateList textColors = getTextColors(); 243 | final int[] drawableState = getDrawableState(); 244 | //获取textView默认颜色值 245 | textColor = textColors.getColorForState(drawableState, 0); 246 | textColorOriginal = textColor; 247 | animaled_textColor = typedArray.getColor(R.styleable.SmartLoadingView_hl_animaled_textColor, textColor); 248 | 249 | corners_radius = (int) typedArray.getDimension(R.styleable.SmartLoadingView_hl_corners_radius, getResources().getDimension(R.dimen.slv_default_corner)); 250 | ellipsize = typedArray.getInt(R.styleable.SmartLoadingView_hl_ellipsize, 1); 251 | ellipsize_speed = typedArray.getInt(R.styleable.SmartLoadingView_hl_ellipsize_speed, 400); 252 | mButtonType = typedArray.getInt(R.styleable.SmartLoadingView_hl_button_type, SMART_FULL_SCREEN); 253 | 254 | int padding_horizontal = (int) getResources().getDimension(R.dimen.slv_padding_horizontal); 255 | int padding_vertical = (int) getResources().getDimension(R.dimen.slv_padding_vertical); 256 | int paddingTop = getPaddingTop() == 0 ? padding_vertical : getPaddingTop(); 257 | int paddingBottom = getPaddingBottom() == 0 ? padding_vertical : getPaddingBottom(); 258 | int paddingLeft = getPaddingLeft() == 0 ? padding_horizontal : getPaddingLeft(); 259 | int paddingRight = getPaddingRight() == 0 ? padding_horizontal : getPaddingRight(); 260 | setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); 261 | setBackgroundColor(0); 262 | setMaxLines(1); 263 | setGravity(Gravity.CENTER); 264 | } 265 | 266 | //画笔初始化 267 | private void initPaint() { 268 | 269 | //矩形画笔 270 | paint = new Paint(Paint.ANTI_ALIAS_FLAG); 271 | paint.setStrokeWidth(4); 272 | paint.setStyle(Paint.Style.FILL); 273 | paint.setAntiAlias(true); 274 | 275 | if (isEnabled()) { 276 | paint.setColor(backgroundColor); 277 | } else { 278 | paint.setColor(unEnabled_backgroundColor); 279 | } 280 | 281 | //打勾画笔 282 | okPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 283 | okPaint.setStrokeWidth(5); 284 | okPaint.setStyle(Paint.Style.STROKE); 285 | okPaint.setAntiAlias(true); 286 | okPaint.setStrokeCap(Paint.Cap.ROUND); 287 | 288 | 289 | // ColorStateList textColors = getTextColors(); 290 | // final int[] drawableState = getDrawableState(); 291 | // //获取textView默认颜色值 292 | // textColor = textColors.getColorForState(drawableState, 0); 293 | okPaint.setColor(textColor); 294 | textAlpha = Color.alpha(textColor); 295 | 296 | 297 | //文字画笔 298 | textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 299 | textPaint.setTextSize(getTextSize()); 300 | textPaint.setColor(textColor); 301 | textPaint.setAntiAlias(true); 302 | } 303 | 304 | //动画初始化 305 | private void initAnimation() { 306 | set_rect_to_circle_animation(); 307 | set_draw_ok_animation(); 308 | animatorSet.play(animator_rect_to_square).with(animator_rect_to_angle); 309 | animatorNetfail.play(animator_squareToRect).with(animator_angle_to_rect); 310 | } 311 | 312 | //绘制对勾 313 | private void initOk() { 314 | //对勾的路径 315 | path.moveTo(default_all_distance + height / 8 * 3, height / 2); 316 | path.lineTo(default_all_distance + height / 2, height / 5 * 3); 317 | path.lineTo(default_all_distance + height / 3 * 2, height / 5 * 2); 318 | pathMeasure = new PathMeasure(path, true); 319 | } 320 | 321 | /** 322 | * 动画方法 323 | * ********************************************************************************************* 324 | */ 325 | /* 326 | * 设置圆角矩形过度到圆的动画 327 | * &圆到圆角矩形 328 | *

329 | * 矩形到圆角矩形的动画 330 | * &圆角矩形到矩形的动画 331 | */ 332 | private void set_rect_to_circle_animation() { 333 | animator_rect_to_square = ValueAnimator.ofInt(0, default_all_distance); 334 | animator_rect_to_square.setDuration(duration); 335 | animator_rect_to_square.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 336 | @Override 337 | public void onAnimationUpdate(ValueAnimator animation) { 338 | current_left = (int) animation.getAnimatedValue(); 339 | 340 | int nowAlpha = textAlpha / 2 - (current_left * textAlpha / default_all_distance) < 0 ? 0 : textAlpha / 2 - (current_left * textAlpha / default_all_distance); 341 | textPaint.setColor(addAlpha(textColor, nowAlpha)); 342 | if (current_left == default_all_distance) { 343 | isDrawLoading = true; 344 | } 345 | invalidate(); 346 | 347 | } 348 | }); 349 | 350 | 351 | animator_rect_to_angle = ValueAnimator.ofInt(corners_radius, height / 2); 352 | animator_rect_to_angle.setDuration(duration); 353 | animator_rect_to_angle.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 354 | @Override 355 | public void onAnimationUpdate(ValueAnimator animation) { 356 | circleAngle = (int) animation.getAnimatedValue(); 357 | invalidate(); 358 | } 359 | }); 360 | 361 | 362 | animator_squareToRect = ValueAnimator.ofInt(default_all_distance, 0); 363 | animator_squareToRect.setDuration(duration); 364 | animator_squareToRect.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 365 | @Override 366 | public void onAnimationUpdate(ValueAnimator animation) { 367 | current_left = (int) animation.getAnimatedValue(); 368 | //当控件扩展到一半时再显示文字,不然当文案过长时,会先显示文字。超过控件 369 | if (current_left <= default_all_distance / 2) { 370 | int nowAlpha = (default_all_distance / 2 - current_left) * textAlpha / (default_all_distance / 2); 371 | textPaint.setColor(addAlpha(textColor, nowAlpha)); 372 | } 373 | //错误动画全部走完之后,才能被点击 374 | if (current_left == 0) { 375 | isLoading = false; 376 | setClickable(true); 377 | } 378 | isDrawLoading = false; 379 | startDrawOk = false; 380 | postInvalidate(); 381 | } 382 | }); 383 | 384 | 385 | animator_angle_to_rect = ValueAnimator.ofInt(height / 2, corners_radius); 386 | animator_angle_to_rect.setDuration(duration); 387 | animator_angle_to_rect.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 388 | @Override 389 | public void onAnimationUpdate(ValueAnimator animation) { 390 | circleAngle = (int) animation.getAnimatedValue(); 391 | postInvalidate(); 392 | } 393 | }); 394 | 395 | } 396 | 397 | /* 398 | * 绘制对勾的动画 399 | */ 400 | private void set_draw_ok_animation() { 401 | animator_draw_ok = ValueAnimator.ofFloat(1, 0); 402 | animator_draw_ok.setDuration(duration); 403 | animator_draw_ok.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 404 | @Override 405 | public void onAnimationUpdate(ValueAnimator animation) { 406 | startDrawOk = true; 407 | isDrawLoading = false; 408 | float value = (Float) animation.getAnimatedValue(); 409 | effect = new DashPathEffect(new float[]{pathMeasure.getLength(), pathMeasure.getLength()}, value * pathMeasure.getLength()); 410 | okPaint.setPathEffect(effect); 411 | invalidate(); 412 | 413 | } 414 | }); 415 | } 416 | 417 | 418 | /* 419 | * 画圆角矩形 420 | * */ 421 | private void draw_oval_to_circle(Canvas canvas) { 422 | rectf.left = current_left; 423 | rectf.top = 0; 424 | rectf.right = width - current_left; 425 | rectf.bottom = height; 426 | 427 | //画圆角矩形 428 | canvas.drawRoundRect(rectf, circleAngle, circleAngle, paint); 429 | } 430 | 431 | /** 432 | * * 433 | * ********************************************************************************************* 434 | */ 435 | @Override 436 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 437 | super.onSizeChanged(w, h, oldw, oldh); 438 | if (width == 0) { 439 | width = w; 440 | height = h; 441 | if (corners_radius > (height / 2)) { 442 | corners_radius = height / 2; 443 | } 444 | circleAngle = corners_radius; 445 | default_all_distance = (w - h) / 2; 446 | initOk(); 447 | initAnimation(); 448 | //如果不是精准模式,我们代码里设置第一次的长宽,成为精准模式 449 | //这样避免,更改文字内容时,总是会改变控件的长宽 450 | setWidth(width); 451 | setHeight(height); 452 | // setEnabled(isEnabled()); 453 | } 454 | } 455 | 456 | //绘制文字相关 457 | private void drawText(final Canvas canvas) { 458 | int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG); 459 | rectf.left = current_left + getPaddingLeft(); 460 | rectf.top = 0; 461 | rectf.right = width - current_left - getPaddingRight(); 462 | rectf.bottom = height; 463 | //画圆角矩形 464 | canvas.drawRoundRect(rectf, circleAngle, circleAngle, paint); 465 | 466 | //设置混合模式 467 | textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 468 | 469 | textRect.left = getPaddingLeft(); 470 | textRect.top = 0; 471 | textRect.right = width - getPaddingRight(); 472 | textRect.bottom = height; 473 | Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt(); 474 | final int baseline = (textRect.bottom + textRect.top - fontMetrics.bottom - fontMetrics.top) / 2; 475 | //这是测量文字的长度。 476 | int myTotal = (int) (textPaint.measureText(currentString) + getPaddingRight() + getPaddingLeft()); 477 | if (myTotal > getWidth()) { 478 | if (ellipsize == 1) { 479 | textPaint.setTextAlign(Paint.Align.LEFT); 480 | if (animator_text_scroll == null && !isLoading) { 481 | //此时文字长度已经超过一行,进行文字滚动 482 | animator_text_scroll = ValueAnimator.ofInt(textRect.left, (int) (textRect.left - textPaint.measureText(currentString) + (getWidth() - getPaddingLeft() - getPaddingRight()))); 483 | animator_text_scroll.setDuration(currentString.length() * ellipsize_speed); 484 | animator_text_scroll.setRepeatMode(ValueAnimator.REVERSE); 485 | animator_text_scroll.setRepeatCount(-1); 486 | animator_text_scroll.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 487 | @Override 488 | public void onAnimationUpdate(ValueAnimator animation) { 489 | drawTextStart = (int) animation.getAnimatedValue(); 490 | postInvalidate(); 491 | } 492 | }); 493 | animator_text_scroll.start(); 494 | } 495 | canvas.drawText(currentString, drawTextStart, baseline, textPaint); 496 | } else { 497 | textPaint.setTextAlign(Paint.Align.LEFT); 498 | if (animator_text_scroll == null && !isLoading) { 499 | //此时文字长度已经超过一行,进行文字滚动 500 | animator_text_scroll = ValueAnimator.ofInt(textRect.left, (int) (textRect.left - textPaint.measureText(currentString))); 501 | animator_text_scroll.setDuration(currentString.length() * ellipsize_speed); 502 | animator_text_scroll.setInterpolator(new LinearInterpolator()); 503 | animator_text_scroll.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 504 | @Override 505 | public void onAnimationUpdate(ValueAnimator animation) { 506 | 507 | drawTextStart = (int) animation.getAnimatedValue(); 508 | postInvalidate(); 509 | if (drawTextStart == textRect.left) { 510 | if (animator_marque != null) { 511 | animator_marque.cancel(); 512 | animator_marque = null; 513 | } 514 | } 515 | if (animator_marque == null && !isLoading && drawTextStart <= (int) (textRect.left - textPaint.measureText(currentString) + (getWidth() - getPaddingLeft() - getPaddingRight()) - (getWidth() - getPaddingLeft() - getPaddingRight()) / 3)) { 516 | int duration = (int) (((currentString.length() * ellipsize_speed) * (textRect.right - textRect.left)) / textPaint.measureText(currentString)); 517 | animator_marque = ValueAnimator.ofInt(textRect.right, textRect.left); 518 | animator_marque.setDuration(duration); 519 | animator_marque.setInterpolator(new LinearInterpolator()); 520 | animator_marque.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 521 | @Override 522 | public void onAnimationUpdate(ValueAnimator animation) { 523 | drawMarqueTextStart = (int) animation.getAnimatedValue(); 524 | if (drawMarqueTextStart == textRect.left) { 525 | SmartLoadingView.this.postDelayed(new Runnable() { 526 | @Override 527 | public void run() { 528 | if (animator_text_scroll != null) { 529 | animator_text_scroll.cancel(); 530 | animator_text_scroll = null; 531 | postInvalidate(); 532 | } 533 | } 534 | }, 1500); 535 | } 536 | postInvalidate(); 537 | } 538 | }); 539 | animator_marque.start(); 540 | } 541 | } 542 | }); 543 | animator_text_scroll.start(); 544 | } 545 | if (animator_marque != null) { 546 | canvas.drawText(currentString, drawMarqueTextStart, baseline, textPaint); 547 | } 548 | canvas.drawText(currentString, drawTextStart, baseline, textPaint); 549 | } 550 | 551 | } else { 552 | cancleScroll(); 553 | textPaint.setTextAlign(Paint.Align.CENTER); 554 | drawTextStart = textRect.left; 555 | canvas.drawText(currentString, textRect.centerX(), baseline, textPaint); 556 | } 557 | 558 | // 还原混合模式 559 | textPaint.setXfermode(null); 560 | // 还原画布 561 | canvas.restoreToCount(sc); 562 | } 563 | 564 | @Override 565 | protected void onDraw(Canvas canvas) { 566 | draw_oval_to_circle(canvas); 567 | drawText(canvas); 568 | 569 | //绘制加载进度 570 | if (isDrawLoading) { 571 | canvas.drawArc(new RectF(width / 2 - height / 2 + height / 4, height / 4, width / 2 + height / 2 - height / 4, height / 2 + height / 2 - height / 4), startAngle, progAngle, false, okPaint); 572 | startAngle += 6; 573 | if (progAngle >= 270) { 574 | progAngle -= 2; 575 | isAdd = false; 576 | } else if (progAngle <= 45) { 577 | progAngle += 6; 578 | isAdd = true; 579 | } else { 580 | if (isAdd) { 581 | progAngle += 6; 582 | } else { 583 | progAngle -= 2; 584 | } 585 | } 586 | postInvalidate(); 587 | } 588 | 589 | //绘制打勾 590 | if (startDrawOk) { 591 | canvas.drawPath(path, okPaint); 592 | } 593 | 594 | } 595 | 596 | 597 | @Override 598 | protected void onDetachedFromWindow() { 599 | super.onDetachedFromWindow(); 600 | cancleScroll(); 601 | } 602 | 603 | 604 | //给TextView字体设置透明度。 605 | private int addAlpha(int color, int alpha) { 606 | int red = Color.red(color); 607 | int green = Color.green(color); 608 | int blue = Color.blue(color); 609 | return Color.argb(alpha, red, green, blue); 610 | } 611 | 612 | //取消所有动画 613 | private void cancleScroll() { 614 | if (animator_text_scroll != null) { 615 | animator_text_scroll.cancel(); 616 | animator_text_scroll = null; 617 | } 618 | 619 | if (animator_marque != null) { 620 | animator_marque.cancel(); 621 | animator_marque = null; 622 | } 623 | } 624 | 625 | 626 | /** 627 | * Function 628 | * ********************************************************************************************* 629 | */ 630 | 631 | @Override 632 | public void setText(CharSequence text, BufferType type) { 633 | super.setText(text, type); 634 | normalString = (String) text; 635 | currentString = (String) text; 636 | postInvalidate(); 637 | } 638 | 639 | public void setAnimaledText(CharSequence text) { 640 | mAnimaledText = (String) text; 641 | } 642 | 643 | @Override 644 | public void setEnabled(boolean enabled) { 645 | super.setEnabled(enabled); 646 | if (enabled) { 647 | if (paint != null) paint.setColor(backgroundColor); 648 | postInvalidate(); 649 | } else { 650 | if (paint != null) paint.setColor(unEnabled_backgroundColor); 651 | postInvalidate(); 652 | } 653 | } 654 | 655 | //快速点击解决 656 | private long startLoading_click_millons = 0L; 657 | 658 | 659 | /** 660 | * SmartLoadingView -- startLoading:开启加载动画 661 | */ 662 | public void startLoading() { 663 | long currentMillons = System.currentTimeMillis(); 664 | if ((currentMillons - startLoading_click_millons) < 500L) { 665 | return; 666 | } 667 | startLoading_click_millons = currentMillons; 668 | //没有在loading的情况下才能点击(没有在请求网络的情况下) 669 | if (!isLoading) { 670 | textColor = textColorOriginal; 671 | cancleScroll(); 672 | startDrawOk = false; 673 | currentString = normalString; 674 | this.setClickable(false); 675 | paint.setColor(backgroundColor); 676 | isLoading = true; 677 | animatorSet.start(); 678 | } 679 | } 680 | 681 | private boolean isFinished; 682 | 683 | public boolean isFinished() { 684 | return isFinished; 685 | } 686 | 687 | private long finished_click_millons = 0L; 688 | 689 | /** 690 | * SmartLoadingView -- setFinished:设置SmartLoadingView的finished状态(无动画) 691 | */ 692 | public void setFinished(boolean success) { 693 | long currentMillons = System.currentTimeMillis(); 694 | if ((currentMillons - finished_click_millons) < 500L) { 695 | return; 696 | } 697 | finished_click_millons = currentMillons; 698 | 699 | if (mButtonType == SMART_FULL_SCREEN) { 700 | //全屏 701 | throw new IllegalArgumentException("hl_button_type = \"smart_full_screen\",不属于关注模式"); 702 | } else if (mButtonType == SMART_BUTTON) { 703 | //正常方式 704 | setFinishedReal(success, SMART_BUTTON); 705 | } else if (mButtonType == SMART_TICK) { 706 | //打勾方式 707 | setFinishedReal(success, SMART_TICK); 708 | } else if (mButtonType == SMART_TICK_HIDE) { 709 | //打勾--隐藏方式 710 | setFinishedReal(success, SMART_TICK_HIDE); 711 | } else if (mButtonType == SMART_TICK_CENTER_HIDE) { 712 | //打勾--移至中间--隐藏方式 713 | setFinishedReal(success, SMART_TICK_CENTER_HIDE); 714 | } 715 | } 716 | 717 | private void setFinishedReal(final boolean success, final int buttonType) { 718 | //兼容控件还未渲染到页面上 719 | if (getWidth() == 0 && getHeight() == 0) { 720 | SmartLoadingView.this.addOnLayoutChangeListener(new OnLayoutChangeListener() { 721 | @Override 722 | public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) { 723 | changeButtonStatus(success, buttonType); 724 | removeOnLayoutChangeListener(this); 725 | } 726 | }); 727 | } else { 728 | changeButtonStatus(success, buttonType); 729 | } 730 | 731 | } 732 | 733 | public void finishLoading() { 734 | finishLoading(true, null); 735 | } 736 | 737 | public void finishLoading(boolean success) { 738 | finishLoading(success, null); 739 | } 740 | 741 | public void finishLoading(LoadingListener listener) { 742 | finishLoading(true, listener); 743 | } 744 | 745 | 746 | /** 747 | * SmartLoadingView -- finishLoading:结束SmartLoadingView的finished状态(动画过渡) 748 | */ 749 | private long finishLoading_click_millons = 0L; 750 | 751 | public void finishLoading(boolean success, LoadingListener listener) { 752 | long currentMillons = System.currentTimeMillis(); 753 | if ((currentMillons - finishLoading_click_millons) < 500L) { 754 | return; 755 | } 756 | finishLoading_click_millons = currentMillons; 757 | 758 | if (mButtonType == SMART_FULL_SCREEN) { 759 | if (success) { 760 | fullScreen(listener); 761 | } else { 762 | backToEnd(listener, false); 763 | } 764 | } else if (mButtonType == SMART_BUTTON) { 765 | if (success) { 766 | isFinished = true; 767 | backToEnd(listener, true); 768 | } else { 769 | backToStart(listener, false); 770 | } 771 | } else if (mButtonType == SMART_TICK) { 772 | if (success) { 773 | isFinished = true; 774 | startTick(listener); 775 | } else { 776 | backToStart(listener, false); 777 | } 778 | } else if (mButtonType == SMART_TICK_HIDE) { 779 | if (success) { 780 | isFinished = true; 781 | startTickHide(listener); 782 | } else { 783 | backToStart(listener, false); 784 | } 785 | } else if (mButtonType == SMART_TICK_CENTER_HIDE) { 786 | if (success) { 787 | isFinished = true; 788 | startTickCenterHide(listener); 789 | } else { 790 | backToStart(listener, false); 791 | } 792 | } 793 | } 794 | 795 | /** 796 | * ================================= fullScreen start ======================================= 797 | */ 798 | 799 | /** 800 | * SmartLoadingView -- finishLoadingWithFullScreen:全屏api额外封装,为快速跳转(动画过度,只支持smart_full_screen模式) 801 | */ 802 | public void finishLoadingWithFullScreen(Activity activity, Class clazz) { 803 | if (mButtonType == SMART_FULL_SCREEN) { 804 | fullScreen(activity, clazz); 805 | } else { 806 | throw new IllegalArgumentException("此api只支持,hl_button_type = \"smart_full_screen\""); 807 | } 808 | } 809 | 810 | private void backToEnd(final LoadingListener listener, final boolean success) { 811 | if (isLoading) { 812 | if (!animatorSet.isRunning()) { 813 | backToEndReal(listener, success); 814 | } else { 815 | this.postDelayed(new Runnable() { 816 | @Override 817 | public void run() { 818 | backToEndReal(listener, success); 819 | } 820 | }, 1000); 821 | } 822 | } 823 | } 824 | 825 | private void backToEndReal(final LoadingListener listener, final boolean success) { 826 | if (animatorNetfail.isRunning()) { 827 | //防止重复播放动画 828 | return; 829 | } 830 | currentString = mAnimaledText; 831 | paint.setColor(animaled_backgroundColor); 832 | textColor = animaled_textColor; 833 | animatorNetfail.addListener(new Animator.AnimatorListener() { 834 | @Override 835 | public void onAnimationStart(Animator animator) { 836 | 837 | } 838 | 839 | @Override 840 | public void onAnimationEnd(Animator animator) { 841 | if (listener != null) { 842 | listener.loadingFinish(success); 843 | } 844 | } 845 | 846 | @Override 847 | public void onAnimationCancel(Animator animator) { 848 | 849 | } 850 | 851 | @Override 852 | public void onAnimationRepeat(Animator animator) { 853 | 854 | } 855 | }); 856 | animatorNetfail.start(); 857 | } 858 | 859 | //开启全屏动画,并监听动画进度 860 | private void fullScreen(final LoadingListener listener) { 861 | //必须,点击了最开始的动画处于,加载状态,才能获得回调 862 | if (isLoading) { 863 | if (!animatorSet.isRunning()) { 864 | toBigCircle(listener); 865 | } else { 866 | //当点击按钮的时候请求网络,假如动画执行时间大于网络请求时间, 867 | //那么咱们默认,执行完加载动画后,立即执行加载成功动画 868 | this.postDelayed(new Runnable() { 869 | @Override 870 | public void run() { 871 | toBigCircle(listener); 872 | } 873 | }, 1000); 874 | } 875 | } 876 | } 877 | 878 | //开启全屏动画,并简化跳转进度 879 | private void fullScreen(final Activity activity, final Class clazz) { 880 | //必须,点击了最开始的动画处于,加载状态,才能获得回调 881 | if (isLoading) { 882 | if (!animatorSet.isRunning()) { 883 | toBigCircle(activity, clazz); 884 | } else { 885 | //当点击按钮的时候请求网络,加入动画执行时间大于网络请求时间, 886 | //那么咱们默认,执行完加载动画后,立即执行加载成功动画 887 | this.postDelayed(new Runnable() { 888 | @Override 889 | public void run() { 890 | toBigCircle(activity, clazz); 891 | } 892 | }, 1000); 893 | } 894 | } 895 | } 896 | 897 | /** 898 | * ================================= fullScreen end ========================================= 899 | */ 900 | 901 | 902 | /** 903 | * ================================= smartButton start ======================================= 904 | */ 905 | private void backToStart(final LoadingListener listener, final boolean success) { 906 | if (isLoading) { 907 | if (!animatorSet.isRunning()) { 908 | backToStartReal(listener, success); 909 | } else { 910 | this.postDelayed(new Runnable() { 911 | @Override 912 | public void run() { 913 | backToStartReal(listener, success); 914 | } 915 | }, 1000); 916 | } 917 | } 918 | } 919 | 920 | private void backToStartReal(final LoadingListener listener, final boolean success) { 921 | if (animatorNetfail.isRunning()) { 922 | //防止重复播放动画 923 | return; 924 | } 925 | currentString = normalString; 926 | paint.setColor(backgroundColor); 927 | textColor = textColorOriginal; 928 | animatorNetfail.addListener(new Animator.AnimatorListener() { 929 | @Override 930 | public void onAnimationStart(Animator animator) { 931 | 932 | } 933 | 934 | @Override 935 | public void onAnimationEnd(Animator animator) { 936 | if (listener != null) { 937 | listener.loadingFinish(success); 938 | } 939 | } 940 | 941 | @Override 942 | public void onAnimationCancel(Animator animator) { 943 | 944 | } 945 | 946 | @Override 947 | public void onAnimationRepeat(Animator animator) { 948 | 949 | } 950 | }); 951 | animatorNetfail.start(); 952 | } 953 | 954 | private void changeButtonStatus(boolean success, int buttonType) { 955 | reset(); 956 | isFinished = success; 957 | if (success) { 958 | if (buttonType == SMART_BUTTON) { 959 | paint.setColor(animaled_backgroundColor); 960 | currentString = mAnimaledText; 961 | textColor = animaled_textColor; 962 | textPaint.setColor(textColor); 963 | postInvalidate(); 964 | } else { 965 | current_left = default_all_distance; 966 | int nowAlpha = textAlpha / 2 - (current_left * textAlpha / default_all_distance) < 0 ? 0 : textAlpha / 2 - (current_left * textAlpha / default_all_distance); 967 | textPaint.setColor(addAlpha(textColor, nowAlpha)); 968 | if (current_left == default_all_distance) { 969 | startDrawOk = true; 970 | } 971 | effect = new DashPathEffect(new float[]{pathMeasure.getLength(), pathMeasure.getLength()}, 0 * pathMeasure.getLength()); 972 | okPaint.setPathEffect(effect); 973 | postInvalidate(); 974 | //SMART_TICK | SMART_TICK_HIDE | SMART_TICK_CENTER_HIDE 975 | if (buttonType == SMART_TICK_HIDE || buttonType == SMART_TICK_CENTER_HIDE) { 976 | setVisibility(View.INVISIBLE); 977 | } 978 | } 979 | } 980 | 981 | } 982 | 983 | /** 984 | * ================================= smartButton end ========================================= 985 | */ 986 | 987 | 988 | /** 989 | * ================================= smartTick start ========================================= 990 | */ 991 | 992 | private void startTick(final LoadingListener listener) { 993 | if (isLoading) { 994 | if (!animatorSet.isRunning()) { 995 | startTickAnimal(listener, false); 996 | } else { 997 | this.postDelayed(new Runnable() { 998 | @Override 999 | public void run() { 1000 | startTickAnimal(listener, false); 1001 | } 1002 | }, 1000); 1003 | } 1004 | } 1005 | } 1006 | 1007 | private void startTickHide(final LoadingListener listener) { 1008 | if (isLoading) { 1009 | if (!animatorSet.isRunning()) { 1010 | startTickAnimal(listener, true); 1011 | } else { 1012 | this.postDelayed(new Runnable() { 1013 | @Override 1014 | public void run() { 1015 | startTickAnimal(listener, true); 1016 | } 1017 | }, 1000); 1018 | } 1019 | } 1020 | } 1021 | 1022 | private void startTickAnimal(final LoadingListener listener, final boolean hide) { 1023 | set_draw_ok_animation(); 1024 | animator_draw_ok.start(); 1025 | animator_draw_ok.addListener(new Animator.AnimatorListener() { 1026 | @Override 1027 | public void onAnimationStart(Animator animation) { 1028 | 1029 | } 1030 | 1031 | @Override 1032 | public void onAnimationEnd(Animator animation) { 1033 | if (listener != null) { 1034 | listener.loadingFinish(true); 1035 | } 1036 | isLoading = false; 1037 | isFinished = true; 1038 | setClickable(true); 1039 | if (hide) { 1040 | //是否隐藏 1041 | Animation animations = AnimationUtils.loadAnimation(getContext(), R.anim.alpha_hide); 1042 | setAnimation(animations); 1043 | animations.start(); 1044 | setVisibility(View.INVISIBLE); 1045 | } 1046 | } 1047 | 1048 | @Override 1049 | public void onAnimationCancel(Animator animation) { 1050 | 1051 | } 1052 | 1053 | @Override 1054 | public void onAnimationRepeat(Animator animation) { 1055 | 1056 | } 1057 | }); 1058 | } 1059 | 1060 | 1061 | /** 1062 | * ================================= smartTick end =========================================== 1063 | */ 1064 | 1065 | 1066 | /** 1067 | * ================================= smartTickCenterHide start =============================== 1068 | */ 1069 | 1070 | private void startTickCenterHide(final LoadingListener listener) { 1071 | if (isLoading) { 1072 | if (!animatorSet.isRunning()) { 1073 | startTickCenterHideAnimal(listener); 1074 | } else { 1075 | this.postDelayed(new Runnable() { 1076 | @Override 1077 | public void run() { 1078 | startTickCenterHideAnimal(listener); 1079 | } 1080 | }, 1000); 1081 | } 1082 | } 1083 | } 1084 | 1085 | private void startTickCenterHideAnimal(final LoadingListener listener) { 1086 | //如果是要移动到中间的模式的话 1087 | int[] location = new int[2]; 1088 | SmartLoadingView.this.getLocationOnScreen(location); 1089 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(height, height); 1090 | layoutParams.leftMargin = location[0] + (width / 2 - height / 2); 1091 | layoutParams.topMargin = location[1]; 1092 | 1093 | final OkView okView = new OkView(getContext()); 1094 | okView.setLayoutParams(layoutParams); 1095 | okView.setCircleColor(backgroundColor); 1096 | okView.setOkColor(textColor); 1097 | okView.setRadius(height / 2); 1098 | 1099 | final ViewGroup activityDecorView = (ViewGroup) ((Activity) getContext()).getWindow().getDecorView(); 1100 | activityDecorView.addView(okView); 1101 | okView.start(duration); 1102 | //初始真正的那个View 1103 | setVisibility(View.INVISIBLE); 1104 | //reset(); 1105 | 1106 | //当前屏幕中心位置 1107 | int window_center_x = UIUtil.getWidth(getContext()) / 2; 1108 | int window_center_y = UIUtil.getHeight(getContext()) / 2; 1109 | 1110 | //okView当前的中心点 1111 | int okView_center_x = location[0] + width / 2; 1112 | int okView_center_y = location[1] + height / 2; 1113 | 1114 | ObjectAnimator translationY = ObjectAnimator.ofFloat(okView, "translationY", 0f, window_center_y - okView_center_y).setDuration(duration); 1115 | ObjectAnimator translationX = ObjectAnimator.ofFloat(okView, "translationX", 0f, window_center_x - okView_center_x).setDuration(duration); 1116 | 1117 | ObjectAnimator toViewAnimatorX = ObjectAnimator.ofFloat(okView, "scaleX", 1f, 1.3f).setDuration(duration / 2); 1118 | toViewAnimatorX.setRepeatMode(ValueAnimator.REVERSE); 1119 | toViewAnimatorX.setRepeatCount(1); 1120 | toViewAnimatorX.setInterpolator(new AnticipateInterpolator()); 1121 | ObjectAnimator toViewAnimatorY = ObjectAnimator.ofFloat(okView, "scaleY", 1f, 1.3f).setDuration(duration / 2); 1122 | toViewAnimatorY.setRepeatMode(ValueAnimator.REVERSE); 1123 | toViewAnimatorY.setRepeatCount(1); 1124 | toViewAnimatorY.setInterpolator(new AnticipateInterpolator()); 1125 | AnimatorSet animatorScale = new AnimatorSet(); 1126 | animatorScale.playTogether(toViewAnimatorX, toViewAnimatorY); 1127 | 1128 | ObjectAnimator toViewAnimatorAlpha = ObjectAnimator.ofFloat(okView, "alpha", 1f, 0f).setDuration(duration); 1129 | //这里就用代码实现把 1130 | AnimatorSet animatorSet = new AnimatorSet(); 1131 | animatorSet.play(translationY).with(translationX).before(animatorScale).before(toViewAnimatorAlpha); 1132 | animatorSet.start(); 1133 | animatorSet.addListener(new Animator.AnimatorListener() { 1134 | @Override 1135 | public void onAnimationStart(Animator animation) { 1136 | 1137 | } 1138 | 1139 | @Override 1140 | public void onAnimationEnd(Animator animation) { 1141 | if (listener != null) { 1142 | listener.loadingFinish(true); 1143 | } 1144 | isLoading = false; 1145 | setClickable(true); 1146 | activityDecorView.removeView(okView); 1147 | } 1148 | 1149 | @Override 1150 | public void onAnimationCancel(Animator animation) { 1151 | 1152 | } 1153 | 1154 | @Override 1155 | public void onAnimationRepeat(Animator animation) { 1156 | 1157 | } 1158 | 1159 | }); 1160 | } 1161 | 1162 | 1163 | /** 1164 | * ================================= smartTickCenterHide end ================================= 1165 | */ 1166 | 1167 | 1168 | public interface LoadingListener { 1169 | void loadingFinish(boolean success); 1170 | 1171 | } 1172 | 1173 | /** 1174 | * --------------------------------------------------------------------------------------------- 1175 | */ 1176 | 1177 | //立即重置状态 1178 | private void reset() { 1179 | isFinished = false; 1180 | //画笔颜色重置 1181 | textColor = textColorOriginal; 1182 | textPaint.setColor(textColor); 1183 | setClickable(true); 1184 | currentString = normalString; 1185 | textPaint.setColor(textColor); 1186 | circleAngle = corners_radius; 1187 | paint.setColor(backgroundColor); 1188 | current_left = 0; 1189 | isDrawLoading = false; 1190 | startDrawOk = false; 1191 | isLoading = false; 1192 | invalidate(); 1193 | 1194 | animator_draw_ok.cancel(); 1195 | animatorSet.cancel(); 1196 | animatorNetfail.cancel(); 1197 | if (circlBigView != null) { 1198 | circlBigView.setCircleR(0); 1199 | } 1200 | setVisibility(View.VISIBLE); 1201 | } 1202 | 1203 | private void toBigCircle(LoadingListener listener) { 1204 | circlBigView.setRadius(this.getMeasuredHeight() / 2); 1205 | circlBigView.setColorBg(backgroundColor); 1206 | int[] location = new int[2]; 1207 | this.getLocationOnScreen(location); 1208 | circlBigView.setXY(location[0] + this.getMeasuredWidth() / 2, location[1]); 1209 | ViewGroup activityDecorView = (ViewGroup) ((Activity) getContext()).getWindow().getDecorView(); 1210 | ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 1211 | activityDecorView.removeView(circlBigView); 1212 | activityDecorView.addView(circlBigView, layoutParams); 1213 | circlBigView.startShowAni(listener, this); 1214 | } 1215 | 1216 | 1217 | private void toBigCircle(Activity activity, Class clazz) { 1218 | circlBigView.setRadius(this.getMeasuredHeight() / 2); 1219 | circlBigView.setColorBg(backgroundColor); 1220 | int[] location = new int[2]; 1221 | this.getLocationOnScreen(location); 1222 | circlBigView.setXY(location[0] + this.getMeasuredWidth() / 2, location[1]); 1223 | ViewGroup activityDecorView = (ViewGroup) ((Activity) getContext()).getWindow().getDecorView(); 1224 | ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 1225 | activityDecorView.removeView(circlBigView); 1226 | activityDecorView.addView(circlBigView, layoutParams); 1227 | circlBigView.startShowAni(activity, clazz); 1228 | } 1229 | 1230 | } 1231 | -------------------------------------------------------------------------------- /smartloadview/src/main/java/com/lihang/help/CirclBigView.java: -------------------------------------------------------------------------------- 1 | package com.lihang.help; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.app.Activity; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.graphics.Canvas; 9 | import android.graphics.Paint; 10 | import android.support.annotation.Nullable; 11 | import android.util.AttributeSet; 12 | import android.view.View; 13 | 14 | import com.lihang.SmartLoadingView; 15 | import com.lihang.smartloadview.R; 16 | 17 | 18 | /** 19 | * 圆圈扩散自定义View 20 | * By leo 21 | * 2019.5.23 22 | */ 23 | 24 | public class CirclBigView extends View { 25 | 26 | //圆圈扩散动画 27 | private ValueAnimator animator_big; 28 | private int myRadius; 29 | //父类控件半径(父类最短一边,长度的一半) 30 | private int fatherRadius; 31 | private Paint showPaint; 32 | private int y;//当前Y轴位置 33 | private int x;//当前X轴位置 34 | 35 | //最大能扩散到的半径 36 | private int maxRadius; 37 | 38 | 39 | public CirclBigView(Context context) { 40 | this(context, null); 41 | } 42 | 43 | public CirclBigView(Context context, @Nullable AttributeSet attrs) { 44 | this(context, attrs, 0); 45 | } 46 | 47 | public CirclBigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 48 | super(context, attrs, defStyleAttr); 49 | showPaint = new Paint(); 50 | showPaint.setAntiAlias(true); 51 | showPaint.setStyle(Paint.Style.FILL); 52 | showPaint.setColor(getResources().getColor(R.color.guide_anim)); 53 | } 54 | 55 | 56 | public void setXY(int x, int y) { 57 | this.x = x; 58 | this.y = y; 59 | int y_true = y + fatherRadius; 60 | //知道了view圆心,计算出到手机4个角的距离,以最大距离为准 61 | int left_top = (int) Math.sqrt((x * x) + (y_true * y_true)); 62 | int left_bottom = (int) Math.sqrt((x * x) + ((UIUtil.getHeight(getContext()) - y_true) * (UIUtil.getHeight(getContext()) - y_true))); 63 | 64 | int right_top = (int) Math.sqrt(((UIUtil.getWidth(getContext()) - x) * (UIUtil.getWidth(getContext()) - x)) + (y_true * y_true)); 65 | int right_bottom = (int) Math.sqrt(((UIUtil.getWidth(getContext()) - x) * (UIUtil.getWidth(getContext()) - x)) + ((UIUtil.getHeight(getContext()) - y_true) * (UIUtil.getHeight(getContext()) - y_true))); 66 | 67 | int left_big = left_top >= left_bottom ? left_top : left_bottom; 68 | int right_big = right_top >= right_bottom ? right_top : right_bottom; 69 | maxRadius = left_big >= right_big ? left_big : right_big; 70 | //这里虚拟键有个bug,我们把半径大小稍加长 71 | maxRadius = maxRadius + UIUtil.getWidth(getContext()) / 6; 72 | 73 | 74 | animator_big = ValueAnimator.ofInt(myRadius, maxRadius); 75 | animator_big.setDuration(200); 76 | animator_big.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 77 | @Override 78 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 79 | myRadius = (int) valueAnimator.getAnimatedValue(); 80 | postInvalidate(); 81 | } 82 | }); 83 | 84 | invalidate(); 85 | } 86 | 87 | public void setRadius(int radius) { 88 | myRadius = radius; 89 | fatherRadius = radius; 90 | } 91 | 92 | public void setCircleR(int currentR) { 93 | myRadius = currentR; 94 | postInvalidate(); 95 | } 96 | 97 | 98 | public void setColorBg(int colorBg) { 99 | showPaint.setColor(colorBg); 100 | } 101 | 102 | 103 | @Override 104 | protected void onDraw(Canvas canvas) { 105 | super.onDraw(canvas); 106 | canvas.drawCircle(x, y + fatherRadius, myRadius, showPaint); 107 | } 108 | 109 | 110 | public void startShowAni(final SmartLoadingView.LoadingListener listener, final SmartLoadingView smartLoadingView) { 111 | 112 | if (listener != null) { 113 | if (!animator_big.isRunning()) { 114 | animator_big.start(); 115 | } 116 | if (listener != null) { 117 | animator_big.addListener(new Animator.AnimatorListener() { 118 | @Override 119 | public void onAnimationStart(Animator animation) { 120 | 121 | } 122 | 123 | @Override 124 | public void onAnimationEnd(Animator animation) { 125 | listener.loadingFinish(true); 126 | } 127 | 128 | @Override 129 | public void onAnimationCancel(Animator animation) { 130 | 131 | } 132 | 133 | @Override 134 | public void onAnimationRepeat(Animator animation) { 135 | 136 | } 137 | }); 138 | } 139 | 140 | } 141 | } 142 | 143 | 144 | public void startShowAni(final Activity activity, final Class clazz) { 145 | 146 | if (!animator_big.isRunning()) { 147 | animator_big.start(); 148 | animator_big.addListener(new Animator.AnimatorListener() { 149 | @Override 150 | public void onAnimationStart(Animator animation) { 151 | 152 | } 153 | 154 | @Override 155 | public void onAnimationEnd(Animator animation) { 156 | activity.startActivity(new Intent(activity, clazz)); 157 | activity.finish(); 158 | activity.overridePendingTransition(R.anim.scale_test_home, R.anim.scale_test2); 159 | } 160 | 161 | @Override 162 | public void onAnimationCancel(Animator animation) { 163 | 164 | } 165 | 166 | @Override 167 | public void onAnimationRepeat(Animator animation) { 168 | 169 | } 170 | }); 171 | } 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /smartloadview/src/main/java/com/lihang/help/OkView.java: -------------------------------------------------------------------------------- 1 | package com.lihang.help; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.DashPathEffect; 7 | import android.graphics.Paint; 8 | import android.graphics.Path; 9 | import android.graphics.PathEffect; 10 | import android.graphics.PathMeasure; 11 | import android.support.annotation.Nullable; 12 | import android.util.AttributeSet; 13 | import android.view.View; 14 | 15 | /** 16 | * Created by leo 17 | * on 2019/11/21. 18 | */ 19 | public class OkView extends View { 20 | //绘制一个小圆圈 21 | private Paint paint; 22 | //绘制打勾paint 23 | private Paint okPaint; 24 | 25 | //背景圆圈的半径 26 | private int myRadius; 27 | //绘制打勾的路径 28 | private Path path = new Path(); 29 | //绘制路径的长度,也可以理解为完成度 30 | private PathMeasure pathMeasure; 31 | 32 | //绘制对勾(√)的动画 33 | private ValueAnimator animator_draw_ok; 34 | 35 | //对路径处理实现绘制动画效果 36 | private PathEffect effect; 37 | 38 | private boolean startDrawOk; 39 | 40 | public OkView(Context context) { 41 | this(context, null); 42 | } 43 | 44 | public OkView(Context context, @Nullable AttributeSet attrs) { 45 | this(context, attrs, 0); 46 | } 47 | 48 | public OkView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 49 | super(context, attrs, defStyleAttr); 50 | paint = new Paint(); 51 | paint.setAntiAlias(true); 52 | paint.setStyle(Paint.Style.FILL); 53 | 54 | 55 | okPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 56 | okPaint.setStrokeWidth(5); 57 | okPaint.setStyle(Paint.Style.STROKE); 58 | okPaint.setStrokeCap(Paint.Cap.ROUND); 59 | 60 | 61 | } 62 | 63 | 64 | @Override 65 | protected void onDraw(Canvas canvas) { 66 | super.onDraw(canvas); 67 | canvas.drawCircle(getWidth() / 2, getHeight() / 2, myRadius, paint); 68 | 69 | if (startDrawOk) { 70 | canvas.drawPath(path, okPaint); 71 | } 72 | } 73 | 74 | public void setOkColor(int color) { 75 | okPaint.setColor(color); 76 | } 77 | 78 | public void setCircleColor(int color) { 79 | paint.setColor(color); 80 | } 81 | 82 | 83 | public void setRadius(int radius) { 84 | myRadius = radius; 85 | //对勾的路径 86 | int cHeight = radius * 2; 87 | path.moveTo(+cHeight / 8 * 3, cHeight / 2); 88 | path.lineTo(+cHeight / 2, cHeight / 5 * 3); 89 | path.lineTo(+cHeight / 3 * 2, cHeight / 5 * 2); 90 | pathMeasure = new PathMeasure(path, true); 91 | 92 | invalidate(); 93 | } 94 | 95 | 96 | public void start(int duration) { 97 | animator_draw_ok = ValueAnimator.ofFloat(1, 0); 98 | animator_draw_ok.setDuration(duration); 99 | animator_draw_ok.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 100 | @Override 101 | public void onAnimationUpdate(ValueAnimator animation) { 102 | startDrawOk = true; 103 | float value = (Float) animation.getAnimatedValue(); 104 | effect = new DashPathEffect(new float[]{pathMeasure.getLength(), pathMeasure.getLength()}, value * pathMeasure.getLength()); 105 | okPaint.setPathEffect(effect); 106 | invalidate(); 107 | } 108 | }); 109 | animator_draw_ok.start(); 110 | } 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /smartloadview/src/main/java/com/lihang/help/UIUtil.java: -------------------------------------------------------------------------------- 1 | package com.lihang.help; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | import android.util.TypedValue; 8 | import android.view.WindowManager; 9 | 10 | import java.io.UnsupportedEncodingException; 11 | import java.lang.reflect.Field; 12 | import java.security.MessageDigest; 13 | import java.security.NoSuchAlgorithmException; 14 | import java.util.Locale; 15 | import java.util.UUID; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | 20 | /** 21 | * Created by lihang Leo on 2016/12/10. 22 | */ 23 | public class UIUtil { 24 | 25 | private static final String TAG = UIUtil.class.getName(); 26 | 27 | /** 28 | * Dip to Px 29 | * 30 | * @param context 31 | * @param dipValue 32 | * @return 33 | */ 34 | public static int dip2px(Context context, float dipValue) { 35 | float scale = context.getResources().getDisplayMetrics().density; 36 | return (int) (dipValue * scale + 0.5f); 37 | } 38 | 39 | /** 40 | * Px To Dip 41 | * 42 | * @param context 43 | * @param pxValue 44 | * @return 45 | */ 46 | public static int px2dip(Context context, float pxValue) { 47 | float scale = context.getResources().getDisplayMetrics().density; 48 | return (int) (pxValue / scale + 0.5f); 49 | } 50 | 51 | //sp转px 52 | public static int Sp2Px(Context context, int sp) { 53 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics()); 54 | } 55 | 56 | 57 | public static int getWidth(Context context) { 58 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 59 | int width = wm.getDefaultDisplay().getWidth(); 60 | return width; 61 | } 62 | 63 | public static int getHeight(Context context) { 64 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 65 | int height = wm.getDefaultDisplay().getHeight(); 66 | return height; 67 | } 68 | 69 | //获取手机状态栏高度 70 | public static int getStatusBarHeight(Context context) { 71 | Class c = null; 72 | Object obj = null; 73 | Field field = null; 74 | int x = 0, statusBarHeight = 0; 75 | try { 76 | c = Class.forName("com.android.internal.R$dimen"); 77 | obj = c.newInstance(); 78 | field = c.getField("status_bar_height"); 79 | x = Integer.parseInt(field.get(obj).toString()); 80 | statusBarHeight = context.getResources().getDimensionPixelSize(x); 81 | } catch (Exception e1) { 82 | e1.printStackTrace(); 83 | } 84 | return statusBarHeight; 85 | } 86 | 87 | 88 | 89 | //获得独一无二的Psuedo ID 90 | public static String getDeviceId() { 91 | String serial = null; 92 | String m_szDevIDShort = "35" + 93 | Build.BOARD.length() % 10 + Build.BRAND.length() % 10 + 94 | 95 | Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 + 96 | 97 | Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 + 98 | 99 | Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 + 100 | 101 | Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 + 102 | 103 | Build.TAGS.length() % 10 + Build.TYPE.length() % 10 + 104 | 105 | Build.USER.length() % 10; //13 位 106 | 107 | try { 108 | serial = Build.class.getField("SERIAL").get(null).toString(); 109 | //API>=9 使用serial号 110 | return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); 111 | } catch (Exception exception) { 112 | //serial需要一个初始化 113 | serial = "serial"; // 随便一个初始化 114 | } 115 | //使用硬件信息拼凑出来的15位号码 116 | return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); 117 | } 118 | 119 | 120 | /** 121 | * 获取当前手机系统语言。 122 | * 123 | * @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN” 124 | */ 125 | public static String getSystemLanguage() { 126 | return Locale.getDefault().getLanguage(); 127 | } 128 | 129 | /** 130 | * 获取当前系统上的语言列表(Locale列表) 131 | * 132 | * @return 语言列表 133 | */ 134 | public static Locale[] getSystemLanguageList() { 135 | return Locale.getAvailableLocales(); 136 | } 137 | 138 | /** 139 | * 获取当前手机系统版本号 140 | * 141 | * @return 系统版本号 142 | */ 143 | public static String getSystemVersion() { 144 | return Build.VERSION.RELEASE; 145 | } 146 | 147 | /** 148 | * 获取手机型号 149 | * 150 | * @return 手机型号 151 | */ 152 | public static String getSystemModel() { 153 | return Build.MODEL; 154 | } 155 | 156 | /** 157 | * 获取手机厂商 158 | * 159 | * @return 手机厂商 160 | */ 161 | public static String getDeviceBrand() { 162 | return Build.BRAND; 163 | } 164 | 165 | 166 | /** 167 | * get App versionCode 168 | * 169 | * @param context 170 | * @return 171 | */ 172 | public static int getVersionCode(Context context) { 173 | PackageManager packageManager = context.getPackageManager(); 174 | PackageInfo packageInfo; 175 | int versionCode = 1; 176 | try { 177 | packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); 178 | versionCode = packageInfo.versionCode ; 179 | } catch (PackageManager.NameNotFoundException e) { 180 | e.printStackTrace(); 181 | } 182 | return versionCode; 183 | } 184 | 185 | /** 186 | * get App versionName 187 | * 188 | * @param context 189 | * @return 190 | */ 191 | public static String getVersionName(Context context) { 192 | PackageManager packageManager = context.getPackageManager(); 193 | PackageInfo packageInfo; 194 | String versionName = ""; 195 | try { 196 | packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); 197 | versionName = packageInfo.versionName; 198 | } catch (PackageManager.NameNotFoundException e) { 199 | e.printStackTrace(); 200 | } 201 | return versionName; 202 | } 203 | 204 | 205 | public static String md5(String content) {//MD5加密 206 | byte[] hash; 207 | try { 208 | hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8")); 209 | } catch (NoSuchAlgorithmException e) { 210 | throw new RuntimeException("NoSuchAlgorithmException",e); 211 | } catch (UnsupportedEncodingException e) { 212 | throw new RuntimeException("UnsupportedEncodingException", e); 213 | } 214 | 215 | StringBuilder hex = new StringBuilder(hash.length * 2); 216 | for (byte b : hash) { 217 | if ((b & 0xFF) < 0x10){ 218 | hex.append("0"); 219 | } 220 | hex.append(Integer.toHexString(b & 0xFF)); 221 | } 222 | return hex.toString(); 223 | } 224 | 225 | 226 | 227 | /** 228 | * 判断手机格式是否正确 229 | * 230 | * @param mobiles 231 | * @return true为格式正确 232 | */ 233 | public static boolean isMobileNO(String mobiles) { 234 | Pattern p = Pattern 235 | .compile("^((13[0-9])|(14[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$"); 236 | Matcher m = p.matcher(mobiles); 237 | return m.matches(); 238 | } 239 | 240 | 241 | 242 | 243 | } 244 | -------------------------------------------------------------------------------- /smartloadview/src/main/res/anim/alpha_hide.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /smartloadview/src/main/res/anim/scale_test2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /smartloadview/src/main/res/anim/scale_test_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /smartloadview/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /smartloadview/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | #bbbbbb 7 | 8 | #3e80fc 9 | #f54949 10 | #ffffff 11 | 12 | 13 | -------------------------------------------------------------------------------- /smartloadview/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0dp 4 | 14dp 5 | 7dp 6 | -------------------------------------------------------------------------------- /smartloadview/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | smartloadview 3 | 4 | --------------------------------------------------------------------------------