master a91fa3f3142b cached
152 files
264.8 KB
72.4k tokens
207 symbols
1 requests
Download .txt
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/v/DylanCaiCoding/LoadingStateView.svg)](https://www.jitpack.io/#DylanCaiCoding/LoadingLoadingStateView) [![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](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

[![QR code](docs/img/app_download_qr_code.png)](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) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|                 ![](docs/gif/activity_error.gif)                  |                ![](docs/gif/view_placeholder.gif)                 |                ![](docs/gif/viewpager_timeout.gif)                |              ![](docs/gif/recyclerview_loading.gif)               |

| [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) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|              ![](docs/gif/special_header_custom.gif)              |             ![](docs/gif/multiple_header_search.gif)              |             ![](docs/gif/special_decor_scrolling.gif)             |               ![](docs/gif/bottom_decor_editor.gif)               |

## 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/v/DylanCaiCoding/LoadingStateView.svg)](https://www.jitpack.io/#DylanCaiCoding/LoadingLoadingStateView) [![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](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
- 支持显示自定义视图
- 支持添加多个头部控件
- 支持设置重新请求数据的事件
- 支持动态更新视图样式
- 可结合绝大部分第三方控件使用

## 示例

点击或者扫描二维码下载

[![QR code](docs/img/app_download_qr_code.png)](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) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|                 ![](docs/gif/activity_error.gif)                  |                ![](docs/gif/view_placeholder.gif)                 |                ![](docs/gif/viewpager_timeout.gif)                |              ![](docs/gif/recyclerview_loading.gif)               |

动态添加标题栏或装饰容器:

| [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) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|              ![](docs/gif/special_header_custom.gif)              |             ![](docs/gif/multiple_header_search.gif)              |             ![](docs/gif/special_decor_scrolling.gif)             |               ![](docs/gif/bottom_decor_editor.gif)               |

## 用法

: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/v/DylanCaiCoding/LoadingStateView.svg)](https://www.jitpack.io/#DylanCaiCoding/LoadingLoadingStateView) 
[![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://github.com/DylanCaiCoding/LoadingStateView/blob/master/LICENSE)
[![GitHub Repo stars](https://img.shields.io/github/stars/DylanCaiCoding/LoadingStateView?style=social)](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
- 支持显示自定义视图
- 支持添加多个头部控件
- 支持设置重新请求数据的事件
- 支持动态更新视图样式
- 可结合绝大部分第三方控件使用

## 示例

点击或者扫描二维码下载

[![QR code](img/app_download_qr_code.png)](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) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|                 ![](gif/activity_error.gif)                  |                ![](gif/view_placeholder.gif)                 |                ![](gif/viewpager_timeout.gif)                |              ![](gif/recyclerview_loading.gif)               |

动态添加标题栏或装饰容器:

| [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) |
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|              ![](gif/special_header_custom.gif)              |             ![](gif/multiple_header_search.gif)              |             ![](gif/special_decor_scrolling.gif)             |               ![](gif/bottom_decor_editor.gif)               |

## 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**

![img.png](../img/base_activity_code.png)

<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**

![img.png](../img/base_fragment_code.png)

<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**

![img.png](../img/base_binding_activity_code.png)

<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**

![img.png](../img/base_binding_fragment_code.png)

<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>


Download .txt
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
Download .txt
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[![](https://www.jitpack.io/v/DylanCaiCoding/LoadingStateView.svg)](ht"
  },
  {
    "path": "README_ZH.md",
    "chars": 5703,
    "preview": "# LoadingStateView\n\n[English](README.md) | 中文\n\n[![](https://www.jitpack.io/v/DylanCaiCoding/LoadingStateView.svg)](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/v/DylanCaiCoding/LoadingStateView.svg)](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.

Copied to clipboard!