Showing preview only (317K chars total). Download the full file or copy to clipboard to get everything.
Repository: DylanCaiCoding/LoadingStateView
Branch: master
Commit: a91fa3f3142b
Files: 152
Total size: 264.8 KB
Directory structure:
gitextract_ifim0oyk/
├── .gitignore
├── LICENSE
├── README.md
├── README_ZH.md
├── build.gradle
├── docs/
│ ├── .nojekyll
│ ├── README.md
│ ├── _sidebar.md
│ ├── index.html
│ └── zh/
│ ├── basic-usage.md
│ ├── delegate.md
│ ├── migration-guide.md
│ ├── q&a.md
│ └── viewbinding.md
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── loadingstateview/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── dylanc/
│ │ └── loadingstateview/
│ │ └── LoadingStateView.kt
│ └── res/
│ └── values/
│ └── strings.xml
├── loadingstateview-ktx/
│ ├── .gitignore
│ ├── build.gradle
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── dylanc/
│ └── loadingstateview/
│ ├── BaseToolbarViewDelegate.kt
│ ├── Decorative.kt
│ ├── LoadingState.kt
│ ├── LoadingStateDelegate.kt
│ └── ToolbarConfig.kt
├── sample-java/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ ├── release/
│ │ ├── app-release.apk
│ │ └── output.json
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── dylanc/
│ │ └── loadingstateview/
│ │ └── sample/
│ │ └── java/
│ │ └── ExampleInstrumentedTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── dylanc/
│ │ │ └── loadingstateview/
│ │ │ └── sample/
│ │ │ └── java/
│ │ │ ├── App.java
│ │ │ ├── animation/
│ │ │ │ └── FadeAnimatable.java
│ │ │ ├── base/
│ │ │ │ └── BaseActivity.java
│ │ │ ├── delegate/
│ │ │ │ ├── BottomEditorDecorViewDelegate.java
│ │ │ │ ├── CoolLoadingViewDelegate.java
│ │ │ │ ├── CustomHeaderViewDelegate.java
│ │ │ │ ├── EmptyViewDelegate.java
│ │ │ │ ├── ErrorViewDelegate.java
│ │ │ │ ├── LoadingViewDelegate.java
│ │ │ │ ├── NavIconType.java
│ │ │ │ ├── NothingViewDelegate.java
│ │ │ │ ├── PlaceholderViewDelegate.java
│ │ │ │ ├── ScrollingDecorViewDelegate.java
│ │ │ │ ├── SearchHeaderViewDelegate.java
│ │ │ │ ├── TimeoutViewDelegate.java
│ │ │ │ └── ToolbarViewDelegate.java
│ │ │ ├── ui/
│ │ │ │ ├── ActErrorActivity.java
│ │ │ │ ├── BottomEditorActivity.java
│ │ │ │ ├── CustomHeaderActivity.java
│ │ │ │ ├── FragmentEmptyActivity.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── MultipleHeaderActivity.java
│ │ │ │ ├── RecyclerViewActivity.java
│ │ │ │ ├── ScrollingToolbarActivity.java
│ │ │ │ ├── ViewPagerActivity.java
│ │ │ │ ├── ViewPlaceholderActivity.java
│ │ │ │ └── fragment/
│ │ │ │ ├── EmptyFragment.java
│ │ │ │ ├── SimpleFragment.java
│ │ │ │ └── TimeoutFragment.java
│ │ │ ├── utils/
│ │ │ │ ├── DensityUtils.java
│ │ │ │ ├── HttpUtils.java
│ │ │ │ ├── KeyboardUtils.java
│ │ │ │ └── ToolbarUtils.java
│ │ │ └── widget/
│ │ │ ├── LoadingDrawable.java
│ │ │ ├── LoadingRenderer.java
│ │ │ ├── LoadingRendererFactory.java
│ │ │ ├── LoadingView.java
│ │ │ └── renderer/
│ │ │ └── ElectricFanLoadingRenderer.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ ├── bg_reload_btn.xml
│ │ │ ├── bg_search.xml
│ │ │ ├── ic_baseline_favorite_24.xml
│ │ │ ├── ic_baseline_message_24.xml
│ │ │ ├── ic_baseline_photo_camera_24.xml
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_fragment.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_recycler_view.xml
│ │ │ ├── activity_scrolling.xml
│ │ │ ├── activity_tab_layout.xml
│ │ │ ├── activity_view.xml
│ │ │ ├── activity_view_pager.xml
│ │ │ ├── layout_bottom_editor.xml
│ │ │ ├── layout_content.xml
│ │ │ ├── layout_cool_loading.xml
│ │ │ ├── layout_custom_header.xml
│ │ │ ├── layout_empty.xml
│ │ │ ├── layout_error.xml
│ │ │ ├── layout_loading.xml
│ │ │ ├── layout_placeholder.xml
│ │ │ ├── layout_scrolling_toolbar.xml
│ │ │ ├── layout_search_header.xml
│ │ │ ├── layout_timeout.xml
│ │ │ ├── layout_toolbar.xml
│ │ │ └── recycler_item_image.xml
│ │ ├── menu/
│ │ │ └── menu_about.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── dylanc/
│ └── loadingstateview/
│ └── sample/
│ └── java/
│ └── ExampleUnitTest.kt
├── sample-kotlin/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── dylanc/
│ │ └── loadingstateview/
│ │ └── sample/
│ │ └── kotlin/
│ │ └── ExampleInstrumentedTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── dylanc/
│ │ │ └── loadingstateview/
│ │ │ └── sample/
│ │ │ └── kotlin/
│ │ │ ├── App.kt
│ │ │ ├── base/
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── BaseBindingActivity.kt
│ │ │ │ ├── BaseBindingFragment.kt
│ │ │ │ └── BaseFragment.kt
│ │ │ ├── delegate/
│ │ │ │ ├── EmptyViewDelegate.kt
│ │ │ │ ├── ErrorViewDelegate.kt
│ │ │ │ ├── FadeAnimatable.kt
│ │ │ │ ├── LoadingViewDelegate.kt
│ │ │ │ ├── ScrollingDecorViewDelegate.kt
│ │ │ │ └── ToolbarViewDelegate.kt
│ │ │ └── ui/
│ │ │ └── MainActivity.kt
│ │ └── res/
│ │ ├── drawable/
│ │ │ ├── bg_reload_btn.xml
│ │ │ ├── ic_add.xml
│ │ │ ├── ic_arrow_back_ios.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ └── ic_refresh.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── layout_empty.xml
│ │ │ ├── layout_error.xml
│ │ │ ├── layout_loading.xml
│ │ │ ├── layout_scrolling_toolbar.xml
│ │ │ └── layout_toolbar.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ └── values-night/
│ │ └── themes.xml
│ └── test/
│ └── java/
│ └── com/
│ └── dylanc/
│ └── loadingstateview/
│ └── sample/
│ └── kotlin/
│ └── ExampleUnitTest.kt
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
.idea
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# LoadingStateView
English | [中文](README_ZH.md)
[](https://www.jitpack.io/#DylanCaiCoding/LoadingLoadingStateView) [](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/LICENSE)
`LoadingStateView` is a highly expandable Android library for showing loading status view on the low-coupling way, the core function is implemented with a [Kotlin file](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/loadingstateview/src/main/java/com/dylanc/loadingstateview/LoadingStateView.kt) of over 200 lines. it not only shows different view like loading, content, error, empty and customized view when loading network data, but also manages title bar.
**Major update: With the Kotlin feature, you can quickly add all functionality to the base class without affecting existing code. The overall usage is further simplified with removing the `ViewHolder`. It is recommended to upgrade!**
## Feature
- No need to add view code to the layout.
- Quickly add all functionality to the base class without affecting existing code. (Kotlin)
- Support for use for Activity, Fragment, RecyclerView, View.
- Support for show custom views.
- Support for managing the title bar and add multiple headers.
- Support for set reload event.
- Support for update views anytime.
- Support for use with most third-party libraries.
## Demo
Click or scan QR code to download
[](https://www.pgyer.com/loadinghelper)
| [Activity(error)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ActErrorActivity.java) | [View(placeholder)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPlaceholderActivity.java) | [ViewPager(timeout)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPagerActivity.java) | [RecyclerView(cool loading)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/RecyclerViewActivity.java) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|  |  |  |  |
| [SpecialHeader(custom)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/CustomHeaderActivity.java) | [MultipleHeader(search)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MultipleHeaderActivity.java) | [SpecialDecorView(scrolling)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ScrollingToolbarActivity.java) | [BottomDecorView(editor)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/BottomEditorActivity.java) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|  |  |  |  |
## Usage
:pencil: **[>> Usage documentation <<](https://dylancaicoding.github.io/LoadingStateView)**
## Gradle
Add it in your root `build.gradle` at the end of repositories:
```groovy
allprojects {
repositories {
// ...
maven { url 'https://www.jitpack.io' }
}
}
```
Add dependencies in your module `build.gradle` :
```groovy
dependencies {
// java
implementation 'com.github.DylanCaiCoding.LoadingStateView:loadingstateview:5.0.0'
// kotlin
implementation 'com.github.DylanCaiCoding.LoadingStateView:loadingstateview-ktx:5.0.0'
}
```
## Change log
[Releases](https://github.com/DylanCaiCoding/LoadingStateView/releases)
## Author's other libraries
| Library | Description |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| [Longan](https://github.com/DylanCaiCoding/Longan) | Probably the best Kotlin utils library for Android. |
| [ViewBindingKTX](https://github.com/DylanCaiCoding/ViewBindingKTX) | The most comprehensive utils of ViewBinding. |
| [MMKV-KTX](https://github.com/DylanCaiCoding/MMKV-KTX) | Use MMKV with property delegates. |
| [MultiBaseUrls](https://github.com/DylanCaiCoding/MultiBaseUrls) | Use annotation to allow Retrofit to support multiple baseUrl and dynamically change baseUrl |
| [Tracker](https://github.com/DylanCaiCoding/Tracker) | A lightweight tracking framework based on the tracking idea of Buzzvideo.|
## Thanks
- [luckbilly/Gloading](https://github.com/luckybilly/Gloading) Optimize my library standing on the shoulders of giants.
- [drakeet/MultiType](https://github.com/drakeet/MultiType) Referenced the usage of multiple adapters.
- [dinuscxj/LoadingDrawable](https://github.com/dinuscxj/LoadingDrawable) The cool loading effect in the demo.
## License
```
Copyright (C) 2019. Dylan Cai
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
================================================
FILE: README_ZH.md
================================================
# LoadingStateView
[English](README.md) | 中文
[](https://www.jitpack.io/#DylanCaiCoding/LoadingLoadingStateView) [](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/LICENSE)
`LoadingStateView` 是一个深度解耦缺省页和标题栏的工具,核心功能的实现代码只有一个 200 多行的 [Kotlin 文件](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/loadingstateview/src/main/java/com/dylanc/loadingstateview/LoadingStateView.kt)。不仅能在请求网络数据时显示加载中、加载成功、加载失败、无数据的视图或自定义视图,还可以对标题栏进行解耦。
**重大更新:结合 Kotlin 语法特性能快速将所有功能集成到基类,不会影响已有代码。移除了 `ViewHolder`,整体用法得到进一步简化,建议升级!**
- 无需在布局添加视图代码
- 支持快速集成到基类,并且不影响有代码(仅 Kotlin)
- 支持 Activity、Fragment、列表或指定的 View
- 支持显示自定义视图
- 支持添加多个头部控件
- 支持设置重新请求数据的事件
- 支持动态更新视图样式
- 可结合绝大部分第三方控件使用
## 示例
点击或者扫描二维码下载
[](https://www.pgyer.com/loadinghelper)
动态添加加载状态的布局:
| [Activity(error)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ActErrorActivity.java) | [View(placeholder)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPlaceholderActivity.java) | [ViewPager(timeout)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPagerActivity.java) | [RecyclerView(cool loading)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/RecyclerViewActivity.java) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|  |  |  |  |
动态添加标题栏或装饰容器:
| [SpecialHeader(custom)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/CustomHeaderActivity.java) | [MultipleHeader(search)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MultipleHeaderActivity.java) | [SpecialDecorView(scrolling)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ScrollingToolbarActivity.java) | [BottomDecorView(editor)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/BottomEditorActivity.java) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|  |  |  |  |
## 用法
:pencil: **[>> 使用文档 <<](https://dylancaicoding.github.io/LoadingStateView)**
## Gradle
在根目录的 `build.gradle` 添加:
```groovy
allprojects {
repositories {
// ...
maven { url 'https://www.jitpack.io' }
}
}
```
在模块的 `build.gradle` 添加依赖:
```groovy
dependencies {
// java
implementation 'com.github.DylanCaiCoding.LoadingStateView:loadingstateview:5.0.0'
// kotlin
implementation 'com.github.DylanCaiCoding.LoadingStateView:loadingstateview-ktx:5.0.0'
}
```
## 更新日志
[Releases](https://github.com/DylanCaiCoding/LoadingStateView/releases)
## 作者其它的库
| 库 | 简介 |
| ------------------------------------------------------------ | ---------------------------------------------- |
| [Longan](https://github.com/DylanCaiCoding/Longan) | 可能是最好用的 Kotlin 工具库 |
| [ViewBindingKTX](https://github.com/DylanCaiCoding/ViewBindingKTX) | 最全面的 ViewBinding 工具 |
| [MMKV-KTX](https://github.com/DylanCaiCoding/MMKV-KTX) | 最灵活易用的 MMKV 工具 |
| [MultiBaseUrls](https://github.com/DylanCaiCoding/MultiBaseUrls) | 用注解让 Retrofit 同时支持多个 baseUrl 以及动态改变 baseUrl |
| [Tracker](https://github.com/DylanCaiCoding/Tracker) | 基于西瓜视频的责任链埋点思路实现的轻量级埋点框架 |
## 感谢
- [luckbilly/Gloading](https://github.com/luckybilly/Gloading) 站在了巨人肩膀上优化了本库,非常感谢!
- [drakeet/MultiType](https://github.com/drakeet/MultiType) 参考了注册配置多适配器的思想和用法
- [dinuscxj/LoadingDrawable](https://github.com/dinuscxj/LoadingDrawable) 示例中的自定义加载动画
## License
```
Copyright (C) 2019. Dylan Cai
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
================================================
FILE: build.gradle
================================================
buildscript {
ext.buildConfig = [
'versionCode' : 1,
'versionName' : "1.0.0",
'compileSdkVersion': 30,
'minSdkVersion' : 14,
'targetSdkVersion' : 30
]
ext {
appCompatVersion = '1.3.1'
constraintLayoutVersion = '2.1.1'
coreVersion = '1.7.0-alpha01'
espressoVersion = '3.4.0'
glideVersion = '4.12.0'
kotlinVersion = "1.5.31"
lifecycleVersion = '2.4.0-alpha03'
junitExtVersion = '1.1.3'
junitVersion = '4.13.2'
materialVersion = '1.4.0'
viewBindingKTXVersion = '2.1.0'
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'https://www.jitpack.io' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: docs/.nojekyll
================================================
================================================
FILE: docs/README.md
================================================
# LoadingStateView
[](https://www.jitpack.io/#DylanCaiCoding/LoadingLoadingStateView)
[](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/LICENSE)
[](https://github.com/DylanCaiCoding/LoadingStateView)
`LoadingStateView` 是一个深度解耦缺省页和标题栏的工具,核心功能的实现代码只有一个 200 行左右(不算注释)的 [Kotlin 文件](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/loadingstateview/src/main/java/com/dylanc/loadingstateview/LoadingStateView.kt)。不仅能在请求网络数据时显示加载中、加载成功、加载失败、无数据的视图或自定义视图,还可以对标题栏进行解耦。
**重大更新:结合 Kotlin 语法特性能快速将所有功能集成到基类,不会影响已有代码。移除了 `ViewHolder`,整体用法得到进一步简化,建议升级!**
- 无需在布局添加视图代码
- 支持快速集成到基类,并且不影响有代码(仅 Kotlin)
- 支持 Activity、Fragment、列表或指定的 View
- 支持显示自定义视图
- 支持添加多个头部控件
- 支持设置重新请求数据的事件
- 支持动态更新视图样式
- 可结合绝大部分第三方控件使用
## 示例
点击或者扫描二维码下载
[](https://www.pgyer.com/loadinghelper)
动态添加加载状态的布局:
| [Activity(error)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ActErrorActivity.java) | [View(placeholder)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPlaceholderActivity.java) | [ViewPager(timeout)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPagerActivity.java) | [RecyclerView(cool loading)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/RecyclerViewActivity.java) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|  |  |  |  |
动态添加标题栏或装饰容器:
| [SpecialHeader(custom)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/CustomHeaderActivity.java) | [MultipleHeader(search)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MultipleHeaderActivity.java) | [SpecialDecorView(scrolling)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ScrollingToolbarActivity.java) | [BottomDecorView(editor)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/BottomEditorActivity.java) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|  |  |  |  |
## Gradle
在根目录的 `build.gradle` 添加:
```groovy
allprojects {
repositories {
// ...
maven { url 'https://www.jitpack.io' }
}
}
```
在模块的 `build.gradle` 添加依赖:
```groovy
dependencies {
// java
implementation 'com.github.DylanCaiCoding.LoadingStateView:loadingstateview:5.0.0'
// kotlin
implementation 'com.github.DylanCaiCoding.LoadingStateView:loadingstateview-ktx:5.0.0'
}
```
## 更新日志
[Releases](https://github.com/DylanCaiCoding/LoadingStateView/releases)
## 作者其它的库
| 库 | 简介 |
| ------------------------------------------------------------ | ---------------------------------------------- |
| [Longan](https://github.com/DylanCaiCoding/Longan) | 可能是最好用的 Kotlin 工具库 |
| [ViewBindingKTX](https://github.com/DylanCaiCoding/ViewBindingKTX) | 最全面的 ViewBinding 工具 |
| [MMKV-KTX](https://github.com/DylanCaiCoding/MMKV-KTX) | 用属性委托的方式使用 MMKV |
| [Tracker](https://github.com/DylanCaiCoding/Tracker) | 基于西瓜视频的责任链埋点思路实现的轻量级埋点框架 |
## 感谢
- [luckbilly/Gloading](https://github.com/luckybilly/Gloading) 站在了巨人肩膀上优化了本库,非常感谢!
- [drakeet/MultiType](https://github.com/drakeet/MultiType) 参考了注册配置多适配器的思想和用法
- [dinuscxj/LoadingDrawable](https://github.com/dinuscxj/LoadingDrawable) 示例中的自定义加载动画
## License
```
Copyright (C) 2019. Dylan Cai
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
================================================
FILE: docs/_sidebar.md
================================================
* [介绍](/)
* [基础用法](/zh/basic-usage)
* [Kotlin 委托用法](/zh/delegate)
* [结合 ViewBinding 使用](/zh/viewbinding)
* [老版本迁移指南](/zh/migration-guide)
* [Q&A](/zh/q&a)
================================================
FILE: docs/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'LoadingStateView',
repo: 'https://github.com/DylanCaiCoding/LoadingStateView',
themeColor: '#1E88E5',
loadSidebar: true,
subMaxLevel: 3,
auto2top: true,
tabs: {
persist : true,
sync : true,
theme : 'classic',
tabComments: true,
tabHeadings: true
}
}
</script>
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-kotlin.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-java.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-groovy.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-tabs@1"></script>
</body>
</html>
================================================
FILE: docs/zh/basic-usage.md
================================================
# 基础用法
使用 Kotlin 开发推荐用[委托用法](/zh/delegate)。
## 显示加载中、加载失败等缺省页
第一步,使用 Activity 或 View 创建 `LoadingStateView`,不需要重新加载的话 `OnReloadListener` 可不传。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
val loadingStateView = LoadingStateView(this, onReloadListener)
// val loadingStateView = LoadingStateView(view, onReloadListener)
```
#### **Java**
```java
LoadingStateView loadingStateView = new LoadingStateView(this, onReloadListener);
// LoadingStateView loadingStateView = new LoadingStateView(view, onReloadListener);
```
<!-- tabs:end -->
第二步,创建一个类继承 `LoadingStateView.ViewDelegate`。
构造函数需要传个参数,决定视图类型,之后显示和更新会用到。默认提供了 `ViewType.LOADING`、`ViewType.ERROR`、`ViewType.EMPTY`、`ViewType.TITLE`,其它的可以用一个 String 作为类型。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
class LoadingViewDelegate : LoadingStateView.ViewDelegate(ViewType.LOADING) {
override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup): View =
inflater.inflate(R.layout.layout_loading, parent, false)
}
```
#### **Java**
```java
public class LoadingViewDelegate extends LoadingStateView.ViewDelegate {
public LoadingViewDelegate() {
super(ViewType.LOADING);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
return inflater.inflate(R.layout.layout_loading, parent, false);
}
}
```
<!-- tabs:end -->
如果需要实现点击重新请求数据,可以在点击事件调用 `onReloadListener?.onReload()` 方法。
第三步,注册 `ViewDelegate`。首先在 Application 注册全局的 `ViewDelegate`:
<!-- tabs:start -->
#### **Kotlin**
```kotlin
LoadingStateView.setViewDelegatePool {
register(LoadingViewDelegate(), ErrorViewDelegate(), EmptyViewDelegate())
}
```
#### **Java**
```java
LoadingStateView.setViewDelegatePool(pool ->
pool.register(new LoadingViewDelegate(), new ErrorViewDelegate(), new EmptyViewDelegate()));
```
<!-- tabs:end -->
如果某个页面需要不同样式,就用该页面的 `LoadingStateView` 注册对应类型的 `ViewDelegate`,就可以把全局的替换了。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
loadingStateView.register(CoolLoadingViewDelegate())
```
#### **Java**
```java
loadingStateView.register(new CoolLoadingViewDelegate());
```
<!-- tabs:end -->
第四步,显示对应类型的视图。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
loadingStateView.showView(viewType)
loadingStateView.showLoadingView() // 显示 ViewType.LOADING 类型的视图
loadingStateView.showContentView() // 显示 ViewType.CONTENT 类型的视图
loadingStateView.showErrorView() // 显示 ViewType.ERROR 类型的视图
loadingStateView.showEmptyView() // 显示 ViewType.EMPTY 类型的视图
```
#### **Java**
```java
loadingStateView.showView(viewType);
loadingStateView.showLoadingView(); // 对应视图类型 ViewType.LOADING
loadingStateView.showContentView(); // 对应视图类型 ViewType.CONTENT
loadingStateView.showErrorView(); // 对应视图类型 ViewType.ERROR
loadingStateView.showEmptyView(); // 对应视图类型 ViewType.EMPTY
```
<!-- tabs:end -->
## 更新视图样式
需要在 `ViewDelegate` 自行增加更新的方法,然后更新对应的 `ViewDelegate`。比如在 `ErrorViewDelegate` 增加了 `updateMsg(msg)` 方法修改请求失败的文字:
<!-- tabs:start -->
#### **Kotlin**
```kotlin
loadingStateView.updateViewDelegate<ErrorViewDelegate>(ViewType.ERROR) {
updateMsg("服务器繁忙,请稍后重试")
}
```
#### **Java**
```java
loadingStateView.updateViewDelegate(ViewType.ERROR, (ErrorViewDelegate delegate) ->
delegate.updateMsg("服务器繁忙,请稍后重试"));
```
<!-- tabs:end -->
## 在内容上方添加视图
实现标题栏或搜索栏等 `ViewDelegate`,之后就可以用 `LoadingStateView` 在内容布局的上方添加视图。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
loadingStateView.setHeaders(ToolbarViewDelegate("消息"), SearchViewDelegate())
```
#### **Java**
```java
loadingStateView.setHeaders(new ToolbarViewDelegate("消息"), new SearchViewDelegate());
```
<!-- tabs:end -->
## 给内容增加装饰
可以给内容布局外层添加一层装饰,实现更复杂的样式,非简单地在内容上方增加控件,比如带联动效果的标题栏、DrawerLayout、底部输入框等布局。
接下来以滑动隐藏标题栏的效果为例,先写一个带联动效果的标题栏布局,其中有个 FragmentLayout 是用于填充内容和显示缺省页。
```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="2dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIcon="@drawable/ic_arrow_back_ios"
android:background="@color/white"
app:titleTextAppearance="@style/ToolbarTextAppearance" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
```
然后写一个类继承 `LoadingStateView.DecorViewDelegate`。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
class ScrollingDecorViewDelegate(
private val activity: Activity,
private val title: String
) : LoadingStateView.DecorViewDelegate() {
override fun onCreateDecorView(context: Context, inflater: LayoutInflater): View {
val view = inflater.inflate(R.layout.layout_scrolling_toolbar, null)
val toolbar: Toolbar = view.findViewById(R.id.toolbar)
toolbar.title = title
toolbar.setNavigationOnClickListener { activity.finish() }
return view
}
override fun getContentParent(decorView: View): ViewGroup {
return decorView.findViewById(R.id.content_parent)
}
}
```
#### **Java**
```java
public class ScrollingDecorViewDelegate extends LoadingStateView.DecorViewDelegate {
private final Activity activity;
private final String title;
public ScrollingDecorViewDelegate(Activity activity, String title) {
this.activity = activity;
this.title = title;
}
@NotNull
@Override
public View onCreateDecorView(@NonNull Context context, @NotNull LayoutInflater inflater) {
View view = inflater.inflate(R.layout.layout_scrolling_toolbar, null);
Toolbar toolbar = view.findViewById(R.id.toolbar);
toolbar.setTitle(title);
toolbar.setNavigationOnClickListener(v -> activity.finish());
return view;
}
@NotNull
@Override
public ViewGroup getContentParent(@NotNull View decorView) {
return decorView.findViewById(R.id.content_parent);
}
}
```
<!-- tabs:end -->
与 `ViewDelegate` 不同的是,需要多实现一个 `getContentParent(decorView)` 方法来指定添加内容的容器,这里我们返回前面的 FrameLayout。
之后就可以给内容进行装饰了。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
loadingStateView.setDecorView(ScrollingDecorViewDelegate(this, "title"))
```
#### **Java**
```java
loadingStateView.setDecorView(new ScrollingDecorViewDelegate(this, "title"));
```
<!-- tabs:end -->
================================================
FILE: docs/zh/delegate.md
================================================
# Kotlin 委托用法
## 准备工作
需要修改基类,只需简单的两步就可以把本库的功能集成到基类,并且不会影响到已有的代码,只是给基类扩展了新的方法。
注意添加的依赖需要是 `loadingstateview-ktx`:
```groovy
dependencies {
implementation 'com.github.DylanCaiCoding.LoadingStateView:loadingstateview-ktx:5.0.0'
}
```
修改步骤如下:
1. 实现 `LoadingState by LoadingStateDelegate()` 。
2. 在 Activity 的 `setContentView()` 方法后执行 `decorateContentView(this)`。在 Fragment 的 `onCreateView()` 返回 `view.decorate(this)`。
<!-- tabs:start -->
#### **Activity**

<details>
<summary>查看代码</summary>
```kotlin
abstract class BaseActivity(private val layoutRes: Int) : AppCompatActivity(),
LoadingState by LoadingStateDelegate() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layoutRes)
decorateContentView(this)
}
}
```
</details>
#### **Fragment**

<details>
<summary>查看代码</summary>
```kotlin
abstract class BaseFragment(private val layoutRes: Int) : Fragment(),
LoadingState by LoadingStateDelegate() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(layoutRes, container, false)
return root.decorate(this)
}
}
```
</details>
<!-- tabs:end -->
这样改造基类后会得到以下的增强:
- 在不影响已有代码的情况下,增加了 [LoadingState](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/LoadingState.kt) 接口提供的常用方法,该接口包含了 `LoadingStateView` 所有功能。
- 如果担心对基类有什么影响,在页面重写 `override val isDecorated = false` 会把一切还原,即使调用了新增的接口方法也不会生效,请放心使用。
## 显示缺省页
先注册各类型缺省页的样式,之后才能调用对应的 `showView()` 方法。
创建类继承 `LoadingStateView.ViewDelegate`,构造函数传个视图类型参数,默认提供了 `ViewType.LOADING`、`ViewType.ERROR`、`ViewType.EMPTY`。
<!-- tabs:start -->
#### **ViewType.LOADING**
```kotlin
class LoadingViewDelegate : LoadingStateView.ViewDelegate(ViewType.LOADING) {
override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup): View =
inflater.inflate(R.layout.layout_loading, parent, false)
}
```
#### **ViewType.ERROR**
```kotlin
class ErrorViewDelegate : LoadingStateView.ViewDelegate(ViewType.ERROR) {
override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup): View =
inflater.inflate(R.layout.layout_error, parent, false).apply {
findViewById<View>(R.id.btn_reload).setOnClickListener {
onReloadListener?.onReload()
}
}
}
```
#### **ViewType.EMPTY**
```kotlin
class EmptyViewDelegate : LoadingStateView.ViewDelegate(ViewType.EMPTY) {
override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup): View =
inflater.inflate(R.layout.layout_empty, parent, false)
}
```
<!-- tabs:end -->
在 Application 注册全局的 `ViewDelegate`。
```kotlin
LoadingStateView.setViewDelegatePool {
register(LoadingViewDelegate(), ErrorViewDelegate(), EmptyViewDelegate())
}
```
在实现了基类的 `Activity` 或 `Fragment` 可以调用对应的 `showView()` 方法。
```kotlin
showLoadingView() // 显示 ViewType.LOADING 类型的视图
showErrorView() // 显示 ViewType.ERROR 类型的视图
showEmptyView() // 显示 ViewType.EMPTY 类型的视图
showContentView() // 显示原来的内容视图
showView(viewType) // 显示自定义类型的视图
```
如果需要实现点击重新加载,就在基类重写 `onReload()` 方法。
如果某个页面需要显示不同的缺省页,可以在显示前调用一下 `registerView(viewDelegate)` 方法覆盖默认的样式。比如:
```kotlin
registerView(CoolLoadingViewDelegate())
showLoadingView()
```
如果需要动态更新某个样式,在 `ViewDelegate` 自行增加更新的方法,比如在 `ErrorViewDelegate` 增加了 `updateMsg(msg)` 方法修改请求失败的文字,然后就能更新了。
```kotlin
updateView<ErrorViewDelegate>(ViewType.ERROR) {
updateMsg("服务器繁忙,请稍后重试")
}
```
## 设置标题栏
先注册标题栏样式,之后才能调用 `setToolbar()` 方法。
创建一个类继承 `BaseToolbarViewDelegate`,通常项目都有各自的标题栏封装,我们能基于已有的标题栏布局或者自定义的标题栏控件实现 `ToolbarViewDelegate`。比如:
```kotlin
class ToolbarViewDelegate : BaseToolbarViewDelegate() {
private lateinit var tvTitle: TextView
private lateinit var ivLeft: ImageView
private lateinit var ivRight: ImageView
override fun onCreateToolbar(inflater: LayoutInflater, parent: ViewGroup): View {
val view = inflater.inflate(R.layout.layout_toolbar, parent, false)
tvTitle = view.findViewById(R.id.tv_title)
ivLeft = view.findViewById(R.id.iv_left)
ivRight = view.findViewById(R.id.iv_right)
return view
}
override fun onBindToolbar(config: ToolbarConfig) {
tvTitle.text = config.title
if (config.navBtnType == NavBtnType.NONE) {
ivLeft.visibility = View.GONE
} else {
ivLeft.setOnClickListener(config.onNavClickListener)
ivLeft.visibility = View.VISIBLE
}
if (config.rightIcon != null) {
ivRight.setImageResource(config.rightIcon!!)
ivRight.setOnClickListener(config.onRightClickListener)
ivRight.visibility = View.VISIBLE
}
}
}
```
`ToolbarConfig` 提供了几个常用的属性。可以根据需要选择处理,比如上述例子只实现了有无返回键和右侧按钮的逻辑,项目中有功能相对完整的[示例代码](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/delegate/ToolbarViewDelegate.kt)。
| 属性 | 含义 |
| -------------------- | -------------------- |
| title | 标题 |
| navBtnType | 导航 (左侧) 按钮类型 |
| navIcon | 导航 (左侧) 图标 |
| navText | 导航 (左侧) 文字 |
| onNavClickListener | 导航 (左侧) 按钮点击事件 |
| rightIcon | 右侧图标 |
| rightText | 右侧文字 |
| onRightClickListener | 右侧按钮点击事件 |
`onNavClickListener` 默认执行 `finish()` 操作。`navBtnType` 默认类型是 `NavBtnType.ICON`,还有 `NavBtnType.NONE`、`NavBtnType.TEXT`、`NavBtnType.ICON_TEXT`类型。其它的属性默认为空,为空的时候不用处理使用默认样式即可。
当然这点属性肯定不能满足所有的需求,所以本库支持给 `ToolbarConfig` 增加扩展属性。比如需要动态修改右侧文字颜色:
```kotlin
var ToolbarConfig.rightTextColor: Int? by toolbarExtras() // 增加 rightTextColor 扩展属性
class ToolbarViewDelegate : BaseToolbarViewDelegate() {
// ...
override fun onBindToolbar(config: ToolbarConfig) {
// ...
config.rightTextColor?.let { tvRight.setTextColor(it) } // 处理扩展属性
}
}
```
在 Application 注册全局的标题栏 `ViewDelegate`。
```kotlin
LoadingStateView.setViewDelegatePool {
register(ToolbarViewDelegate(), // ... )
}
```
之后就能在实现了基类的 `Activity` 或 `Fragment` 设置标题栏了。
```kotlin
setToolbar() // 默认有返回键
setToolbar("title") // 有标题和返回键
setToolbar("title", NavBtnType.NONE) // 只有标题,无返回键
setToolbar("title") { // 以下可选
navIcon = R.drawable.account // 只修改返回键图标
navIcon { ... } // 只修改返回键的点击事件
navIcon(R.drawable.message) { ... } // 修改返回键的图标和点击事件
rightIcon(R.drawable.add) { ... } // 添加右侧图标
rightText("Delete") { ... } // 添加右侧文字
rightTextColor = Color.RED // 新增的扩展属性,修改右侧文字颜色
}
```
这样就多了一种添加标题栏的方式,新写的代码可以用上述的方式添加标题栏,老的代码保留已有的 `<include/>` 布局或者自定义标题栏控件的用法。样式都是一样的,因为是基于已有标题栏实现的。
如果某个页面的标题栏样式变动很大,不建议写太多扩展属性来配置,这样代码阅读性也差。推荐用新布局再写一个 `BaseToolbarViewDelegate` 的实现类,在设置标题栏之前注册,这会覆盖掉默认的样式。
```kotlin
registerView(SpecialToolbarViewDelegate())
setToolbar("title")
```
如果需要动态更新标题栏样式:
```kotlin
updateToolbar { title = "Loading..." }
```
如果有扩展属性不好配置的需求,比如开启或关闭动画,可以在 `ToolbarViewDelegate` 自行增加对应功能的函数,然后在更新的时候调用。
```kotlin
updateView<ToolbarViewDelegate>(ViewType.TITLE) {
startAnimation()
}
```
## 在顶部添加控件
可添加多个控件,比如添加标题栏和搜索栏,搜索栏需要另写一个类继承 `LoadingStateView.ViewDelegate`。
```kotlin
setHeaders(
ToolbarViewDelegate("Search") {
rightIcon(R.drawable.more) { ... }
},
SearchViewDelegate(onSearchListener)
)
```
## 给内容增加装饰
可以给内容布局再套上一层装饰,实现更复杂的样式,非简单地在顶部增加控件,比如带联动效果的标题栏、DrawerLayout、底部输入框等布局。
接下来以滑动隐藏标题栏的效果为例,先写一个带联动效果的标题栏布局,其中有个 FragmentLayout 是用于填充内容和显示缺省页。
```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="2dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIcon="@drawable/ic_arrow_back_ios"
android:background="@color/white"
app:titleTextAppearance="@style/ToolbarTextAppearance" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
```
然后写一个类继承 `LoadingStateView.DecorViewDelegate`。
```kotlin
class ScrollingDecorViewDelegate(
private val activity: Activity,
private val title: String
) : LoadingStateView.DecorViewDelegate() {
override fun onCreateDecorView(context: Context, inflater: LayoutInflater): View {
val view = inflater.inflate(R.layout.layout_scrolling_toolbar, null)
val toolbar: Toolbar = view.findViewById(R.id.toolbar)
toolbar.title = title
toolbar.setNavigationOnClickListener { activity.finish() }
return view
}
override fun getContentParent(decorView: View): ViewGroup {
return decorView.findViewById(R.id.content_parent)
}
}
```
与 `LoadingStateView.ViewDelegate` 不同的是,需要多实现一个 `getContentParent(decorView)` 方法来指定添加内容的容器,这里我们返回前面的 FrameLayout。
之后就可以给内容进行装饰了。
```kotlin
setDecorView(ScrollingDecorViewDelegate(this, "title"))
```
================================================
FILE: docs/zh/migration-guide.md
================================================
# 老版本升级指南
`4.0.1` 版本对用法进行了优化,移除了 `ViewHolder`,使用起来更加简单,不过就意味着不能兼容之前的代码。建议根据[文档](/zh/basic-usage)改成新的用法,通常是封装在基类,要改的不多,`ViewHolder` 的代码需要移到 `onCreateView()` 中。
但是如果报红的地方特别多,个人有一个折中的方案。由于之前版本只有一个两三百行的 kt 文件,那么可以把源码拷贝出来并删除依赖,也就是说改成源码的方式集成,全局改一下相关包名。然后就可以添加新版本依赖进行使用,再慢慢将老用法改成新用法。
================================================
FILE: docs/zh/q&a.md
================================================
# Q&A
## 为什么不分成标题栏库和缺省页库?
个人深思熟虑了非常久才决定写成一个库,有以下考虑:
- 支持给内容和缺省页添加头部,所以具有管理标题栏的应用场景,感觉没什么不妥。
- 大多数情况下标题栏和缺省页关联性很强,因为缺省页往往是要在标题栏下方显示,如果分成两个库就经常需要调用两个工具类,使用起来更加麻烦。
- 分成两个库可能会多一层无意义的布局嵌套。
- 即使写在一起,核心功能的实现类才 200 多行代码,还要啥自行车。由于适配器和 View 的缓存代码能复用,在解耦缺省页后,仅加多几十行代码就能把标题栏给一起解耦了,何乐而不为。
## 可以在布局上预览标题栏吗?
不能,这可能是本库唯一的缺点,但是本库解耦标题栏的收益远大于在布局上预览标题栏的收益。
================================================
FILE: docs/zh/viewbinding.md
================================================
# 结合 ViewBinding 使用
## 基础用法
如果要同时使用 `LoadingStateView` 和 `ViewBinding`,需要先得到对应的 `ViewBinding` 实例,再用其根视图去创建 `LoadingStateView`。
<!-- tabs:start -->
#### **Kotlin**
```kotlin
val loadingStateView = LoadingStateView(binding.root, onReloadListener)
```
#### **Java**
```java
LoadingStateView loadingStateView = new LoadingStateView(binding.getRoot(), onReloadListener);
```
<!-- tabs:end -->
## Kotlin 委托用法
委托用法再结合上 ViewBinding 才是个人理想中的用法。会使用到个人的另一个库 [ViewBindingKTX](https://github.com/DylanCaiCoding/ViewBindingKTX),可以快速集成 ViewBinding 到基类中。
添加依赖:
```groovy
implementation 'com.github.DylanCaiCoding.ViewBindingKTX:viewbinding-base:2.1.0'
```
根据[文档](https://dylancaicoding.github.io/ViewBindingKTX/#/zh/baseclass)集成 ViewBinding,再对 ViewBinding 的根视图进行装饰。以下是在委托用法基础上修改的代码:
<!-- tabs:start -->
#### **Activity**

<details>
<summary>查看代码</summary>
```kotlin
abstract class BaseBindingActivity<VB : ViewBinding> : AppCompatActivity(),
LoadingState by LoadingStateDelegate(), ActivityBinding<VB> by ActivityBindingDelegate() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentViewWithBinding()
binding.root.decorate(this)
}
}
```
</details>
#### **Fragment**

<details>
<summary>查看代码</summary>
```kotlin
abstract class BaseBindingFragment<VB : ViewBinding> : Fragment(),
LoadingState by LoadingStateDelegate(), FragmentBinding<VB> by FragmentBindingDelegate() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return createViewWithBinding(inflater, container).decorate(this)
}
}
```
</details>
<!-- tabs:end -->
这样封装后不仅能在 Activity 或 Fragment 获取 `binding` 属性,还能很方便地指定显示缺省页的区域。
比如我们在已有的项目迭代开发,一些页面的布局已经写了标题栏。如果直接调用 `showLoadingView()` 函数,缺省页会把标题栏给覆盖了,通常要在标题栏下方显示缺省页,此时就可以重写 `contentView` 属性,声明在哪个控件显示缺省页,比如:
```kotlin
class MainActivity : BaseBindingActivity<ActivityMainBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
showLoadingView()
// ...
}
override val contentView get() = binding.container
}
```
### 已有的基类如何修改
由于要给基类增加 ViewBinding 泛型,肯定不可能直接修改基类,这会影响到已有的代码,建议继承原基类再扩展出一个支持 ViewBinding 的基类。
假设已有的基类是这种常见的封装,通过 `getLayoutId()` 函数去设置布局。
```java
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initData();
initViews();
}
public abstract int getLayoutId();
public abstract void initData();
public abstract void initViews();
}
```
目前直接继承是实现不了的,因为需要重写 `setContentView()` 的代码,所以要先将 `setContentView()` 抽到一个函数中。
```java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initContentView();
initData();
initViews();
}
protected void initContentView() {
setContentView(getLayoutId());
}
```
之后就可以继承基类重写该函数替换掉原来的 `setContentView()` 工作。
```kotlin
abstract class BaseBindingActivity<VB : ViewBinding> : BaseActivity(),
LoadingState by LoadingStateDelegate(), OnReloadListener, Decorative,
ActivityBinding<VB> by ActivityBindingDelegate() {
override fun initContentView() {
setContentViewWithBinding()
binding.root.decorate(this, this)
}
override fun getLayoutId() = -1 // 使用 ViewBinding 后,就不需要布局 id 了
}
```
Fragment 的修改也同理。
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Nov 18 20:32:14 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.useAndroidX=true
android.enableJetifier=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: loadingstateview/.gitignore
================================================
/build
================================================
FILE: loadingstateview/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion buildConfig.compileSdkVersion
defaultConfig {
minSdkVersion buildConfig.minSdkVersion
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
subprojects {
tasks.withType(Javadoc).all { enabled = false }
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += ['-module-name', "loading_state_view"]
}
}
dependencies {
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
}
================================================
FILE: loadingstateview/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: loadingstateview/src/main/AndroidManifest.xml
================================================
<manifest package="com.dylanc.loadingstateview" />
================================================
FILE: loadingstateview/src/main/java/com/dylanc/loadingstateview/LoadingStateView.kt
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview
import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout
/**
* @author Dylan Cai
*/
class LoadingStateView @JvmOverloads constructor(
private val contentView: View,
var onReloadListener: OnReloadListener? = null
) {
lateinit var decorView: View private set
lateinit var currentViewType: Any private set
private lateinit var contentParent: ViewGroup
private val parent: ViewGroup?
private var currentView: View? = null
private var viewDelegates: HashMap<Any, ViewDelegate> = HashMap()
private val viewCaches: HashMap<Any, View> = HashMap()
/**
* Constructs a LoadingStateView with an activity and listener.
*/
@JvmOverloads
constructor(activity: Activity, listener: OnReloadListener? = null) :
this(activity.findViewById<ViewGroup>(android.R.id.content).getChildAt(0), listener)
init {
poolInitializer?.apply { PoolInitializer(this@LoadingStateView).invoke() }
parent = contentView.parent as ViewGroup?
register(ContentViewDelegate())
setDecorView(LinearDecorViewDelegate(emptyList()))
}
/**
* Adds one or more views to decorate content in the header.
*
* @param delegates the view delegates of creating view
*/
fun setHeaders(vararg delegates: ViewDelegate) = setDecorView(LinearDecorViewDelegate(delegates))
/**
* Sets an view delegate for decorating content view.
*
* @param decorViewDelegate the view delegate for decorating content view.
*/
fun setDecorView(decorViewDelegate: DecorViewDelegate) {
currentView = null
if (parent != null) {
val index = parent.indexOfChild(contentView)
if (index >= 0) {
parent.removeView(contentView)
} else {
parent.removeView(decorView)
(contentView.parent as ViewGroup).removeView(contentView)
}
decorView = decorViewDelegate.createDecorView()
parent.addView(decorView, index)
} else {
decorView = decorViewDelegate.createDecorView()
}
contentParent = decorViewDelegate.getContentParent(decorView)
showView(ViewType.CONTENT)
}
/**
* Adds child decorative header between the content and the decorative view.
*
* @param delegates the view delegates of creating view
*/
fun addChildHeaders(vararg delegates: ViewDelegate) = addChildDecorView(LinearDecorViewDelegate(delegates))
/**
* Adds child decorative view between the content and the decorative view.
*
* @param decorViewDelegate the view delegate for decorating content view.
*/
fun addChildDecorView(decorViewDelegate: DecorViewDelegate) {
contentParent.removeView(currentView)
currentView = null
val childDecorView = decorViewDelegate.createDecorView()
contentParent.addView(childDecorView)
contentParent = decorViewDelegate.getContentParent(childDecorView)
showView(ViewType.CONTENT)
}
private fun DecorViewDelegate.createDecorView() =
onCreateDecorView(contentView.context, LayoutInflater.from(contentView.context)).also { decorView ->
contentView.layoutParams?.let {
decorView.layoutParams = if (it is ConstraintLayout.LayoutParams) ConstraintLayout.LayoutParams(it) else it
(it as? ViewGroup.MarginLayoutParams)?.setMargins(0, 0, 0, 0)
}
}
/**
* Registers the view delegate of creating view before showing view.
*
* @param delegates the view delegate of creating view
*/
fun register(vararg delegates: ViewDelegate) = delegates.forEach { viewDelegates[it.viewType] = it }
@JvmOverloads
fun showLoadingView(animatable: Animatable? = defaultAnimatable) = showView(ViewType.LOADING, animatable)
@JvmOverloads
fun showContentView(animatable: Animatable? = defaultAnimatable) = showView(ViewType.CONTENT, animatable)
@JvmOverloads
fun showErrorView(animatable: Animatable? = defaultAnimatable) = showView(ViewType.ERROR, animatable)
@JvmOverloads
fun showEmptyView(animatable: Animatable? = defaultAnimatable) = showView(ViewType.EMPTY, animatable)
/**
* Shows the view by view type
*
* @param viewType the view type of view delegate
*/
@JvmOverloads
fun showView(viewType: Any, animatable: Animatable? = defaultAnimatable) {
val currentView = currentView
if (currentView == null) {
addView(viewType)
} else {
if (viewCaches[viewType] == null) addView(viewType)
if (viewType != currentViewType) {
val nextView = getOrCreateView(viewType)
nextView.visibility = View.VISIBLE
val nextViewDelegate = getViewDelegate<ViewDelegate>(viewType)
nextViewDelegate?.onViewAttached(nextView)
getViewDelegate<ViewDelegate>(currentViewType)?.onViewDetached(nextView)
if (animatable != null && nextViewDelegate != null) {
animatable.toggleViewsAnimation(nextView, currentView, viewType, currentViewType)
} else {
currentView.visibility = View.GONE
}
this.currentView = nextView
}
}
currentViewType = viewType
}
fun <T : ViewDelegate> updateViewDelegate(viewType: Any, callback: Callback<T>) =
callback.apply { getViewDelegate<T>(viewType)?.invoke() }
@Suppress("UNCHECKED_CAST")
fun <T : ViewDelegate> getViewDelegate(viewType: Any) = viewDelegates[viewType] as? T
private fun addView(viewType: Any) {
val view = getOrCreateView(viewType)
(view.parent as? ViewGroup)?.removeView(view)
if (parent is ConstraintLayout && viewType == ViewType.CONTENT) {
val params = view.layoutParams
if (view.measuredWidth == 0) params.width = MATCH_PARENT
if (view.measuredHeight == 0) params.height = MATCH_PARENT
view.layoutParams = params
}
contentParent.addView(view)
currentView = view
}
private fun getOrCreateView(viewType: Any): View {
if (viewCaches[viewType] == null) {
val viewDelegate = requireNotNull(getViewDelegate(viewType)) { "Please register view delegate for $viewType type." }
val view = viewDelegate.onCreateView(LayoutInflater.from(contentParent.context), contentParent)
viewDelegate.onReloadListener = onReloadListener
viewCaches[viewType] = view
}
return viewCaches[viewType]!!
}
abstract class ViewDelegate(val viewType: Any) {
abstract fun onCreateView(inflater: LayoutInflater, parent: ViewGroup): View
open fun onViewAttached(view: View) = Unit
open fun onViewDetached(view: View) = Unit
var onReloadListener: OnReloadListener? = null
internal set
}
private inner class ContentViewDelegate : ViewDelegate(ViewType.CONTENT) {
override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup) = contentView
}
abstract class DecorViewDelegate {
abstract fun onCreateDecorView(context: Context, inflater: LayoutInflater): View
abstract fun getContentParent(decorView: View): ViewGroup
}
private inner class LinearDecorViewDelegate(private val views: List<View>) : DecorViewDelegate() {
private lateinit var contentParent: FrameLayout
constructor(delegates: Array<out ViewDelegate>) : this(delegates.map {
register(it)
getOrCreateView(it.viewType)
})
override fun onCreateDecorView(context: Context, inflater: LayoutInflater) =
LinearLayout(inflater.context).apply {
orientation = LinearLayout.VERTICAL
contentParent = FrameLayout(context)
contentParent.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
views.forEach { addView(it) }
addView(contentParent)
}
override fun getContentParent(decorView: View) = contentParent
}
class PoolInitializer internal constructor(private val stateView: LoadingStateView) {
fun register(vararg delegates: ViewDelegate) = stateView.register(*delegates)
}
fun interface Callback<in T> {
fun T.invoke()
}
interface Animatable {
fun toggleViewsAnimation(showView: View, hideView: View, showViewType: Any, hideViewType: Any)
}
companion object {
private var poolInitializer: Callback<PoolInitializer>? = null
@JvmStatic
var defaultAnimatable: Animatable? = null
@JvmStatic
fun setViewDelegatePool(poolInitializer: Callback<PoolInitializer>) {
this.poolInitializer = poolInitializer
}
}
}
interface OnReloadListener {
fun onReload() = Unit
}
enum class ViewType {
TITLE, LOADING, CONTENT, ERROR, EMPTY
}
================================================
FILE: loadingstateview/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">Library</string>
</resources>
================================================
FILE: loadingstateview-ktx/.gitignore
================================================
/build
================================================
FILE: loadingstateview-ktx/build.gradle
================================================
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdkVersion buildConfig.compileSdkVersion
defaultConfig {
minSdkVersion buildConfig.minSdkVersion
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += ['-module-name', "loading_state_view_ktx"]
}
}
dependencies {
api project(':loadingstateview')
implementation "androidx.appcompat:appcompat:$appCompatVersion"
}
================================================
FILE: loadingstateview-ktx/consumer-rules.pro
================================================
================================================
FILE: loadingstateview-ktx/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: loadingstateview-ktx/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dylanc.loadingstateview.ext">
</manifest>
================================================
FILE: loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/BaseToolbarViewDelegate.kt
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
abstract class BaseToolbarViewDelegate : LoadingStateView.ViewDelegate(ViewType.TITLE) {
internal lateinit var config: ToolbarConfig
override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup) =
onCreateToolbar(inflater, parent).apply { onBindToolbar(config) }
abstract fun onCreateToolbar(inflater: LayoutInflater, parent: ViewGroup): View
abstract fun onBindToolbar(config: ToolbarConfig)
}
================================================
FILE: loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/Decorative.kt
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview
import android.view.View
interface Decorative : OnReloadListener {
val isDecorated: Boolean get() = true
val contentView: View? get() = null
}
================================================
FILE: loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/LoadingState.kt
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview
import android.app.Activity
import android.view.View
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
interface LoadingState : Decorative, OnReloadListener {
val loadingStateViewType: Any?
@Deprecated("Use Activity.decorateContentView(this) instead", ReplaceWith("decorateContentView(decorative)"))
fun Activity.decorateContentView(listener: OnReloadListener, decorative: Decorative) = decorateContentView(decorative)
fun Activity.decorateContentView(decorative: Decorative)
@Deprecated("Use View.decorate(this) instead", ReplaceWith("decorate(decorative)"))
fun View.decorate(listener: OnReloadListener, decorative: Decorative): View = decorate(decorative)
fun View.decorate(decorative: Decorative): View
fun registerView(vararg viewDelegates: LoadingStateView.ViewDelegate)
fun Activity.setToolbar(@StringRes titleId: Int, navBtnType: NavBtnType = NavBtnType.ICON, block: (ToolbarConfig.() -> Unit)? = null)
fun Activity.setToolbar(title: String? = null, navBtnType: NavBtnType = NavBtnType.ICON, block: (ToolbarConfig.() -> Unit)? = null)
fun Fragment.setToolbar(@StringRes titleId: Int, navBtnType: NavBtnType = NavBtnType.ICON, block: (ToolbarConfig.() -> Unit)? = null)
fun Fragment.setToolbar(title: String? = null, navBtnType: NavBtnType = NavBtnType.ICON, block: (ToolbarConfig.() -> Unit)? = null)
fun Activity.setHeaders(vararg delegates: LoadingStateView.ViewDelegate)
fun Fragment.setHeaders(vararg delegates: LoadingStateView.ViewDelegate)
fun Activity.setDecorView(delegate: LoadingStateView.DecorViewDelegate)
fun Fragment.setDecorView(delegate: LoadingStateView.DecorViewDelegate)
fun showLoadingView(animatable: LoadingStateView.Animatable? = LoadingStateView.defaultAnimatable)
fun showContentView(animatable: LoadingStateView.Animatable? = LoadingStateView.defaultAnimatable)
fun showErrorView(animatable: LoadingStateView.Animatable? = LoadingStateView.defaultAnimatable)
fun showEmptyView(animatable: LoadingStateView.Animatable? = LoadingStateView.defaultAnimatable)
fun showCustomView(viewType: Any, animatable: LoadingStateView.Animatable? = LoadingStateView.defaultAnimatable)
fun updateToolbar(block: ToolbarConfig.() -> Unit)
fun <T : LoadingStateView.ViewDelegate> updateView(viewType: Any, block: T.() -> Unit)
@Suppress("FunctionName")
fun ToolbarViewDelegate(
title: String? = null, navBtnType: NavBtnType = NavBtnType.ICON, block: (ToolbarConfig.() -> Unit)? = null
): BaseToolbarViewDelegate
}
================================================
FILE: loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/LoadingStateDelegate.kt
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview
import android.app.Activity
import android.view.View
import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
class LoadingStateDelegate : LoadingState {
private var loadingStateView: LoadingStateView? = null
override val loadingStateViewType: Any?
get() = loadingStateView?.currentViewType
override fun Activity.decorateContentView(decorative: Decorative) {
findViewById<ViewGroup>(android.R.id.content).getChildAt(0).decorate(decorative)
}
override fun View.decorate(decorative: Decorative): View =
when {
!decorative.isDecorated -> this
decorative.contentView == null ->
LoadingStateView(this, decorative).also { loadingStateView = it }.decorView
else -> {
loadingStateView = LoadingStateView(decorative.contentView!!, decorative)
this
}
}
override fun registerView(vararg viewDelegates: LoadingStateView.ViewDelegate) {
loadingStateView?.register(*viewDelegates)
}
override fun Activity.setToolbar(@StringRes titleId: Int, navBtnType: NavBtnType, block: (ToolbarConfig.() -> Unit)?) {
setToolbar(getString(titleId), navBtnType, block)
}
override fun Activity.setToolbar(title: String?, navBtnType: NavBtnType, block: (ToolbarConfig.() -> Unit)?) {
loadingStateView?.setHeaders(ToolbarViewDelegate(title, navBtnType, block))
}
override fun Fragment.setToolbar(@StringRes titleId: Int, navBtnType: NavBtnType, block: (ToolbarConfig.() -> Unit)?) {
setToolbar(getString(titleId), navBtnType, block)
}
override fun Fragment.setToolbar(title: String?, navBtnType: NavBtnType, block: (ToolbarConfig.() -> Unit)?) {
loadingStateView?.addChildHeaders(ToolbarViewDelegate(title, navBtnType, block))
}
override fun Activity.setHeaders(vararg delegates: LoadingStateView.ViewDelegate) {
loadingStateView?.setHeaders(*delegates)
}
override fun Fragment.setHeaders(vararg delegates: LoadingStateView.ViewDelegate) {
loadingStateView?.addChildHeaders(*delegates)
}
override fun Activity.setDecorView(delegate: LoadingStateView.DecorViewDelegate) {
loadingStateView?.setDecorView(delegate)
}
override fun Fragment.setDecorView(delegate: LoadingStateView.DecorViewDelegate) {
loadingStateView?.addChildDecorView(delegate)
}
override fun showLoadingView(animatable: LoadingStateView.Animatable?) {
loadingStateView?.showLoadingView(animatable)
}
override fun showContentView(animatable: LoadingStateView.Animatable?) {
loadingStateView?.showContentView(animatable)
}
override fun showErrorView(animatable: LoadingStateView.Animatable?) {
loadingStateView?.showErrorView(animatable)
}
override fun showEmptyView(animatable: LoadingStateView.Animatable?) {
loadingStateView?.showEmptyView(animatable)
}
override fun showCustomView(viewType: Any, animatable: LoadingStateView.Animatable?) {
loadingStateView?.showView(viewType, animatable)
}
override fun updateToolbar(block: ToolbarConfig.() -> Unit) {
updateView<BaseToolbarViewDelegate>(ViewType.TITLE) { onBindToolbar(config.apply(block)) }
}
override fun <T : LoadingStateView.ViewDelegate> updateView(viewType: Any, block: T.() -> Unit) {
loadingStateView?.updateViewDelegate(viewType, block)
}
override fun ToolbarViewDelegate(title: String?, navBtnType: NavBtnType, block: (ToolbarConfig.() -> Unit)?) =
requireNotNull(loadingStateView?.getViewDelegate<BaseToolbarViewDelegate>(ViewType.TITLE)) {
"ToolbarViewDelegate must be registered before."
}.apply {
config = ToolbarConfig(title, navBtnType).apply { block?.invoke(this) }
}
}
================================================
FILE: loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/ToolbarConfig.kt
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("unused")
package com.dylanc.loadingstateview
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.View
import androidx.annotation.DrawableRes
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
enum class NavBtnType {
ICON, TEXT, ICON_TEXT, NONE
}
class ToolbarConfig(
var title: String? = null,
var navBtnType: NavBtnType = NavBtnType.ICON,
val extras: HashMap<String, Any?> = HashMap(),
) {
@DrawableRes
var navIcon: Int? = null
var navText: String? = null
private set
var onNavClickListener = View.OnClickListener {
var context: Context? = it.context
while (context is ContextWrapper) {
if (context is Activity) {
context.finish()
return@OnClickListener
}
context = context.baseContext
}
}
private set
@DrawableRes
var rightIcon: Int? = null
private set
var rightText: String? = null
private set
var onRightClickListener: View.OnClickListener? = null
private set
fun navIcon(@DrawableRes icon: Int? = navIcon, listener: View.OnClickListener) {
navIcon = icon
onNavClickListener = listener
}
fun navText(text: String, listener: View.OnClickListener) {
navText = text
onNavClickListener = listener
}
fun rightIcon(@DrawableRes icon: Int, listener: View.OnClickListener) {
rightIcon = icon
onRightClickListener = listener
}
fun rightText(text: String, listener: View.OnClickListener) {
rightText = text
onRightClickListener = listener
}
}
fun <T> toolbarExtras() = object : ReadWriteProperty<ToolbarConfig, T?> {
@Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: ToolbarConfig, property: KProperty<*>): T? =
thisRef.extras[property.name] as? T
override fun setValue(thisRef: ToolbarConfig, property: KProperty<*>, value: T?) {
thisRef.extras[property.name] = value
}
}
================================================
FILE: sample-java/.gitignore
================================================
/build
================================================
FILE: sample-java/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
compileSdkVersion buildConfig.compileSdkVersion
defaultConfig {
applicationId "com.dylanc.loadingstateview.sample.java"
minSdkVersion buildConfig.minSdkVersion
targetSdkVersion buildConfig.targetSdkVersion
versionCode buildConfig.versionCode
versionName buildConfig.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
viewBinding {
enabled = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':loadingstateview')
implementation "androidx.appcompat:appcompat:$appCompatVersion"
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
implementation "com.google.android.material:material:$materialVersion"
implementation "com.github.bumptech.glide:glide:$glideVersion"
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$junitExtVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
}
================================================
FILE: sample-java/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep public class com.dylanc.loadingstateview.sample.java.widget.**{*;}
================================================
FILE: sample-java/release/output.json
================================================
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":2,"versionName":"1.0.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]
================================================
FILE: sample-java/src/androidTest/java/com/dylanc/loadingstateview/sample/java/ExampleInstrumentedTest.kt
================================================
package com.dylanc.loadingstateview.sample.java
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.dylanc.loadinghelper.sample", appContext.packageName)
}
}
================================================
FILE: sample-java/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.dylanc.loadingstateview.sample.java">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@drawable/ic_logo"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.ActErrorActivity" />
<activity android:name=".ui.FragmentEmptyActivity" />
<activity android:name=".ui.ViewPlaceholderActivity" />
<activity android:name=".ui.ViewPagerActivity" />
<activity android:name=".ui.RecyclerViewActivity" />
<activity android:name=".ui.CustomHeaderActivity" />
<activity android:name=".ui.MultipleHeaderActivity" />
<activity android:name=".ui.ScrollingToolbarActivity" />
<activity android:name=".ui.BottomEditorActivity"/>
</application>
</manifest>
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/App.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java;
import android.app.Application;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.sample.java.animation.FadeAnimatable;
import com.dylanc.loadingstateview.sample.java.delegate.EmptyViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.ErrorViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.LoadingViewDelegate;
/**
* @author Dylan Cai
*/
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
LoadingStateView.setViewDelegatePool(pool ->
pool.register(new LoadingViewDelegate(), new ErrorViewDelegate(), new EmptyViewDelegate()));
LoadingStateView.setDefaultAnimatable(new FadeAnimatable());
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/animation/FadeAnimatable.java
================================================
package com.dylanc.loadingstateview.sample.java.animation;
import android.view.View;
import androidx.annotation.NonNull;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import org.jetbrains.annotations.NotNull;
/**
* @author Dylan Cai
*/
public class FadeAnimatable implements LoadingStateView.Animatable {
private static final long DEFAULT_DURATION = 500;
private final long duration;
public FadeAnimatable() {
this(DEFAULT_DURATION);
}
public FadeAnimatable(long duration) {
this.duration = duration;
}
@Override
public void toggleViewsAnimation(@NonNull View showView, @NonNull View hideView, @NonNull Object showViewType, @NonNull Object hideViewType) {
showView.setAlpha(0);
showView.setVisibility(View.VISIBLE);
showView.animate().alpha(1).setDuration(duration);
if (showViewType == ViewType.LOADING && hideViewType == ViewType.CONTENT) {
hideView.setVisibility(View.GONE);
} else {
hideView.animate().alpha(0).setDuration(duration).withEndAction(() -> {
hideView.setAlpha(1);
hideView.setVisibility(View.GONE);
});
}
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/base/BaseActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.base;
import androidx.annotation.IdRes;
import androidx.appcompat.app.AppCompatActivity;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.OnReloadListener;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.delegate.ToolbarViewDelegate;
/**
* 这是耦合度较低的封装方式,没有任何抽象方法,可以很方便地将基类里的代码拷贝到其它项目的基类里使用。
* <p>
* 使用该基类时注意以下事项:
* 1. 显示对应视图之前需要注册适配器,可以设置全局适配器,某个页面想修改样式时再注册个新的适配器。
* 2. 设置标题栏的方法应该根据项目需要进行编写,下面提供了参考示例。
*
* @author Dylan Cai
*/
@SuppressWarnings("unused")
public class BaseActivity extends AppCompatActivity implements OnReloadListener {
private LoadingStateView loadingStateView;
@Override
public void setContentView(int layoutResID) {
this.setContentView(layoutResID, 0);
}
public void setContentView(int layoutResID, @IdRes int contentViewId) {
super.setContentView(layoutResID);
if (contentViewId == 0) {
loadingStateView = new LoadingStateView(this, this);
} else {
loadingStateView = new LoadingStateView(findViewById(contentViewId));
}
}
/**
* 添加标题栏的示例方法,请根据自己的需求进行修改
*/
public void setToolbar(String title) {
setToolbar(title, NavIconType.BACK, 0);
}
public void setToolbar(String title, NavIconType type) {
setToolbar(title, type, 0);
}
public void setToolbar(String title, NavIconType type, int menuId) {
loadingStateView.setHeaders(new ToolbarViewDelegate(title, type, menuId, this::onOptionsItemSelected));
}
public void showLoadingView() {
loadingStateView.showLoadingView();
}
public void showContentView() {
loadingStateView.showContentView();
}
public void showErrorView() {
loadingStateView.showErrorView();
}
public void showEmptyView() {
loadingStateView.showEmptyView();
}
public void showCustomView(Object viewType) {
loadingStateView.showView(viewType);
}
@Override
public void onReload() {
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/BottomEditorDecorViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import androidx.annotation.NonNull;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.utils.KeyboardUtils;
import org.jetbrains.annotations.NotNull;
/**
* @author Dylan Cai
*/
public class BottomEditorDecorViewDelegate extends LoadingStateView.DecorViewDelegate {
private final OnSendListener onSendListener;
public BottomEditorDecorViewDelegate(OnSendListener onSendListener) {
this.onSendListener = onSendListener;
}
@NotNull
@Override
@SuppressLint("InflateParams")
public View onCreateDecorView(@NonNull Context context, @NotNull LayoutInflater inflater) {
View view = inflater.inflate(R.layout.layout_bottom_editor, null);
EditText edtContent = view.findViewById(R.id.edt_content);
view.findViewById(R.id.btn_send).setOnClickListener(v -> {
if (onSendListener != null) {
onSendListener.onSend(edtContent.getText().toString());
edtContent.setText("");
KeyboardUtils.hideKeyboard(edtContent);
}
});
return view;
}
@NotNull
@Override
public ViewGroup getContentParent(@NotNull View decorView) {
return decorView.findViewById(R.id.content_parent);
}
public interface OnSendListener {
void onSend(String content);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/CoolLoadingViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class CoolLoadingViewDelegate extends LoadingStateView.ViewDelegate {
public CoolLoadingViewDelegate() {
super(ViewType.LOADING);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
return inflater.inflate(R.layout.layout_cool_loading, parent, false);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/CustomHeaderViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import android.app.Activity;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class CustomHeaderViewDelegate extends LoadingStateView.ViewDelegate {
private final View.OnClickListener onMessageClickListener;
private final int firstDrawableId;
private final View.OnClickListener onFirstBtnClickListener;
private final int secondDrawableId;
private final View.OnClickListener onSecondBtnClickListener;
public CustomHeaderViewDelegate(View.OnClickListener onMessageClickListener, int firstDrawableId,
View.OnClickListener onFirstBtnClickListener, int secondDrawableId,
View.OnClickListener onSecondBtnClickListener) {
super(ViewType.TITLE);
this.onMessageClickListener = onMessageClickListener;
this.firstDrawableId = firstDrawableId;
this.onFirstBtnClickListener = onFirstBtnClickListener;
this.secondDrawableId = secondDrawableId;
this.onSecondBtnClickListener = onSecondBtnClickListener;
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.layout_custom_header, parent, false);
ImageView btnFirst = view.findViewById(R.id.btn_first);
ImageView btnSecond = view.findViewById(R.id.btn_second);
View btnMessage = view.findViewById(R.id.btn_message);
Activity activity = (Activity) view.getContext();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
btnMessage.setOnClickListener(onMessageClickListener);
btnFirst.setImageDrawable(ContextCompat.getDrawable(activity, firstDrawableId));
btnFirst.setOnClickListener(onFirstBtnClickListener);
btnSecond.setImageDrawable(ContextCompat.getDrawable(activity, secondDrawableId));
btnSecond.setOnClickListener(onSecondBtnClickListener);
return view;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/EmptyViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class EmptyViewDelegate extends LoadingStateView.ViewDelegate {
public EmptyViewDelegate() {
super(ViewType.EMPTY);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
return inflater.inflate(R.layout.layout_empty, parent, false);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ErrorViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class ErrorViewDelegate extends LoadingStateView.ViewDelegate {
public ErrorViewDelegate() {
super(ViewType.ERROR);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.layout_error, parent, false);
view.findViewById(R.id.btn_reload).setOnClickListener(v -> {
if (getOnReloadListener() != null) {
getOnReloadListener().onReload();
}
});
return view;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/LoadingViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class LoadingViewDelegate extends LoadingStateView.ViewDelegate {
public int height = ViewGroup.LayoutParams.MATCH_PARENT;
public LoadingViewDelegate() {
super(ViewType.LOADING);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.layout_loading, parent, false);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = height;
view.setLayoutParams(layoutParams);
return view;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/NavIconType.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
/**
* @author Dylan Cai
*/
public enum NavIconType {
BACK, NONE
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/NothingViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
/**
* @author Dylan Cai
*/
public class NothingViewDelegate extends LoadingStateView.ViewDelegate {
public NothingViewDelegate() {
super(ViewType.EMPTY);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
return new View(parent.getContext());
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/PlaceholderViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class PlaceholderViewDelegate extends LoadingStateView.ViewDelegate {
public PlaceholderViewDelegate() {
super(ViewType.LOADING);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
return inflater.inflate(R.layout.layout_placeholder, parent, false);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ScrollingDecorViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.sample.java.R;
import org.jetbrains.annotations.NotNull;
/**
* @author Dylan Cai
*/
public class ScrollingDecorViewDelegate extends LoadingStateView.DecorViewDelegate {
private final String title;
public ScrollingDecorViewDelegate(String title) {
this.title = title;
}
@NotNull
@Override
@SuppressLint("InflateParams")
public View onCreateDecorView(@NonNull Context context, @NotNull LayoutInflater inflater) {
View view = inflater.inflate(R.layout.layout_scrolling_toolbar, null);
Activity activity = (Activity) inflater.getContext();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
Toolbar toolbar = view.findViewById(R.id.toolbar);
toolbar.setTitle(title);
toolbar.setNavigationOnClickListener(v -> activity.finish());
return view;
}
@NotNull
@Override
public ViewGroup getContentParent(@NotNull View decorView) {
return decorView.findViewById(R.id.content_parent);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/SearchHeaderViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import androidx.annotation.NonNull;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.utils.KeyboardUtils;
/**
* @author Dylan Cai
*/
public class SearchHeaderViewDelegate extends LoadingStateView.ViewDelegate{
public static final String VIEW_TYPE_SEARCH = "search";
private final OnSearchListener onSearchListener;
public SearchHeaderViewDelegate(OnSearchListener onSearchListener) {
super(VIEW_TYPE_SEARCH);
this.onSearchListener = onSearchListener;
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.layout_search_header, parent, false);
EditText edtSearch = view.findViewById(R.id.edt_search);
edtSearch.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
KeyboardUtils.hideKeyboard(edtSearch);
if (onSearchListener != null) {
onSearchListener.onSearch(edtSearch.getText().toString());
}
return true;
}
return false;
});
return view;
}
public interface OnSearchListener {
void onSearch(String keyword);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/TimeoutViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class TimeoutViewDelegate extends LoadingStateView.ViewDelegate {
public static final String VIEW_TYPE_TIMEOUT = "timeout";
public TimeoutViewDelegate() {
super(VIEW_TYPE_TIMEOUT);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.layout_timeout, parent, false);
view.setOnClickListener(v -> {
if (getOnReloadListener() != null) {
getOnReloadListener().onReload();
}
});
return view;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ToolbarViewDelegate.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.delegate;
import android.app.Activity;
import android.os.Build;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
import org.jetbrains.annotations.NotNull;
import kotlin.jvm.functions.Function1;
/**
* @author Dylan Cai
*/
public class ToolbarViewDelegate extends LoadingStateView.ViewDelegate {
private final String title;
private final NavIconType type;
private final int menuId;
private final Function1<? super MenuItem, Boolean> onMenuItemClick;
public ToolbarViewDelegate(String title, NavIconType type) {
this(title, type, 0, null);
}
public ToolbarViewDelegate(String title, NavIconType type, int menuId, Function1<? super MenuItem, Boolean> onMenuItemClick) {
super(ViewType.TITLE);
this.title = title;
this.type = type;
this.menuId = menuId;
this.onMenuItemClick = onMenuItemClick;
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.layout_toolbar, parent, false);
Activity activity = (Activity) view.getContext();
Toolbar toolbar = view.findViewById(R.id.toolbar);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
if (!TextUtils.isEmpty(title)) {
toolbar.setTitle(title);
}
if (type == NavIconType.BACK) {
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black);
toolbar.setNavigationOnClickListener(v -> activity.finish());
} else {
toolbar.setNavigationIcon(null);
}
if (menuId > 0 && onMenuItemClick != null) {
toolbar.inflateMenu(menuId);
toolbar.setOnMenuItemClickListener(onMenuItemClick::invoke);
}
return view;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ActErrorActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.OnReloadListener;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
/**
* @author Dylan Cai
*/
public class ActErrorActivity extends AppCompatActivity implements OnReloadListener {
private LoadingStateView loadingStateView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_content);
loadingStateView = ToolbarUtils.setToolbar(this, "Activity(error)", NavIconType.BACK);
loadingStateView.setOnReloadListener(this);
loadData();
}
private void loadData() {
loadingStateView.showLoadingView();
HttpUtils.requestFailure(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showErrorView();
}
});
}
@Override
public void onReload() {
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showErrorView();
}
});
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/BottomEditorActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.BottomEditorDecorViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.NothingViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
/**
* @author Dylan Cai
*/
public class BottomEditorActivity extends AppCompatActivity implements BottomEditorDecorViewDelegate.OnSendListener {
private LoadingStateView loadingStateView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_content);
loadingStateView = ToolbarUtils.setToolbar(this, "BottomDecorView(editor)", NavIconType.BACK);
loadingStateView.addChildDecorView(new BottomEditorDecorViewDelegate(this));
loadingStateView.register(new NothingViewDelegate());
loadingStateView.showEmptyView();
}
@Override
public void onSend(String content) {
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showErrorView();
}
});
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/CustomHeaderActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.ui.fragment.SimpleFragment;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
import com.google.android.material.tabs.TabLayout;
/**
* @author Dylan Cai
*/
public class CustomHeaderActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
ToolbarUtils.setCustomToolbar(this, this::onMessageClick,
R.drawable.ic_baseline_photo_camera_24, this::onFirstBtnClick,
R.drawable.ic_baseline_favorite_24, this::onSecondBtnClick);
// This TabLayout is in the custom toolbar.
TabLayout tabLayout = findViewById(R.id.tab_layout);
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(new TabPagerAdapter(getSupportFragmentManager()));
tabLayout.setupWithViewPager(viewPager);
}
private void onMessageClick(View view) {
Toast.makeText(this, "message", Toast.LENGTH_SHORT).show();
}
private void onFirstBtnClick(View view) {
Toast.makeText(this, "camera", Toast.LENGTH_SHORT).show();
}
private void onSecondBtnClick(View view) {
Toast.makeText(this, "favorite", Toast.LENGTH_SHORT).show();
}
public static class TabPagerAdapter extends FragmentPagerAdapter {
public TabPagerAdapter(FragmentManager fm) {
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NonNull
@Override
public Fragment getItem(int i) {
return new SimpleFragment();
}
@Override
public int getCount() {
return 2;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return "tab " + position;
}
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/FragmentEmptyActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.ui.fragment.EmptyFragment;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
/**
* @author Dylan Cai
*/
public class FragmentEmptyActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
ToolbarUtils.setToolbar(this,"Fragment(empty)", NavIconType.BACK);
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.content_view, new EmptyFragment());
transaction.commit();
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MainActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.base.BaseActivity;
/**
* @author Dylan Cai
*/
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setToolbar(getString(R.string.app_name), NavIconType.NONE, R.menu.menu_about);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.about) {
Uri uri = Uri.parse("https://github.com/DylanCaiCoding/LoadingHelper");
Intent intent = new Intent("android.intent.action.VIEW", uri);
startActivity(intent);
}
return true;
}
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn_activity_error:
startActivity(new Intent(this, ActErrorActivity.class));
break;
case R.id.btn_fragment_empty:
startActivity(new Intent(this, FragmentEmptyActivity.class));
break;
case R.id.btn_view_placeholder:
startActivity(new Intent(this, ViewPlaceholderActivity.class));
break;
case R.id.btn_viewpager_timeout:
startActivity(new Intent(this, ViewPagerActivity.class));
break;
case R.id.btn_recyclerview_loading:
startActivity(new Intent(this, RecyclerViewActivity.class));
break;
case R.id.btn_custom_header:
startActivity(new Intent(this, CustomHeaderActivity.class));
break;
case R.id.btn_search_header:
startActivity(new Intent(this, MultipleHeaderActivity.class));
break;
case R.id.btn_scrolling:
startActivity(new Intent(this, ScrollingToolbarActivity.class));
break;
case R.id.btn_bottom_editor:
startActivity(new Intent(this, BottomEditorActivity.class));
break;
}
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MultipleHeaderActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.NothingViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.SearchHeaderViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.ToolbarViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
/**
* @author Dylan Cai
*/
public class MultipleHeaderActivity extends AppCompatActivity implements SearchHeaderViewDelegate.OnSearchListener {
private LoadingStateView loadingStateView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_content);
loadingStateView = new LoadingStateView(this);
loadingStateView.register(new NothingViewDelegate());
loadingStateView.setHeaders(
new ToolbarViewDelegate("MultipleHeader(search)", NavIconType.BACK),
new SearchHeaderViewDelegate(this)
);
loadingStateView.showEmptyView();
}
@Override
public void onSearch(String keyword) {
Toast.makeText(this, "search: " + keyword, Toast.LENGTH_SHORT).show();
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showErrorView();
}
});
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/RecyclerViewActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.CoolLoadingViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
/**
* @author Dylan Cai
*/
public class RecyclerViewActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_view);
ToolbarUtils.setToolbar(this,"RecyclerView(cool loading)", NavIconType.BACK);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setAdapter(new ImageAdapter());
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
}
public static class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item_image, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
holder.showImage(HttpUtils.getRandomImageUrl());
}
@Override
public int getItemCount() {
return 10;
}
static class ViewHolder extends RecyclerView.ViewHolder{
private final LoadingStateView loadingStateView;
ImageView imageView;
private String url;
ViewHolder(@NonNull View itemView) {
super(itemView);
loadingStateView = new LoadingStateView(itemView.findViewById(R.id.loading_view));
loadingStateView.register(new CoolLoadingViewDelegate());
loadingStateView.setOnReloadListener(() -> showImage(url));
imageView = itemView.findViewById(R.id.image_view);
}
void showImage(String url) {
this.url = url;
loadingStateView.showLoadingView();
Glide.with(imageView.getContext())
.load(url)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target,
boolean isFirstResource) {
loadingStateView.showErrorView();
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target,
DataSource dataSource, boolean isFirstResource) {
loadingStateView.showContentView();
return false;
}
})
.into(imageView);
}
}
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ScrollingToolbarActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.LoadingViewDelegate;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
/**
* @author Dylan Cai
*/
public class ScrollingToolbarActivity extends AppCompatActivity {
private LoadingStateView loadingStateView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
loadingStateView = ToolbarUtils.setScrollingToolbar(this, "SpecialDecorView(scrolling)");
loadingStateView.updateViewDelegate(ViewType.LOADING, (LoadingViewDelegate delegate) ->
delegate.height = ViewGroup.LayoutParams.WRAP_CONTENT);
loadData();
}
private void loadData() {
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showErrorView();
}
});
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPagerActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.ui.fragment.TimeoutFragment;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
import com.google.android.material.tabs.TabLayout;
/**
* @author Dylan Cai
*/
public class ViewPagerActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_layout);
ToolbarUtils.setToolbar(this, "ViewPager(timeout)", NavIconType.BACK);
TabLayout tabLayout = findViewById(R.id.tab_layout);
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(new TabPagerAdapter(getSupportFragmentManager()));
tabLayout.setupWithViewPager(viewPager);
}
public static class TabPagerAdapter extends FragmentPagerAdapter {
public TabPagerAdapter(FragmentManager fm) {
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NonNull
@Override
public Fragment getItem(int i) {
return new TimeoutFragment();
}
@Override
public int getCount() {
return 2;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return "tab " + position;
}
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPlaceholderActivity.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.ViewType;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.delegate.PlaceholderViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
import com.dylanc.loadingstateview.sample.java.utils.ToolbarUtils;
/**
* @author Dylan Cai
*/
public class ViewPlaceholderActivity extends AppCompatActivity {
private LoadingStateView loadingStateView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view);
ToolbarUtils.setToolbar(this,"View(placeholder)", NavIconType.BACK);
View view = findViewById(R.id.content);
loadingStateView = new LoadingStateView(view);
loadingStateView.register(new PlaceholderViewDelegate());
loadData();
}
private void loadData() {
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showLoadingView();
}
});
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/EmptyFragment.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.OnReloadListener;
import com.dylanc.loadingstateview.sample.java.databinding.LayoutContentBinding;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
/**
* @author Dylan Cai
*/
@SuppressWarnings("FieldCanBeLocal")
public class EmptyFragment extends Fragment implements OnReloadListener {
private LayoutContentBinding binding;
private LoadingStateView loadingStateView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = LayoutContentBinding.inflate(inflater, container, false);
loadingStateView = new LoadingStateView(binding.getRoot());
loadingStateView.setOnReloadListener(this);
return loadingStateView.getDecorView();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
loadData();
}
private void loadData() {
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showEmptyView();
}
@Override
public void onFailure() {
loadingStateView.showErrorView();
}
});
}
@Override
public void onReload() {
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showEmptyView();
}
@Override
public void onFailure() {
loadingStateView.showErrorView();
}
});
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/SimpleFragment.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.dylanc.loadingstateview.sample.java.R;
/**
* @author Dylan Cai
*/
public class SimpleFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.layout_content, container, false);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/TimeoutFragment.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.OnReloadListener;
import com.dylanc.loadingstateview.sample.java.delegate.TimeoutViewDelegate;
import com.dylanc.loadingstateview.sample.java.databinding.LayoutContentBinding;
import com.dylanc.loadingstateview.sample.java.utils.HttpUtils;
/**
* @author Dylan Cai
*/
@SuppressWarnings("FieldCanBeLocal")
public class TimeoutFragment extends Fragment implements OnReloadListener {
public static final String VIEW_TYPE_TIMEOUT = "timeout";
private LayoutContentBinding binding;
private LoadingStateView loadingStateView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = LayoutContentBinding.inflate(inflater, container, false);
loadingStateView = new LoadingStateView(binding.getRoot());
loadingStateView.register(new TimeoutViewDelegate());
loadingStateView.setOnReloadListener(this);
return loadingStateView.getDecorView();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
loadData();
}
private void loadData() {
loadingStateView.showLoadingView();
HttpUtils.requestFailure(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showView(VIEW_TYPE_TIMEOUT);
}
});
}
@Override
public void onReload() {
loadingStateView.showLoadingView();
HttpUtils.requestSuccess(new HttpUtils.Callback() {
@Override
public void onSuccess() {
loadingStateView.showContentView();
}
@Override
public void onFailure() {
loadingStateView.showView(VIEW_TYPE_TIMEOUT);
}
});
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/DensityUtils.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.utils;
import android.content.Context;
/**
* @author Dylan Cai
*/
public class DensityUtils {
public static float dip2px(Context context, float dpValue) {
float scale = context.getResources().getDisplayMetrics().density;
return dpValue * scale;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/HttpUtils.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.utils;
import android.os.Handler;
import java.util.Random;
/**
* @author Dylan Cai
*/
public class HttpUtils {
/**
* 模拟请求,两秒后回调请求成功方法
*/
public static void requestSuccess(final Callback callback){
new Handler().postDelayed(() -> {
if (callback!=null){
callback.onSuccess();
}
},2000);
}
/**
* 模拟请求,两秒后回调请求失败方法
*/
public static void requestFailure(final Callback callback){
new Handler().postDelayed(() -> {
if (callback!=null){
callback.onFailure();
}
},2000);
}
public static String getRandomImageUrl(){
int position = new Random().nextInt(100);
return "https://source.unsplash.com/collection/"+position+"/1600x900";
}
public interface Callback{
void onSuccess();
void onFailure();
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/KeyboardUtils.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.utils;
import android.content.Context;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
/**
* @author Dylan Cai
*/
public class KeyboardUtils {
public static void showKeyboard( EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(editText, InputMethodManager.RESULT_SHOWN);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
}
public static void hideKeyboard(EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
}
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/ToolbarUtils.java
================================================
/*
* Copyright (c) 2019. Dylan Cai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dylanc.loadingstateview.sample.java.utils;
import android.app.Activity;
import android.view.MenuItem;
import android.view.View;
import com.dylanc.loadingstateview.LoadingStateView;
import com.dylanc.loadingstateview.sample.java.delegate.CustomHeaderViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.ScrollingDecorViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.ToolbarViewDelegate;
import com.dylanc.loadingstateview.sample.java.delegate.NavIconType;
import kotlin.jvm.functions.Function1;
/**
* @author Dylan Cai
*/
@SuppressWarnings("UnusedReturnValue")
public class ToolbarUtils {
public static LoadingStateView setToolbar(Activity activity, String title, NavIconType type) {
return setToolbar(activity, title, type, 0, null);
}
public static LoadingStateView setToolbar(Activity activity, String title, NavIconType type, int menuId,
Function1<? super MenuItem, Boolean> onMenuItemClick) {
LoadingStateView loadingStateView = new LoadingStateView(activity);
loadingStateView.setHeaders(new ToolbarViewDelegate(title, type, menuId, onMenuItemClick));
return loadingStateView;
}
public static LoadingStateView setCustomToolbar(Activity activity, View.OnClickListener onMessageClick,
int firstDrawableId, View.OnClickListener onFirstBtnClick,
int secondDrawableId, View.OnClickListener onSecondBtnClick) {
LoadingStateView loadingStateView = new LoadingStateView(activity);
loadingStateView.setHeaders(new CustomHeaderViewDelegate(onMessageClick,
firstDrawableId, onFirstBtnClick, secondDrawableId, onSecondBtnClick));
return loadingStateView;
}
public static LoadingStateView setScrollingToolbar(Activity activity, String title) {
LoadingStateView loadingStateView = new LoadingStateView(activity);
loadingStateView.setDecorView(new ScrollingDecorViewDelegate(title));
return loadingStateView;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingDrawable.java
================================================
package com.dylanc.loadingstateview.sample.java.widget;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
public class LoadingDrawable extends Drawable implements Animatable {
private final LoadingRenderer mLoadingRender;
LoadingDrawable(LoadingRenderer loadingRender) {
this.mLoadingRender = loadingRender;
Callback mCallback = new Callback() {
@Override
public void invalidateDrawable(@NonNull Drawable d) {
invalidateSelf();
}
@Override
public void scheduleDrawable(@NonNull Drawable d, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
public void unscheduleDrawable(@NonNull Drawable d, @NonNull Runnable what) {
unscheduleSelf(what);
}
};
this.mLoadingRender.setCallback(mCallback);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
this.mLoadingRender.setBounds(bounds);
}
@Override
public void draw(@NonNull Canvas canvas) {
if (!getBounds().isEmpty()) {
this.mLoadingRender.draw(canvas);
}
}
@Override
public void setAlpha(int alpha) {
this.mLoadingRender.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
this.mLoadingRender.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void start() {
this.mLoadingRender.start();
}
@Override
public void stop() {
this.mLoadingRender.stop();
}
@Override
public boolean isRunning() {
return this.mLoadingRender.isRunning();
}
@Override
public int getIntrinsicHeight() {
return (int) this.mLoadingRender.mHeight;
}
@Override
public int getIntrinsicWidth() {
return (int) this.mLoadingRender.mWidth;
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingRenderer.java
================================================
package com.dylanc.loadingstateview.sample.java.widget;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import com.dylanc.loadingstateview.sample.java.utils.DensityUtils;
public abstract class LoadingRenderer {
private static final long ANIMATION_DURATION = 1333;
private static final float DEFAULT_SIZE = 56.0f;
private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener
= animation -> {
computeRender((float) animation.getAnimatedValue());
invalidateSelf();
};
/**
* Whenever {@link LoadingDrawable} boundary changes mBounds will be updated.
* More details you can see {@link LoadingDrawable#onBoundsChange(Rect)}
*/
@SuppressWarnings("WeakerAccess")
protected final Rect mBounds = new Rect();
private Drawable.Callback mCallback;
private ValueAnimator mRenderAnimator;
protected long mDuration;
protected float mWidth;
protected float mHeight;
public LoadingRenderer(Context context) {
initParams(context);
setupAnimators();
}
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
protected void draw(Canvas canvas, Rect bounds) {
}
@SuppressWarnings({"WeakerAccess", "deprecation"})
protected void draw(Canvas canvas) {
draw(canvas, mBounds);
}
protected abstract void computeRender(float renderProgress);
protected abstract void setAlpha(int alpha);
protected abstract void setColorFilter(ColorFilter cf);
protected abstract void reset();
protected void addRenderListener(Animator.AnimatorListener animatorListener) {
mRenderAnimator.addListener(animatorListener);
}
void start() {
reset();
mRenderAnimator.addUpdateListener(mAnimatorUpdateListener);
mRenderAnimator.setRepeatCount(ValueAnimator.INFINITE);
mRenderAnimator.setDuration(mDuration);
mRenderAnimator.start();
}
void stop() {
// if I just call mRenderAnimator.end(),
// it will always call the method onAnimationUpdate(ValueAnimator animation)
// why ? if you know why please send email to me (dinus_developer@163.com)
mRenderAnimator.removeUpdateListener(mAnimatorUpdateListener);
mRenderAnimator.setRepeatCount(0);
mRenderAnimator.setDuration(0);
mRenderAnimator.end();
}
boolean isRunning() {
return mRenderAnimator.isRunning();
}
void setCallback(Drawable.Callback callback) {
this.mCallback = callback;
}
void setBounds(Rect bounds) {
mBounds.set(bounds);
}
private void initParams(Context context) {
mWidth = DensityUtils.dip2px(context, DEFAULT_SIZE);
mHeight = DensityUtils.dip2px(context, DEFAULT_SIZE);
mDuration = ANIMATION_DURATION;
}
@SuppressLint("WrongConstant")
private void setupAnimators() {
mRenderAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mRenderAnimator.setRepeatCount(Animation.INFINITE);
mRenderAnimator.setRepeatMode(Animation.RESTART);
mRenderAnimator.setDuration(mDuration);
//fuck you! the default interpolator is AccelerateDecelerateInterpolator
mRenderAnimator.setInterpolator(new LinearInterpolator());
mRenderAnimator.addUpdateListener(mAnimatorUpdateListener);
}
private void invalidateSelf() {
mCallback.invalidateDrawable(null);
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingRendererFactory.java
================================================
package com.dylanc.loadingstateview.sample.java.widget;
import android.content.Context;
import android.util.SparseArray;
import com.dylanc.loadingstateview.sample.java.widget.renderer.ElectricFanLoadingRenderer;
import java.lang.reflect.Constructor;
final class LoadingRendererFactory {
private static final SparseArray<Class<? extends LoadingRenderer>> LOADING_RENDERERS = new SparseArray<>();
static {
LOADING_RENDERERS.put(9, ElectricFanLoadingRenderer.class);
}
private LoadingRendererFactory() {
}
static LoadingRenderer createLoadingRenderer(Context context, int loadingRendererId) throws Exception {
Class<?> loadingRendererClazz = LOADING_RENDERERS.get(loadingRendererId);
Constructor<?>[] constructors = loadingRendererClazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].equals(Context.class)) {
constructor.setAccessible(true);
return (LoadingRenderer) constructor.newInstance(context);
}
}
throw new InstantiationException();
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingView.java
================================================
package com.dylanc.loadingstateview.sample.java.widget;
import android.content.Context;
import android.content.res.TypedArray;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import com.dylanc.loadingstateview.sample.java.R;
public class LoadingView extends AppCompatImageView {
private LoadingDrawable mLoadingDrawable;
public LoadingView(Context context) {
super(context);
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
}
private void initAttrs(Context context, AttributeSet attrs) {
try {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);
int loadingRendererId = ta.getInt(R.styleable.LoadingView_loading_renderer, 0);
LoadingRenderer loadingRenderer = LoadingRendererFactory.createLoadingRenderer(context, loadingRendererId);
setLoadingRenderer(loadingRenderer);
ta.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setLoadingRenderer(LoadingRenderer loadingRenderer) {
mLoadingDrawable = new LoadingDrawable(loadingRenderer);
setImageDrawable(mLoadingDrawable);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
startAnimation();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopAnimation();
}
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
if (visible) {
startAnimation();
} else {
stopAnimation();
}
}
private void startAnimation() {
if (mLoadingDrawable != null) {
mLoadingDrawable.start();
}
}
private void stopAnimation() {
if (mLoadingDrawable != null) {
mLoadingDrawable.stop();
}
}
}
================================================
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/renderer/ElectricFanLoadingRenderer.java
================================================
package com.dylanc.loadingstateview.sample.java.widget.renderer;
import android.animation.*;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.Drawable;
import androidx.annotation.IntDef;
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import com.dylanc.loadingstateview.sample.java.R;
import com.dylanc.loadingstateview.sample.java.utils.DensityUtils;
import com.dylanc.loadingstateview.sample.java.widget.LoadingRenderer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ElectricFanLoadingRenderer extends LoadingRenderer {
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator FASTOUTLINEARIN_INTERPOLATOR = new FastOutLinearInInterpolator();
private static final Interpolator[] INTERPOLATORS = new Interpolator[]{LINEAR_INTERPOLATOR,
DECELERATE_INTERPOLATOR, ACCELERATE_INTERPOLATOR, FASTOUTLINEARIN_INTERPOLATOR, MATERIAL_INTERPOLATOR};
private static final List<LeafHolder> mLeafHolders = new ArrayList<>();
private static final Random mRandom = new Random();
static final int MODE_NORMAL = 0;
static final int MODE_LEAF_COUNT = 1;
@SuppressWarnings("WeakerAccess")
@IntDef({MODE_NORMAL, MODE_LEAF_COUNT})
@Retention(RetentionPolicy.SOURCE)
public @interface MODE {
}
private static final String PERCENTAGE_100 = "100%";
private static final long ANIMATION_DURATION = 7333;
private static final int LEAF_COUNT = 28;
private static final int DEGREE_180 = 180;
private static final int DEGREE_360 = 360;
private static final int FULL_GROUP_ROTATION = (int) (5.25f * DEGREE_360);
private static final int DEFAULT_PROGRESS_COLOR = 0xfffca72e;
private static final int DEFAULT_PROGRESS_BGCOLOR = 0xfffcd49f;
private static final int DEFAULT_ELECTRIC_FAN_BGCOLOR = 0xfffccc59;
private static final int DEFAULT_ELECTRIC_FAN_OUTLINE_COLOR = Color.WHITE;
private static final float DEFAULT_WIDTH = 182.0f;
private static final float DEFAULT_HEIGHT = 65.0f;
private static final float DEFAULT_TEXT_SIZE = 11.0f;
private static final float DEFAULT_STROKE_WIDTH = 2.0f;
private static final float DEFAULT_STROKE_INTERVAL = .2f;
private static final float DEFAULT_CENTER_RADIUS = 16.0f;
private static final float DEFAULT_PROGRESS_CENTER_RADIUS = 11.0f;
private static final float DEFAULT_LEAF_FLY_DURATION_FACTOR = 0.1f;
private static final float LEAF_CREATE_DURATION_INTERVAL = 1.0f / LEAF_COUNT;
private static final float DECELERATE_DURATION_PERCENTAGE = 0.4f;
private static final float ACCELERATE_DURATION_PERCENTAGE = 0.6f;
private final Paint mPaint = new Paint();
private final RectF mTempBounds = new RectF();
private final RectF mCurrentProgressBounds = new RectF();
private float mTextSize;
private float mStrokeXInset;
private float mStrokeYInset;
private float mProgressCenterRadius;
private float mScale;
private float mRotation;
private float mProgress;
private float mNextLeafCreateThreshold;
private int mProgressColor;
private int mProgressBgColor;
private int mElectricFanBgColor;
private int mElectricFanOutlineColor;
private float mStrokeWidth;
private float mCenterRadius;
@MODE
private int mMode;
private int mCurrentLeafCount;
private Drawable mLeafDrawable;
private Drawable mLoadingDrawable;
private Drawable mElectricFanDrawable;
private ElectricFanLoadingRenderer(Context context) {
super(context);
init(context);
setupPaint();
Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animator) {
super.onAnimationRepeat(animator);
reset();
}
};
addRenderListener(mAnimatorListener);
}
private void init(Context context) {
mMode = MODE_NORMAL;
mWidth = DensityUtils.dip2px(context, DEFAULT_WIDTH);
mHeight = DensityUtils.dip2px(context, DEFAULT_HEIGHT);
mTextSize = DensityUtils.dip2px(context, DEFAULT_TEXT_SIZE);
mStrokeWidth = DensityUtils.dip2px(context, DEFAULT_STROKE_WIDTH);
mCenterRadius = DensityUtils.dip2px(context, DEFAULT_CENTER_RADIUS);
mProgressCenterRadius = DensityUtils.dip2px(context, DEFAULT_PROGRESS_CENTER_RADIUS);
mProgressColor = DEFAULT_PROGRESS_COLOR;
mProgressBgColor = DEFAULT_PROGRESS_BGCOLOR;
mElectricFanBgColor = DEFAULT_ELECTRIC_FAN_BGCOLOR;
mElectricFanOutlineColor = DEFAULT_ELECTRIC_FAN_OUTLINE_COLOR;
mLeafDrawable = context.getResources().getDrawable(R.drawable.ic_leaf);
mLoadingDrawable = context.getResources().getDrawable(R.drawable.ic_loading);
mElectricFanDrawable = context.getResources().getDrawable(R.drawable.ic_eletric_fan);
mDuration = ANIMATION_DURATION;
setInsets((int) mWidth, (int) mHeight);
}
private void setupPaint() {
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@SuppressWarnings("deprecation")
@Override
protected void draw(Canvas canvas, Rect bounds) {
int saveCount = canvas.save();
RectF arcBounds = mTempBounds;
arcBounds.set(bounds);
arcBounds.inset(mStrokeXInset, mStrokeYInset);
mCurrentProgressBounds.set(arcBounds.left, arcBounds.bottom - 2 * mCenterRadius,
arcBounds.right, arcBounds.bottom);
//draw loading drawable
mLoadingDrawable.setBounds((int) arcBounds.centerX() - mLoadingDrawable.getIntrinsicWidth() / 2,
0,
(int) arcBounds.centerX() + mLoadingDrawable.getIntrinsicWidth() / 2,
mLoadingDrawable.getIntrinsicHeight());
mLoadingDrawable.draw(canvas);
//draw progress background
float progressInset = mCenterRadius - mProgressCenterRadius;
RectF progressRect = new RectF(mCurrentProgressBounds);
//sub DEFAULT_STROKE_INTERVAL, otherwise will have a interval between progress background and progress outline
progressRect.inset(progressInset - DEFAULT_STROKE_INTERVAL, progressInset - DEFAULT_STROKE_INTERVAL);
mPaint.setColor(mProgressBgColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(progressRect, mProgressCenterRadius, mProgressCenterRadius, mPaint);
//draw progress
mPaint.setColor(mProgressColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(createProgressPath(mProgress, mProgressCenterRadius, progressRect), mPaint);
//draw leaves
for (int i = 0; i < mLeafHolders.size(); i++) {
int leafSaveCount = canvas.save();
LeafHolder leafHolder = mLeafHolders.get(i);
Rect leafBounds = leafHolder.mLeafRect;
canvas.rotate(leafHolder.mLeafRotation, leafBounds.centerX(), leafBounds.centerY());
mLeafDrawable.setBounds(leafBounds);
mLeafDrawable.draw(canvas);
canvas.restoreToCount(leafSaveCount);
}
//draw progress background outline,
//after drawing the leaves and then draw the outline of the progress background can
//prevent the leaves from flying to the outside
RectF progressOutlineRect = new RectF(mCurrentProgressBounds);
float progressOutlineStrokeInset = (mCenterRadius - mProgressCenterRadius) / 2.0f;
progressOutlineRect.inset(progressOutlineStrokeInset, progressOutlineStrokeInset);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mProgressBgColor);
mPaint.setStrokeWidth(mCenterRadius - mProgressCenterRadius);
canvas.drawRoundRect(progressOutlineRect, mCenterRadius, mCenterRadius, mPaint);
//draw electric fan outline
float electricFanCenterX = arcBounds.right - mCenterRadius;
float electricFanCenterY = arcBounds.bottom - mCenterRadius;
mPaint.setColor(mElectricFanOutlineColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
canvas.drawCircle(arcBounds.right - mCenterRadius, arcBounds.bottom - mCenterRadius,
mCenterRadius - mStrokeWidth / 2.0f, mPaint);
//draw electric background
mPaint.setColor(mElectricFanBgColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(arcBounds.right - mCenterRadius, arcBounds.bottom - mCenterRadius,
mCenterRadius - mStrokeWidth + DEFAULT_STROKE_INTERVAL, mPaint);
//draw electric fan
int rotateSaveCount = canvas.save();
canvas.rotate(mRotation, electricFanCenterX, electricFanCenterY);
mElectricFanDrawable.setBounds((int) (electricFanCenterX - mElectricFanDrawable.getIntrinsicWidth() / 2 * mScale),
(int) (electricFanCenterY - mElectricFanDrawable.getIntrinsicHeight() / 2 * mScale),
(int) (electricFanCenterX + mElectricFanDrawable.getIntrinsicWidth() / 2 * mScale),
(int) (electricFanCenterY + mElectricFanDrawable.getIntrinsicHeight() / 2 * mScale));
mElectricFanDrawable.draw(canvas);
canvas.restoreToCount(rotateSaveCount);
//draw 100% text
if (mScale < 1.0f) {
mPaint.setTextSize(mTextSize * (1 - mScale));
mPaint.setColor(mElectricFanOutlineColor);
Rect textRect = new Rect();
mPaint.getTextBounds(PERCENTAGE_100, 0, PERCENTAGE_100.length(), textRect);
canvas.drawText(PERCENTAGE_100, electricFanCenterX - textRect.width() / 2.0f,
electricFanCenterY + textRect.height() / 2.0f, mPaint);
}
canvas.restoreToCount(saveCount);
}
private Path createProgressPath(float progress, float circleRadius, RectF progressRect) {
RectF arcProgressRect = new RectF(progressRect.left, progressRect.top, progressRect.left + circleRadius * 2, progressRect.bottom);
RectF rectProgressRect = null;
float progressWidth = progress * progressRect.width();
float progressModeWidth = mMode == MODE_LEAF_COUNT ?
(float) mCurrentLeafCount / (float) LEAF_COUNT * progressRect.width() : progress * progressRect.width();
float swipeAngle = DEGREE_180;
//the left half circle of the progressbar
if (progressModeWidth < circleRadius) {
swipeAngle = progressModeWidth / circleRadius * DEGREE_180;
}
//the center rect of the progressbar
if (progressModeWidth < progressRect.width() - circleRadius && progressModeWidth >= circleRadius) {
rectProgressRect = new RectF(progressRect.left + circleRadius, progressRect.top, progressRect.left + progressModeWidth, progressRect.bottom);
}
//the right half circle of the progressbar
if (progressWidth >= progressRect.width() - circleRadius) {
rectProgressRect = new RectF(progressRect.left + circleRadius, progressRect.top, progressRect.right - circleRadius, progressRect.bottom);
mScale = (progressRect.width() - progressWidth) / circleRadius;
}
//the left of the right half circle
if (progressWidth < progressRect.width() - circleRadius) {
mRotation = (progressWidth / (progressRect.width() - circleRadius)) * FULL_GROUP_ROTATION % DEGREE_360;
RectF leafRect = new RectF(progressRect.left + progressWidth, progressRect.top, progressRect.right - circleRadius, progressRect.bottom);
addLeaf(progress, leafRect);
}
Path path = new Path();
path.addArc(arcProgressRect, DEGREE_180 - swipeAngle / 2, swipeAngle);
if (rectProgressRect != null) {
path.addRect(rectProgressRect, Path.Direction.CW);
}
return path;
}
@Override
protected void computeRender(float renderProgress) {
if (renderProgress < DECELERATE_DURATION_PERCENTAGE) {
mProgress = DECELERATE_INTERPOLATOR.getInterpolation(renderProgress / DECELERATE_DURATION_PERCENTAGE) * DECELERATE_DURATION_PERCENTAGE;
} else {
mProgress = ACCELERATE_INTERPOLATOR.getInterpolation((renderProgress - DECELERATE_DURATION_PERCENTAGE) / ACCELERATE_DURATION_PERCENTAGE) * ACCELERATE_DURATION_PERCENTAGE + DECELERATE_DURATION_PERCENTAGE;
}
}
@Override
protected void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
protected void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
protected void reset() {
mScale = 1.0f;
mCurrentLeafCount = 0;
mNextLeafCreateThreshold = 0.0f;
mLeafHolders.clear();
}
private void setInsets(int width, int height) {
final float minEdge = (float) Math.min(width, height);
float insetXs;
if (mCenterRadius <= 0 || minEdge < 0) {
insetXs = (float) Math.ceil(mCenterRadius / 2.0f);
} else {
insetXs = mCenterRadius;
}
mStrokeYInset = (float) Math.ceil(mCenterRadius / 2.0f);
mStrokeXInset = insetXs;
}
private void addLeaf(float progress, RectF leafFlyRect) {
if (progress < mNextLeafCreateThreshold) {
return;
}
mNextLeafCreateThreshold += LEAF_CREATE_DURATION_INTERVAL;
LeafHolder leafHolder = new LeafHolder();
mLeafHolders.add(leafHolder);
Animator leafAnimator = getAnimator(leafHolder, leafFlyRect, progress);
leafAnimator.addListener(new AnimEndListener(leafHolder));
leafAnimator.start();
}
private Animator getAnimator(LeafHolder target, RectF leafFlyRect, float progress) {
ValueAnimator bezierValueAnimator = getBezierValueAnimator(target, leafFlyRect, progress);
AnimatorSet finalSet = new AnimatorSet();
finalSet.playSequentially(bezierValueAnimator);
finalSet.setInterpolator(INTERPOLATORS[mRandom.nextInt(INTERPOLATORS.length)]);
finalSet.setTarget(target);
return finalSet;
}
private ValueAnimator getBezierValueAnimator(LeafHolder target, RectF leafFlyRect, float progress) {
BezierEvaluator evaluator = new BezierEvaluator(getPoint1(leafFlyRect), getPoint2(leafFlyRect));
int leafFlyStartY = (int) (mCurrentProgressBounds.bottom - mLeafDrawable.getIntrinsicHeight());
int leafFlyRange = (int) (mCurrentProgressBounds.height() - mLeafDrawable.getIntrinsicHeight());
int startPointY = leafFlyStartY - mRandom.nextInt(leafFlyRange);
int endPointY = leafFlyStartY - mRandom.nextInt(leafFlyRange);
ValueAnimator animator = ValueAnimator.ofObject(evaluator,
new PointF((int) (leafFlyRect.right - mLeafDrawable.getIntrinsicWidth()), startPointY),
new PointF(leafFlyRect.left, endPointY));
animator.addUpdateListener(new BezierListener(target));
animator.setTarget(target);
animator.setDuration((long) ((mRandom.nextInt(300) + mDuration * DEFAULT_LEAF_FLY_DURATION_FACTOR) * (1.0f - progress)));
return animator;
}
//get the pointF which belong to the right half side
private PointF getPoint1(RectF leafFlyRect) {
PointF point = new PointF();
point.x = leafFlyRect.right - mRandom.nextInt((int) (leafFlyRect.width() / 2));
point.y = (int) (leafFlyRect.bottom - mRandom.nextInt((int) leafFlyRect.height()));
return point;
}
//get the pointF which belong to the left half side
private PointF getPoint2(RectF leafFlyRect) {
PointF point = new PointF();
point.x = leafFlyRect.left + mRandom.nextInt((int) (leafFlyRect.width() / 2));
point.y = (int) (leafFlyRect.bottom - mRandom.nextInt((int) leafFlyRect.height()));
return point;
}
private static class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF point1;
private PointF point2;
BezierEvaluator(PointF point1, PointF point2) {
this.point1 = point1;
this.point2 = point2;
}
//Third-order Bezier curve formula: B(t) = point0 * (1-t)^3 + 3 * point1 * t * (1-t)^2 + 3 * point2 * t^2 * (1-t) + point3 * t^3
@Override
public PointF evaluate(float fraction, PointF point0, PointF point3) {
float tLeft = 1.0f - fraction;
float x = (float) (point0.x * Math.pow(tLeft, 3) + 3 * point1.x * fraction * Math.pow(tLeft, 2) + 3 * point2.x * Math.pow(fraction, 2) * tLeft + point3.x * Math.pow(fraction, 3));
float y = (float) (point0.y * Math.pow(tLeft, 3) + 3 * point1.y * fraction * Math.pow(tLeft, 2) + 3 * point2.y * Math.pow(fraction, 2) * tLeft + point3.y * Math.pow(fraction, 3));
return new PointF(x, y);
}
}
private class BezierListener implements ValueAnimator.AnimatorUpdateListener {
private LeafHolder target;
BezierListener(LeafHolder target) {
this.target = target;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF point = (PointF) animation.getAnimatedValue();
target.mLeafRect.set((int) point.x, (int) point.y,
(int) (point.x + mLeafDrawable.getIntrinsicWidth()), (int) (point.y + mLeafDrawable.getIntrinsicHeight()));
target.mLeafRotation = target.mMaxRotation * animation.getAnimatedFraction();
}
}
private class AnimEndListener extends AnimatorListenerAdapter {
private LeafHolder target;
AnimEndListener(LeafHolder target) {
this.target = target;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mLeafHolders.remove(target);
mCurrentLeafCount++;
}
}
private static class LeafHolder {
Rect mLeafRect = new Rect();
float mLeafRotation = 0.0f;
float mMaxRotation = mRandom.nextInt(120);
}
}
================================================
FILE: sample-java/src/main/res/drawable/bg_reload_btn.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp"/>
<stroke android:width="1dp"
android:color="#bbb"/>
</shape>
================================================
FILE: sample-java/src/main/res/drawable/bg_search.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="18dp"/>
<solid android:color="#eee"/>
</shape>
================================================
FILE: sample-java/src/main/res/drawable/ic_baseline_favorite_24.xml
================================================
<vector android:height="24dp" android:tint="#666666"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>
gitextract_ifim0oyk/ ├── .gitignore ├── LICENSE ├── README.md ├── README_ZH.md ├── build.gradle ├── docs/ │ ├── .nojekyll │ ├── README.md │ ├── _sidebar.md │ ├── index.html │ └── zh/ │ ├── basic-usage.md │ ├── delegate.md │ ├── migration-guide.md │ ├── q&a.md │ └── viewbinding.md ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── loadingstateview/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── dylanc/ │ │ └── loadingstateview/ │ │ └── LoadingStateView.kt │ └── res/ │ └── values/ │ └── strings.xml ├── loadingstateview-ktx/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── dylanc/ │ └── loadingstateview/ │ ├── BaseToolbarViewDelegate.kt │ ├── Decorative.kt │ ├── LoadingState.kt │ ├── LoadingStateDelegate.kt │ └── ToolbarConfig.kt ├── sample-java/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ ├── release/ │ │ ├── app-release.apk │ │ └── output.json │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── dylanc/ │ │ └── loadingstateview/ │ │ └── sample/ │ │ └── java/ │ │ └── ExampleInstrumentedTest.kt │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── dylanc/ │ │ │ └── loadingstateview/ │ │ │ └── sample/ │ │ │ └── java/ │ │ │ ├── App.java │ │ │ ├── animation/ │ │ │ │ └── FadeAnimatable.java │ │ │ ├── base/ │ │ │ │ └── BaseActivity.java │ │ │ ├── delegate/ │ │ │ │ ├── BottomEditorDecorViewDelegate.java │ │ │ │ ├── CoolLoadingViewDelegate.java │ │ │ │ ├── CustomHeaderViewDelegate.java │ │ │ │ ├── EmptyViewDelegate.java │ │ │ │ ├── ErrorViewDelegate.java │ │ │ │ ├── LoadingViewDelegate.java │ │ │ │ ├── NavIconType.java │ │ │ │ ├── NothingViewDelegate.java │ │ │ │ ├── PlaceholderViewDelegate.java │ │ │ │ ├── ScrollingDecorViewDelegate.java │ │ │ │ ├── SearchHeaderViewDelegate.java │ │ │ │ ├── TimeoutViewDelegate.java │ │ │ │ └── ToolbarViewDelegate.java │ │ │ ├── ui/ │ │ │ │ ├── ActErrorActivity.java │ │ │ │ ├── BottomEditorActivity.java │ │ │ │ ├── CustomHeaderActivity.java │ │ │ │ ├── FragmentEmptyActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MultipleHeaderActivity.java │ │ │ │ ├── RecyclerViewActivity.java │ │ │ │ ├── ScrollingToolbarActivity.java │ │ │ │ ├── ViewPagerActivity.java │ │ │ │ ├── ViewPlaceholderActivity.java │ │ │ │ └── fragment/ │ │ │ │ ├── EmptyFragment.java │ │ │ │ ├── SimpleFragment.java │ │ │ │ └── TimeoutFragment.java │ │ │ ├── utils/ │ │ │ │ ├── DensityUtils.java │ │ │ │ ├── HttpUtils.java │ │ │ │ ├── KeyboardUtils.java │ │ │ │ └── ToolbarUtils.java │ │ │ └── widget/ │ │ │ ├── LoadingDrawable.java │ │ │ ├── LoadingRenderer.java │ │ │ ├── LoadingRendererFactory.java │ │ │ ├── LoadingView.java │ │ │ └── renderer/ │ │ │ └── ElectricFanLoadingRenderer.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── bg_reload_btn.xml │ │ │ ├── bg_search.xml │ │ │ ├── ic_baseline_favorite_24.xml │ │ │ ├── ic_baseline_message_24.xml │ │ │ ├── ic_baseline_photo_camera_24.xml │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_fragment.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_recycler_view.xml │ │ │ ├── activity_scrolling.xml │ │ │ ├── activity_tab_layout.xml │ │ │ ├── activity_view.xml │ │ │ ├── activity_view_pager.xml │ │ │ ├── layout_bottom_editor.xml │ │ │ ├── layout_content.xml │ │ │ ├── layout_cool_loading.xml │ │ │ ├── layout_custom_header.xml │ │ │ ├── layout_empty.xml │ │ │ ├── layout_error.xml │ │ │ ├── layout_loading.xml │ │ │ ├── layout_placeholder.xml │ │ │ ├── layout_scrolling_toolbar.xml │ │ │ ├── layout_search_header.xml │ │ │ ├── layout_timeout.xml │ │ │ ├── layout_toolbar.xml │ │ │ └── recycler_item_image.xml │ │ ├── menu/ │ │ │ └── menu_about.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── dylanc/ │ └── loadingstateview/ │ └── sample/ │ └── java/ │ └── ExampleUnitTest.kt ├── sample-kotlin/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── dylanc/ │ │ └── loadingstateview/ │ │ └── sample/ │ │ └── kotlin/ │ │ └── ExampleInstrumentedTest.kt │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── dylanc/ │ │ │ └── loadingstateview/ │ │ │ └── sample/ │ │ │ └── kotlin/ │ │ │ ├── App.kt │ │ │ ├── base/ │ │ │ │ ├── BaseActivity.kt │ │ │ │ ├── BaseBindingActivity.kt │ │ │ │ ├── BaseBindingFragment.kt │ │ │ │ └── BaseFragment.kt │ │ │ ├── delegate/ │ │ │ │ ├── EmptyViewDelegate.kt │ │ │ │ ├── ErrorViewDelegate.kt │ │ │ │ ├── FadeAnimatable.kt │ │ │ │ ├── LoadingViewDelegate.kt │ │ │ │ ├── ScrollingDecorViewDelegate.kt │ │ │ │ └── ToolbarViewDelegate.kt │ │ │ └── ui/ │ │ │ └── MainActivity.kt │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── bg_reload_btn.xml │ │ │ ├── ic_add.xml │ │ │ ├── ic_arrow_back_ios.xml │ │ │ ├── ic_launcher_background.xml │ │ │ └── ic_refresh.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_main.xml │ │ │ ├── layout_empty.xml │ │ │ ├── layout_error.xml │ │ │ ├── layout_loading.xml │ │ │ ├── layout_scrolling_toolbar.xml │ │ │ └── layout_toolbar.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ └── values-night/ │ │ └── themes.xml │ └── test/ │ └── java/ │ └── com/ │ └── dylanc/ │ └── loadingstateview/ │ └── sample/ │ └── kotlin/ │ └── ExampleUnitTest.kt └── settings.gradle
SYMBOL INDEX (207 symbols across 38 files)
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/App.java
class App (line 30) | public class App extends Application {
method onCreate (line 31) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/animation/FadeAnimatable.java
class FadeAnimatable (line 15) | public class FadeAnimatable implements LoadingStateView.Animatable {
method FadeAnimatable (line 20) | public FadeAnimatable() {
method FadeAnimatable (line 24) | public FadeAnimatable(long duration) {
method toggleViewsAnimation (line 28) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/base/BaseActivity.java
class BaseActivity (line 36) | @SuppressWarnings("unused")
method setContentView (line 41) | @Override
method setContentView (line 46) | public void setContentView(int layoutResID, @IdRes int contentViewId) {
method setToolbar (line 58) | public void setToolbar(String title) {
method setToolbar (line 62) | public void setToolbar(String title, NavIconType type) {
method setToolbar (line 66) | public void setToolbar(String title, NavIconType type, int menuId) {
method showLoadingView (line 70) | public void showLoadingView() {
method showContentView (line 74) | public void showContentView() {
method showErrorView (line 78) | public void showErrorView() {
method showEmptyView (line 82) | public void showEmptyView() {
method showCustomView (line 86) | public void showCustomView(Object viewType) {
method onReload (line 90) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/BottomEditorDecorViewDelegate.java
class BottomEditorDecorViewDelegate (line 37) | public class BottomEditorDecorViewDelegate extends LoadingStateView.Deco...
method BottomEditorDecorViewDelegate (line 40) | public BottomEditorDecorViewDelegate(OnSendListener onSendListener) {
method onCreateDecorView (line 44) | @NotNull
method getContentParent (line 60) | @NotNull
type OnSendListener (line 66) | public interface OnSendListener {
method onSend (line 67) | void onSend(String content);
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/CoolLoadingViewDelegate.java
class CoolLoadingViewDelegate (line 31) | public class CoolLoadingViewDelegate extends LoadingStateView.ViewDelega...
method CoolLoadingViewDelegate (line 33) | public CoolLoadingViewDelegate() {
method onCreateView (line 37) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/CustomHeaderViewDelegate.java
class CustomHeaderViewDelegate (line 36) | public class CustomHeaderViewDelegate extends LoadingStateView.ViewDeleg...
method CustomHeaderViewDelegate (line 44) | public CustomHeaderViewDelegate(View.OnClickListener onMessageClickLis...
method onCreateView (line 55) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/EmptyViewDelegate.java
class EmptyViewDelegate (line 31) | public class EmptyViewDelegate extends LoadingStateView.ViewDelegate {
method EmptyViewDelegate (line 33) | public EmptyViewDelegate() {
method onCreateView (line 37) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ErrorViewDelegate.java
class ErrorViewDelegate (line 32) | public class ErrorViewDelegate extends LoadingStateView.ViewDelegate {
method ErrorViewDelegate (line 34) | public ErrorViewDelegate() {
method onCreateView (line 38) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/LoadingViewDelegate.java
class LoadingViewDelegate (line 32) | public class LoadingViewDelegate extends LoadingStateView.ViewDelegate {
method LoadingViewDelegate (line 36) | public LoadingViewDelegate() {
method onCreateView (line 40) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/NavIconType.java
type NavIconType (line 22) | public enum NavIconType {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/NothingViewDelegate.java
class NothingViewDelegate (line 30) | public class NothingViewDelegate extends LoadingStateView.ViewDelegate {
method NothingViewDelegate (line 32) | public NothingViewDelegate() {
method onCreateView (line 36) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/PlaceholderViewDelegate.java
class PlaceholderViewDelegate (line 31) | public class PlaceholderViewDelegate extends LoadingStateView.ViewDelega...
method PlaceholderViewDelegate (line 33) | public PlaceholderViewDelegate() {
method onCreateView (line 37) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ScrollingDecorViewDelegate.java
class ScrollingDecorViewDelegate (line 38) | public class ScrollingDecorViewDelegate extends LoadingStateView.DecorVi...
method ScrollingDecorViewDelegate (line 41) | public ScrollingDecorViewDelegate(String title) {
method onCreateDecorView (line 45) | @NotNull
method getContentParent (line 60) | @NotNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/SearchHeaderViewDelegate.java
class SearchHeaderViewDelegate (line 35) | public class SearchHeaderViewDelegate extends LoadingStateView.ViewDeleg...
method SearchHeaderViewDelegate (line 40) | public SearchHeaderViewDelegate(OnSearchListener onSearchListener) {
method onCreateView (line 45) | @NonNull
type OnSearchListener (line 63) | public interface OnSearchListener {
method onSearch (line 64) | void onSearch(String keyword);
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/TimeoutViewDelegate.java
class TimeoutViewDelegate (line 31) | public class TimeoutViewDelegate extends LoadingStateView.ViewDelegate {
method TimeoutViewDelegate (line 34) | public TimeoutViewDelegate() {
method onCreateView (line 38) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ToolbarViewDelegate.java
class ToolbarViewDelegate (line 41) | public class ToolbarViewDelegate extends LoadingStateView.ViewDelegate {
method ToolbarViewDelegate (line 48) | public ToolbarViewDelegate(String title, NavIconType type) {
method ToolbarViewDelegate (line 52) | public ToolbarViewDelegate(String title, NavIconType type, int menuId,...
method onCreateView (line 60) | @NonNull
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ActErrorActivity.java
class ActErrorActivity (line 34) | public class ActErrorActivity extends AppCompatActivity implements OnRel...
method onCreate (line 38) | @Override
method loadData (line 47) | private void loadData() {
method onReload (line 62) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/BottomEditorActivity.java
class BottomEditorActivity (line 36) | public class BottomEditorActivity extends AppCompatActivity implements B...
method onCreate (line 40) | @Override
method onSend (line 50) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/CustomHeaderActivity.java
class CustomHeaderActivity (line 39) | public class CustomHeaderActivity extends AppCompatActivity {
method onCreate (line 41) | @Override
method onMessageClick (line 56) | private void onMessageClick(View view) {
method onFirstBtnClick (line 60) | private void onFirstBtnClick(View view) {
method onSecondBtnClick (line 64) | private void onSecondBtnClick(View view) {
class TabPagerAdapter (line 68) | public static class TabPagerAdapter extends FragmentPagerAdapter {
method TabPagerAdapter (line 70) | public TabPagerAdapter(FragmentManager fm) {
method getItem (line 74) | @NonNull
method getCount (line 80) | @Override
method getPageTitle (line 85) | @Nullable
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/FragmentEmptyActivity.java
class FragmentEmptyActivity (line 33) | public class FragmentEmptyActivity extends AppCompatActivity {
method onCreate (line 35) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MainActivity.java
class MainActivity (line 35) | public class MainActivity extends BaseActivity {
method onCreate (line 37) | @Override
method onOptionsItemSelected (line 44) | @Override
method onViewClicked (line 54) | public void onViewClicked(View view) {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MultipleHeaderActivity.java
class MultipleHeaderActivity (line 36) | public class MultipleHeaderActivity extends AppCompatActivity implements...
method onCreate (line 40) | @Override
method onSearch (line 53) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/RecyclerViewActivity.java
class RecyclerViewActivity (line 49) | public class RecyclerViewActivity extends AppCompatActivity {
method onCreate (line 51) | @Override
class ImageAdapter (line 63) | public static class ImageAdapter extends RecyclerView.Adapter<ImageAda...
method onCreateViewHolder (line 65) | @NonNull
method onBindViewHolder (line 72) | @Override
method getItemCount (line 77) | @Override
class ViewHolder (line 82) | static class ViewHolder extends RecyclerView.ViewHolder{
method ViewHolder (line 88) | ViewHolder(@NonNull View itemView) {
method showImage (line 96) | void showImage(String url) {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ScrollingToolbarActivity.java
class ScrollingToolbarActivity (line 35) | public class ScrollingToolbarActivity extends AppCompatActivity {
method onCreate (line 39) | @Override
method loadData (line 49) | private void loadData() {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPagerActivity.java
class ViewPagerActivity (line 38) | public class ViewPagerActivity extends AppCompatActivity {
method onCreate (line 40) | @Override
class TabPagerAdapter (line 52) | public static class TabPagerAdapter extends FragmentPagerAdapter {
method TabPagerAdapter (line 54) | public TabPagerAdapter(FragmentManager fm) {
method getItem (line 58) | @NonNull
method getCount (line 64) | @Override
method getPageTitle (line 69) | @Nullable
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPlaceholderActivity.java
class ViewPlaceholderActivity (line 36) | public class ViewPlaceholderActivity extends AppCompatActivity {
method onCreate (line 40) | @Override
method loadData (line 53) | private void loadData() {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/EmptyFragment.java
class EmptyFragment (line 36) | @SuppressWarnings("FieldCanBeLocal")
method onCreateView (line 42) | @Nullable
method onViewCreated (line 51) | @Override
method loadData (line 57) | private void loadData() {
method onReload (line 72) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/SimpleFragment.java
class SimpleFragment (line 33) | public class SimpleFragment extends Fragment {
method onCreateView (line 35) | @Nullable
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/TimeoutFragment.java
class TimeoutFragment (line 37) | @SuppressWarnings("FieldCanBeLocal")
method onCreateView (line 44) | @Nullable
method onViewCreated (line 54) | @Override
method loadData (line 60) | private void loadData() {
method onReload (line 75) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/DensityUtils.java
class DensityUtils (line 24) | public class DensityUtils {
method dip2px (line 26) | public static float dip2px(Context context, float dpValue) {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/HttpUtils.java
class HttpUtils (line 26) | public class HttpUtils {
method requestSuccess (line 31) | public static void requestSuccess(final Callback callback){
method requestFailure (line 42) | public static void requestFailure(final Callback callback){
method getRandomImageUrl (line 50) | public static String getRandomImageUrl(){
type Callback (line 55) | public interface Callback{
method onSuccess (line 56) | void onSuccess();
method onFailure (line 58) | void onFailure();
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/KeyboardUtils.java
class KeyboardUtils (line 26) | public class KeyboardUtils {
method showKeyboard (line 28) | public static void showKeyboard( EditText editText) {
method hideKeyboard (line 38) | public static void hideKeyboard(EditText editText) {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/ToolbarUtils.java
class ToolbarUtils (line 34) | @SuppressWarnings("UnusedReturnValue")
method setToolbar (line 36) | public static LoadingStateView setToolbar(Activity activity, String ti...
method setToolbar (line 40) | public static LoadingStateView setToolbar(Activity activity, String ti...
method setCustomToolbar (line 47) | public static LoadingStateView setCustomToolbar(Activity activity, Vie...
method setScrollingToolbar (line 56) | public static LoadingStateView setScrollingToolbar(Activity activity, ...
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingDrawable.java
class LoadingDrawable (line 12) | public class LoadingDrawable extends Drawable implements Animatable {
method LoadingDrawable (line 15) | LoadingDrawable(LoadingRenderer loadingRender) {
method onBoundsChange (line 36) | @Override
method draw (line 42) | @Override
method setAlpha (line 49) | @Override
method setColorFilter (line 54) | @Override
method getOpacity (line 59) | @Override
method start (line 64) | @Override
method stop (line 69) | @Override
method isRunning (line 74) | @Override
method getIntrinsicHeight (line 79) | @Override
method getIntrinsicWidth (line 84) | @Override
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingRenderer.java
class LoadingRenderer (line 15) | public abstract class LoadingRenderer {
method LoadingRenderer (line 40) | public LoadingRenderer(Context context) {
method draw (line 45) | @SuppressWarnings("DeprecatedIsStillUsed")
method draw (line 50) | @SuppressWarnings({"WeakerAccess", "deprecation"})
method computeRender (line 55) | protected abstract void computeRender(float renderProgress);
method setAlpha (line 57) | protected abstract void setAlpha(int alpha);
method setColorFilter (line 59) | protected abstract void setColorFilter(ColorFilter cf);
method reset (line 61) | protected abstract void reset();
method addRenderListener (line 63) | protected void addRenderListener(Animator.AnimatorListener animatorLis...
method start (line 67) | void start() {
method stop (line 76) | void stop() {
method isRunning (line 87) | boolean isRunning() {
method setCallback (line 91) | void setCallback(Drawable.Callback callback) {
method setBounds (line 95) | void setBounds(Rect bounds) {
method initParams (line 99) | private void initParams(Context context) {
method setupAnimators (line 106) | @SuppressLint("WrongConstant")
method invalidateSelf (line 117) | private void invalidateSelf() {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingRendererFactory.java
class LoadingRendererFactory (line 9) | final class LoadingRendererFactory {
method LoadingRendererFactory (line 16) | private LoadingRendererFactory() {
method createLoadingRenderer (line 19) | static LoadingRenderer createLoadingRenderer(Context context, int load...
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingView.java
class LoadingView (line 12) | public class LoadingView extends AppCompatImageView {
method LoadingView (line 15) | public LoadingView(Context context) {
method LoadingView (line 19) | public LoadingView(Context context, AttributeSet attrs) {
method initAttrs (line 24) | private void initAttrs(Context context, AttributeSet attrs) {
method setLoadingRenderer (line 36) | public void setLoadingRenderer(LoadingRenderer loadingRenderer) {
method onAttachedToWindow (line 41) | @Override
method onDetachedFromWindow (line 47) | @Override
method onVisibilityChanged (line 53) | @Override
method startAnimation (line 65) | private void startAnimation() {
method stopAnimation (line 71) | private void stopAnimation() {
FILE: sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/renderer/ElectricFanLoadingRenderer.java
class ElectricFanLoadingRenderer (line 24) | public class ElectricFanLoadingRenderer extends LoadingRenderer {
method ElectricFanLoadingRenderer (line 104) | private ElectricFanLoadingRenderer(Context context) {
method init (line 118) | private void init(Context context) {
method setupPaint (line 141) | private void setupPaint() {
method draw (line 148) | @SuppressWarnings("deprecation")
method createProgressPath (line 244) | private Path createProgressPath(float progress, float circleRadius, Re...
method computeRender (line 287) | @Override
method setAlpha (line 296) | @Override
method setColorFilter (line 302) | @Override
method reset (line 308) | @Override
method setInsets (line 316) | private void setInsets(int width, int height) {
method addLeaf (line 328) | private void addLeaf(float progress, RectF leafFlyRect) {
method getAnimator (line 341) | private Animator getAnimator(LeafHolder target, RectF leafFlyRect, flo...
method getBezierValueAnimator (line 351) | private ValueAnimator getBezierValueAnimator(LeafHolder target, RectF ...
method getPoint1 (line 372) | private PointF getPoint1(RectF leafFlyRect) {
method getPoint2 (line 380) | private PointF getPoint2(RectF leafFlyRect) {
class BezierEvaluator (line 387) | private static class BezierEvaluator implements TypeEvaluator<PointF> {
method BezierEvaluator (line 392) | BezierEvaluator(PointF point1, PointF point2) {
method evaluate (line 398) | @Override
class BezierListener (line 410) | private class BezierListener implements ValueAnimator.AnimatorUpdateLi...
method BezierListener (line 414) | BezierListener(LeafHolder target) {
method onAnimationUpdate (line 418) | @Override
class AnimEndListener (line 427) | private class AnimEndListener extends AnimatorListenerAdapter {
method AnimEndListener (line 430) | AnimEndListener(LeafHolder target) {
method onAnimationEnd (line 434) | @Override
class LeafHolder (line 442) | private static class LeafHolder {
Condensed preview — 152 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (304K chars).
[
{
"path": ".gitignore",
"chars": 209,
"preview": "*.iml\n.gradle\n.idea\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navE"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 6710,
"preview": "# LoadingStateView\n\nEnglish | [中文](README_ZH.md)\n\n[](ht"
},
{
"path": "README_ZH.md",
"chars": 5703,
"preview": "# LoadingStateView\n\n[English](README.md) | 中文\n\n[](https"
},
{
"path": "build.gradle",
"chars": 1052,
"preview": "buildscript {\n ext.buildConfig = [\n 'versionCode' : 1,\n 'versionName' : \"1.0.0\",\n "
},
{
"path": "docs/.nojekyll",
"chars": 0,
"preview": ""
},
{
"path": "docs/README.md",
"chars": 5600,
"preview": "# LoadingStateView\n\n[](https://www.jitpack.io/#DylanCai"
},
{
"path": "docs/_sidebar.md",
"chars": 154,
"preview": "* [介绍](/)\n* [基础用法](/zh/basic-usage)\n* [Kotlin 委托用法](/zh/delegate)\n* [结合 ViewBinding 使用](/zh/viewbinding)\n* [老版本迁移指南](/zh"
},
{
"path": "docs/index.html",
"chars": 1343,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Document</title>\n <meta http-equiv=\"X-UA-Comp"
},
{
"path": "docs/zh/basic-usage.md",
"chars": 6872,
"preview": "# 基础用法\n\n使用 Kotlin 开发推荐用[委托用法](/zh/delegate)。\n\n## 显示加载中、加载失败等缺省页\n\n第一步,使用 Activity 或 View 创建 `LoadingStateView`,不需要重新加载的话 "
},
{
"path": "docs/zh/delegate.md",
"chars": 9603,
"preview": "# Kotlin 委托用法\n\n## 准备工作\n\n需要修改基类,只需简单的两步就可以把本库的功能集成到基类,并且不会影响到已有的代码,只是给基类扩展了新的方法。\n\n注意添加的依赖需要是 `loadingstateview-ktx`:\n\n```"
},
{
"path": "docs/zh/migration-guide.md",
"chars": 278,
"preview": "# 老版本升级指南\n\n`4.0.1` 版本对用法进行了优化,移除了 `ViewHolder`,使用起来更加简单,不过就意味着不能兼容之前的代码。建议根据[文档](/zh/basic-usage)改成新的用法,通常是封装在基类,要改的不多,`"
},
{
"path": "docs/zh/q&a.md",
"chars": 342,
"preview": "# Q&A\n\n## 为什么不分成标题栏库和缺省页库?\n\n个人深思熟虑了非常久才决定写成一个库,有以下考虑:\n\n- 支持给内容和缺省页添加头部,所以具有管理标题栏的应用场景,感觉没什么不妥。\n- 大多数情况下标题栏和缺省页关联性很强,因为缺省"
},
{
"path": "docs/zh/viewbinding.md",
"chars": 3544,
"preview": "# 结合 ViewBinding 使用\n\n## 基础用法\n\n如果要同时使用 `LoadingStateView` 和 `ViewBinding`,需要先得到对应的 `ViewBinding` 实例,再用其根视图去创建 `LoadingSta"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 232,
"preview": "#Mon Nov 18 20:32:14 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradle.properties",
"chars": 870,
"preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
},
{
"path": "gradlew",
"chars": 5296,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "loadingstateview/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "loadingstateview/build.gradle",
"chars": 834,
"preview": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\n\nandroid {\n compileSdkVersion buildConfig.compileS"
},
{
"path": "loadingstateview/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "loadingstateview/src/main/AndroidManifest.xml",
"chars": 51,
"preview": "<manifest package=\"com.dylanc.loadingstateview\" />\n"
},
{
"path": "loadingstateview/src/main/java/com/dylanc/loadingstateview/LoadingStateView.kt",
"chars": 9245,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "loadingstateview/src/main/res/values/strings.xml",
"chars": 68,
"preview": "<resources>\n <string name=\"app_name\">Library</string>\n</resources>\n"
},
{
"path": "loadingstateview-ktx/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "loadingstateview-ktx/build.gradle",
"chars": 836,
"preview": "plugins {\n id 'com.android.library'\n id 'org.jetbrains.kotlin.android'\n}\n\nandroid {\n compileSdkVersion buildCon"
},
{
"path": "loadingstateview-ktx/consumer-rules.pro",
"chars": 0,
"preview": ""
},
{
"path": "loadingstateview-ktx/proguard-rules.pro",
"chars": 750,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "loadingstateview-ktx/src/main/AndroidManifest.xml",
"chars": 165,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"c"
},
{
"path": "loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/BaseToolbarViewDelegate.kt",
"chars": 1144,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/Decorative.kt",
"chars": 782,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/LoadingState.kt",
"chars": 3166,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/LoadingStateDelegate.kt",
"chars": 4306,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "loadingstateview-ktx/src/main/java/com/dylanc/loadingstateview/ToolbarConfig.kt",
"chars": 2545,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "sample-java/build.gradle",
"chars": 1496,
"preview": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n compileSdkVersion buildConfig.comp"
},
{
"path": "sample-java/proguard-rules.pro",
"chars": 825,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "sample-java/release/output.json",
"chars": 249,
"preview": "[{\"outputType\":{\"type\":\"APK\"},\"apkData\":{\"type\":\"MAIN\",\"splits\":[],\"versionCode\":2,\"versionName\":\"1.0.1\",\"enabled\":true,"
},
{
"path": "sample-java/src/androidTest/java/com/dylanc/loadingstateview/sample/java/ExampleInstrumentedTest.kt",
"chars": 698,
"preview": "package com.dylanc.loadingstateview.sample.java\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androi"
},
{
"path": "sample-java/src/main/AndroidManifest.xml",
"chars": 1434,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tool"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/App.java",
"chars": 1385,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/animation/FadeAnimatable.java",
"chars": 1174,
"preview": "package com.dylanc.loadingstateview.sample.java.animation;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNul"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/base/BaseActivity.java",
"chars": 2613,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/BottomEditorDecorViewDelegate.java",
"chars": 2178,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/CoolLoadingViewDelegate.java",
"chars": 1307,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/CustomHeaderViewDelegate.java",
"chars": 2995,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/EmptyViewDelegate.java",
"chars": 1286,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ErrorViewDelegate.java",
"chars": 1475,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/LoadingViewDelegate.java",
"chars": 1517,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/NavIconType.java",
"chars": 727,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/NothingViewDelegate.java",
"chars": 1215,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/PlaceholderViewDelegate.java",
"chars": 1306,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ScrollingDecorViewDelegate.java",
"chars": 2100,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/SearchHeaderViewDelegate.java",
"chars": 2147,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/TimeoutViewDelegate.java",
"chars": 1469,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/delegate/ToolbarViewDelegate.java",
"chars": 2767,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ActErrorActivity.java",
"chars": 2290,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/BottomEditorActivity.java",
"chars": 2275,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/CustomHeaderActivity.java",
"chars": 2868,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/FragmentEmptyActivity.java",
"chars": 1591,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MainActivity.java",
"chars": 2867,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/MultipleHeaderActivity.java",
"chars": 2371,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/RecyclerViewActivity.java",
"chars": 4348,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ScrollingToolbarActivity.java",
"chars": 2069,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPagerActivity.java",
"chars": 2367,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/ViewPlaceholderActivity.java",
"chars": 2116,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/EmptyFragment.java",
"chars": 2611,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/SimpleFragment.java",
"chars": 1254,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/ui/fragment/TimeoutFragment.java",
"chars": 2836,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/DensityUtils.java",
"chars": 911,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/HttpUtils.java",
"chars": 1447,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/KeyboardUtils.java",
"chars": 1510,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/utils/ToolbarUtils.java",
"chars": 2668,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingDrawable.java",
"chars": 2248,
"preview": "package com.dylanc.loadingstateview.sample.java.widget;\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFi"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingRenderer.java",
"chars": 3797,
"preview": "package com.dylanc.loadingstateview.sample.java.widget;\n\nimport android.animation.Animator;\nimport android.animation.Val"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingRendererFactory.java",
"chars": 1236,
"preview": "package com.dylanc.loadingstateview.sample.java.widget;\n\nimport android.content.Context;\nimport android.util.SparseArray"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/LoadingView.java",
"chars": 2239,
"preview": "package com.dylanc.loadingstateview.sample.java.widget;\n\nimport android.content.Context;\nimport android.content.res.Type"
},
{
"path": "sample-java/src/main/java/com/dylanc/loadingstateview/sample/java/widget/renderer/ElectricFanLoadingRenderer.java",
"chars": 19008,
"preview": "package com.dylanc.loadingstateview.sample.java.widget.renderer;\n\nimport android.animation.*;\nimport android.content.Con"
},
{
"path": "sample-java/src/main/res/drawable/bg_reload_btn.xml",
"chars": 211,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <corners and"
},
{
"path": "sample-java/src/main/res/drawable/bg_search.xml",
"chars": 181,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <corners and"
},
{
"path": "sample-java/src/main/res/drawable/ic_baseline_favorite_24.xml",
"chars": 459,
"preview": "<vector android:height=\"24dp\" android:tint=\"#666666\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "sample-java/src/main/res/drawable/ic_baseline_message_24.xml",
"chars": 432,
"preview": "<vector android:height=\"24dp\" android:tint=\"#666666\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "sample-java/src/main/res/drawable/ic_baseline_photo_camera_24.xml",
"chars": 590,
"preview": "<vector android:height=\"24dp\" android:tint=\"#666666\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "sample-java/src/main/res/drawable/ic_launcher_background.xml",
"chars": 4887,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample-java/src/main/res/drawable-v24/ic_launcher_foreground.xml",
"chars": 1969,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:aapt=\"http://schemas.android.com/aapt\"\n"
},
{
"path": "sample-java/src/main/res/layout/activity_fragment.xml",
"chars": 315,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://schema"
},
{
"path": "sample-java/src/main/res/layout/activity_main.xml",
"chars": 4901,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-java/src/main/res/layout/activity_recycler_view.xml",
"chars": 447,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://schema"
},
{
"path": "sample-java/src/main/res/layout/activity_scrolling.xml",
"chars": 523,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/"
},
{
"path": "sample-java/src/main/res/layout/activity_tab_layout.xml",
"chars": 634,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n androi"
},
{
"path": "sample-java/src/main/res/layout/activity_view.xml",
"chars": 2502,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://schema"
},
{
"path": "sample-java/src/main/res/layout/activity_view_pager.xml",
"chars": 244,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.viewpager.widget.ViewPager xmlns:android=\"http://schemas.android.com/ap"
},
{
"path": "sample-java/src/main/res/layout/layout_bottom_editor.xml",
"chars": 1285,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n androi"
},
{
"path": "sample-java/src/main/res/layout/layout_content.xml",
"chars": 869,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-java/src/main/res/layout/layout_cool_loading.xml",
"chars": 460,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns"
},
{
"path": "sample-java/src/main/res/layout/layout_custom_header.xml",
"chars": 3009,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-java/src/main/res/layout/layout_empty.xml",
"chars": 1467,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-java/src/main/res/layout/layout_error.xml",
"chars": 2150,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://schema"
},
{
"path": "sample-java/src/main/res/layout/layout_loading.xml",
"chars": 906,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-java/src/main/res/layout/layout_placeholder.xml",
"chars": 1863,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://schema"
},
{
"path": "sample-java/src/main/res/layout/layout_scrolling_toolbar.xml",
"chars": 1156,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schema"
},
{
"path": "sample-java/src/main/res/layout/layout_search_header.xml",
"chars": 1190,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-java/src/main/res/layout/layout_timeout.xml",
"chars": 1945,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-java/src/main/res/layout/layout_toolbar.xml",
"chars": 478,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.Toolbar xmlns:android=\"http://schemas.android.com/apk/"
},
{
"path": "sample-java/src/main/res/layout/recycler_item_image.xml",
"chars": 491,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andro"
},
{
"path": "sample-java/src/main/res/menu/menu_about.xml",
"chars": 269,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:app=\"htt"
},
{
"path": "sample-java/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample-java/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample-java/src/main/res/values/attrs.xml",
"chars": 1327,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <declare-styleable name=\"LoadingView\">\n <attr name=\"loadin"
},
{
"path": "sample-java/src/main/res/values/colors.xml",
"chars": 525,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#fff</color>\n <color name=\"colorPri"
},
{
"path": "sample-java/src/main/res/values/strings.xml",
"chars": 5733,
"preview": "<resources>\n <string name=\"app_name\">LoadingStateView</string>\n <string name=\"app_introduce\">A highly expandable Andro"
},
{
"path": "sample-java/src/main/res/values/styles.xml",
"chars": 566,
"preview": "<resources>\n\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n <item name=\"colorPrimary\">@color/c"
},
{
"path": "sample-java/src/test/java/com/dylanc/loadingstateview/sample/java/ExampleUnitTest.kt",
"chars": 364,
"preview": "package com.dylanc.loadingstateview.sample.java\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local "
},
{
"path": "sample-kotlin/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "sample-kotlin/build.gradle",
"chars": 1539,
"preview": "plugins {\n id 'com.android.application'\n id 'org.jetbrains.kotlin.android'\n}\n\nandroid {\n compileSdkVersion buil"
},
{
"path": "sample-kotlin/proguard-rules.pro",
"chars": 750,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "sample-kotlin/src/androidTest/java/com/dylanc/loadingstateview/sample/kotlin/ExampleInstrumentedTest.kt",
"chars": 691,
"preview": "package com.dylanc.loadingstateview.sample.kotlin\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport andr"
},
{
"path": "sample-kotlin/src/main/AndroidManifest.xml",
"chars": 746,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"c"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/App.kt",
"chars": 809,
"preview": "package com.dylanc.loadingstateview.sample.kotlin\n\nimport android.app.Application\nimport com.dylanc.loadingstateview.Loa"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/base/BaseActivity.kt",
"chars": 1112,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/base/BaseBindingActivity.kt",
"chars": 1315,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/base/BaseBindingFragment.kt",
"chars": 1369,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/base/BaseFragment.kt",
"chars": 1173,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/delegate/EmptyViewDelegate.kt",
"chars": 1136,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/delegate/ErrorViewDelegate.kt",
"chars": 1259,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/delegate/FadeAnimatable.kt",
"chars": 865,
"preview": "package com.dylanc.loadingstateview.sample.kotlin.delegate\n\nimport android.view.View\nimport com.dylanc.loadingstateview."
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/delegate/LoadingViewDelegate.kt",
"chars": 1394,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/delegate/ScrollingDecorViewDelegate.kt",
"chars": 1565,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/delegate/ToolbarViewDelegate.kt",
"chars": 3093,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/java/com/dylanc/loadingstateview/sample/kotlin/ui/MainActivity.kt",
"chars": 1814,
"preview": "/*\n * Copyright (c) 2019. Dylan Cai\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "sample-kotlin/src/main/res/drawable/bg_reload_btn.xml",
"chars": 211,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <corners and"
},
{
"path": "sample-kotlin/src/main/res/drawable/ic_add.xml",
"chars": 315,
"preview": "<vector android:height=\"24dp\" android:tint=\"#333333\"\n android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n andr"
},
{
"path": "sample-kotlin/src/main/res/drawable/ic_arrow_back_ios.xml",
"chars": 346,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n andr"
},
{
"path": "sample-kotlin/src/main/res/drawable/ic_launcher_background.xml",
"chars": 5012,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:widt"
},
{
"path": "sample-kotlin/src/main/res/drawable/ic_refresh.xml",
"chars": 509,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"48dp\"\n android:height=\"48dp\"\n andr"
},
{
"path": "sample-kotlin/src/main/res/drawable-v24/ic_launcher_foreground.xml",
"chars": 1550,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:aapt=\"http://schemas.android.com/aapt\"\n andr"
},
{
"path": "sample-kotlin/src/main/res/layout/activity_main.xml",
"chars": 791,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-kotlin/src/main/res/layout/layout_empty.xml",
"chars": 1467,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-kotlin/src/main/res/layout/layout_error.xml",
"chars": 2099,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-kotlin/src/main/res/layout/layout_loading.xml",
"chars": 742,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-kotlin/src/main/res/layout/layout_scrolling_toolbar.xml",
"chars": 1194,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schema"
},
{
"path": "sample-kotlin/src/main/res/layout/layout_toolbar.xml",
"chars": 2562,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "sample-kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 268,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <bac"
},
{
"path": "sample-kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 268,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <bac"
},
{
"path": "sample-kotlin/src/main/res/values/colors.xml",
"chars": 675,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"purple_200\">#FFBB86FC</color>\n <color name=\"purple_50"
},
{
"path": "sample-kotlin/src/main/res/values/strings.xml",
"chars": 76,
"preview": "<resources>\n <string name=\"app_name\">LoadingStateView</string>\n</resources>"
},
{
"path": "sample-kotlin/src/main/res/values/themes.xml",
"chars": 995,
"preview": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n <!-- Base application theme. -->\n <style name=\"Theme.Loadi"
},
{
"path": "sample-kotlin/src/main/res/values-night/themes.xml",
"chars": 788,
"preview": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n <!-- Base application theme. -->\n <style name=\"Theme.Loadi"
},
{
"path": "sample-kotlin/src/test/java/com/dylanc/loadingstateview/sample/kotlin/ExampleUnitTest.kt",
"chars": 355,
"preview": "package com.dylanc.loadingstateview.sample.kotlin\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example loca"
},
{
"path": "settings.gradle",
"chars": 146,
"preview": "rootProject.name = \"LoadingStateView\"\ninclude ':loadingstateview'\ninclude ':loadingstateview-ktx'\ninclude ':sample-java'"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the DylanCaiCoding/LoadingStateView GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 152 files (264.8 KB), approximately 72.4k tokens, and a symbol index with 207 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.