├── .gitignore ├── LICENSE ├── README.md ├── README_EN.md ├── apk └── app-release.apk ├── app ├── .gitignore ├── build.gradle ├── key_debug ├── key_release ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── bakumon │ │ └── statuslayoutmanager │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── me │ │ │ └── bakumon │ │ │ └── statuslayoutmanager │ │ │ ├── MainActivity.java │ │ │ ├── SimpleData.java │ │ │ └── SimpleRecyclerViewAdapter.java │ └── res │ │ ├── drawable-nodpi │ │ ├── jay_1.jpg │ │ ├── jay_2.jpg │ │ ├── jay_3.jpg │ │ ├── jay_4.jpg │ │ └── jay_5.jpg │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xxhdpi │ │ ├── ic_customer.png │ │ └── ic_network_error.png │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── selector_btn_blue.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── item_list.xml │ │ ├── layout_custome.xml │ │ ├── layout_empty.xml │ │ ├── layout_error.xml │ │ └── layout_loading.xml │ │ ├── menu │ │ └── status.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-en │ │ └── strings.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── me │ └── bakumon │ └── statuslayoutmanager │ └── ExampleUnitTest.java ├── build.gradle ├── gif └── status_layout_manager.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── bakumon │ │ └── statuslayoutmanager │ │ └── library │ │ ├── DefaultOnStatusChildClickListener.java │ │ ├── OnStatusChildClickListener.java │ │ ├── ReplaceLayoutHelper.java │ │ └── StatusLayoutManager.java │ └── res │ ├── drawable-xxhdpi │ ├── status_layout_manager_ic_empty.png │ └── status_layout_manager_ic_error.png │ ├── layout │ ├── layout_status_layout_manager_empty.xml │ ├── layout_status_layout_manager_error.xml │ └── layout_status_layout_manager_loading.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values-zh-rHK │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ └── values │ ├── colors.xml │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 食梦兽 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StatusLayoutManager 2 | 3 | [![Release](https://jitpack.io/v/Bakumon/StatusLayoutManager.svg)](https://jitpack.io/#Bakumon/StatusLayoutManager) 4 | [![API](https://img.shields.io/badge/API-11%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=11) 5 | [![Gitads.io](https://img.shields.io/badge/GitAds-valid-brightgreen)](https://images.gitads.io/StatusLayoutManager) 6 | 7 | StatusLayoutManager is being sponsored by the following tool; please help to support us by taking a look and signing up to a free trial 8 | 9 | 中文版 | [English Version](https://github.com/Bakumon/StatusLayoutManager/blob/master/README_EN.md) 10 | 11 | 切换不同的数据状态布局,包含加载中、空数据和出错状态。 12 | 13 | ## 特征 14 | 15 | 1. 不会增加布局层数 16 | 2. 提供一套可配置的默认状态布局 17 | 3. 布局懒加载 18 | 4. 重试按钮点击事件回调 19 | 5. 可自由扩展其他状态布局 20 | 21 | ## 预览 22 | 23 | 下载 [demo](https://github.com/Bakumon/StatusLayoutManager/raw/master/apk/app-release.apk) 体验 24 | 25 | ![status_layout_manager.gif](https://github.com/Bakumon/StatusLayoutManager/raw/master/gif/status_layout_manager.gif) 26 | 27 | ## 下载 28 | 29 | 1. 在项目的 `build.gradle` 中添加: 30 | 31 | ``` 32 | allprojects { 33 | repositories { 34 | ... 35 | maven { url 'https://jitpack.io' } 36 | } 37 | } 38 | ``` 39 | 40 | 2. 添加依赖 41 | 42 | ``` 43 | dependencies { 44 | implementation 'com.github.Bakumon:StatusLayoutManager:1.0.4' 45 | } 46 | ``` 47 | 48 | ## 使用 49 | 50 | ### 快速使用 51 | 52 | 使用 `StatusLayoutManager#Builder` 创建 `StatusLayoutManager` 对象,`Builder` 构造函数的参数是在切换布局时被替换的 View。 53 | 54 | ```java 55 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 56 | .build(); 57 | ``` 58 | 59 | 在合适的时机显示对应的状态布局: 60 | 61 | ```java 62 | // 加载中 63 | statusLayoutManager.showLoadingLayout(); 64 | // 空数据 65 | statusLayoutManager.showEmptyLayout(); 66 | // 加载失败 67 | statusLayoutManager.showErrorLayout(); 68 | // 加载成功,显示原布局 69 | statusLayoutManager.showSuccessLayout(); 70 | ``` 71 | 72 | 以上可以满足大多数场景。 73 | 74 | ### 配置默认布局 75 | 76 | 以下 API 提供修改默认布局的方法,具体说明见注释。 77 | 78 | ```java 79 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 80 | 81 | // 设置默认加载中布局的提示文本 82 | .setDefaultLoadingText("l拼命加载中...") 83 | 84 | // 设置默认空数据布局的提示文本 85 | .setDefaultEmptyText("空白了,哈哈哈哈") 86 | // 设置默认空数据布局的图片 87 | .setDefaultEmptyImg(R.mipmap.ic_launcher) 88 | // 设置默认空数据布局重试按钮的文本 89 | .setDefaultEmptyRetryText("retry") 90 | // 设置默认空数据布局重试按钮的文本颜色 91 | .setDefaultEmptyRetryTextColor(getResources().getColor(R.color.colorAccent)) 92 | // 设置默认空数据布局重试按钮是否显示 93 | .setDefaultEmptyRetryVisible(false) 94 | 95 | // 设置默认出错布局的提示文本 96 | .setDefaultErrorText(R.string.app_name) 97 | // 设置默认出错布局的图片 98 | .setDefaultErrorImg(R.mipmap.ic_launcher) 99 | // 设置默认出错布局重试按钮的文本 100 | .setDefaultErrorRetryText("重试一波") 101 | // 设置默认出错布局重试按钮的文本颜色 102 | .setDefaultErrorRetryTextColor(getResources().getColor(R.color.colorPrimaryDark)) 103 | // 设置默认出错布局重试按钮是否显示 104 | .setDefaultErrorRetryVisible(true) 105 | 106 | // 设置默认布局背景,包括加载中、空数据和出错布局 107 | .setDefaultLayoutsBackgroundColor(Color.WHITE) 108 | .build(); 109 | ``` 110 | 111 | ### 自定义默认布局 112 | 113 | ```java 114 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 115 | // 设置加载中布局 116 | .setLoadingLayout(inflate(R.layout.layout_loading)) 117 | // 设置空数据布局 118 | .setEmptyLayout(inflate(R.layout.layout_empty)) 119 | // 设置出错布局 120 | .setErrorLayout(inflate(R.layout.layout_error)) 121 | 122 | // 设置加载中布局 123 | .setLoadingLayout(R.layout.layout_loading) 124 | // 设置空数据布局 125 | .setEmptyLayout(R.layout.layout_empty) 126 | // 设置出错布局 127 | .setErrorLayout(R.layout.layout_error) 128 | 129 | // 设置空数据布局重试按钮 ID 130 | .setEmptyRetryID(R.id.tv_empty) 131 | // 设置出错布局重试按钮 ID 132 | .setErrorRetryID(R.id.tv_error) 133 | .build(); 134 | ``` 135 | 136 | ### 显示自定义状态布局 137 | 138 | `statusLayoutManager#showCustomLayout()`有几个重载方法,下面以参数最多的为例介绍: 139 | 140 | ```java 141 | /** 142 | * 显示自定义状态布局 143 | * 144 | * @param customLayoutID 自定义布局 ID 145 | * @param clickViewID 重试按钮 ID 146 | * @return 自定义状态布局 147 | */ 148 | statusLayoutManager.showCustomLayout(R.layout.layout_custome, R.id.tv_customer, R.id.tv_customer1); 149 | ``` 150 | 151 | 其中 `clickViewID` 参数,表示想要添加点击事件的 View 的 id。 152 | 153 | ### 点击监听 154 | 155 | ```java 156 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 157 | 158 | // 设置重试事件监听器 159 | .setOnStatusLayoutClickListener(new OnStatusLayoutClickListener() { 160 | @Override 161 | public void onEmptyChildClick(View view) { 162 | 163 | } 164 | 165 | @Override 166 | public void onErrorChildClick(View view) { 167 | 168 | } 169 | 170 | @Override 171 | public void onCustomerChildClick(View view) { 172 | 173 | } 174 | } 175 | }) 176 | .build(); 177 | ``` 178 | 179 | 也可以使用 `OnStatusLayoutClickListener` 默认的实现类,像下面这样: 180 | 181 | ```java 182 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 183 | // 设置重试事件监听器 184 | .setOnStatusChildClickListener(new DefaultOnStatusChildClickListener() { 185 | @Override 186 | public void onEmptyChildClick(View view) { 187 | 188 | } 189 | 190 | @Override 191 | public void onErrorChildClick(View view) { 192 | 193 | } 194 | }) 195 | .build(); 196 | ``` 197 | 198 | ## 已知问题 199 | 200 | #### 1. StatusLayoutManager#Builder(View view):view 参数的要求 201 | 202 | 由于该库的原理是,首先获取需要被替换的 view 在父控件中的 `LayoutParams`,然后通过调用父控件的 `removeViewAt()` 方法移除原有布局,调用父控件的 `addView()` 方法添加进去新的布局,来达到切换布局的目的。所以要求被替换 `view` 的父控件支持这种方式。 203 | 204 | 目前已知`android.support.v4.widget.SwipeRefreshLayout` 等刷新控件不支持这种方式。建议直接把 `SwipeRefreshLayout` 对象当作要替换的 `view` 传递给 `Builder` 构造函数。 205 | 206 | ## License 207 | 208 | [MIT License](https://github.com/Bakumon/StatusLayoutManager/blob/master/LICENSE) 209 | 210 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # StatusLayoutManager 2 | 3 | [![Release](https://jitpack.io/v/Bakumon/StatusLayoutManager.svg)](https://jitpack.io/#Bakumon/StatusLayoutManager) 4 | [![API](https://img.shields.io/badge/API-11%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=11) 5 | [![Gitads.io](https://img.shields.io/badge/GitAds-valid-brightgreen)](https://images.gitads.io/StatusLayoutManager) 6 | 7 | StatusLayoutManager is being sponsored by the following tool; please help to support us by taking a look and signing up to a free trial 8 | 9 | GitAds 10 | 11 | [中文版](https://github.com/Bakumon/StatusLayoutManager/blob/master/README.md) | English Version 12 | 13 | Switch between different data state layouts, including loading medium and empty data and error status. 14 | 15 | ## Feature 16 | 17 | 1. Does not increase the number of layout layers 18 | 2. Provide a configurable default state layout 19 | 3. Layout lazy loading 20 | 4. Retry button unified callback 21 | 5. Support custom state layout 22 | 23 | ## Demo 24 | 25 | Download [demo](https://github.com/Bakumon/StatusLayoutManager/raw/master/apk/app-release.apk) 26 | 27 | Get it on Google Play 28 | 29 | ![status_layout_manager.gif](https://github.com/Bakumon/StatusLayoutManager/raw/master/gif/status_layout_manager.gif) 30 | 31 | ## Download 32 | 33 | 1. project `build.gradle`: 34 | 35 | ``` 36 | allprojects { 37 | repositories { 38 | ... 39 | maven { url 'https://jitpack.io' } 40 | } 41 | } 42 | ``` 43 | 44 | 2. app `build.gradle`: 45 | 46 | ``` 47 | dependencies { 48 | implementation 'com.github.Bakumon:StatusLayoutManager:1.0.4' 49 | } 50 | ``` 51 | 52 | ## Usage & Examples 53 | 54 | ### Getting started 55 | 56 | Create a `StatusLayoutManager` object with `StatusLayoutManager#Builder`: 57 | 58 | ```java 59 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 60 | .build(); 61 | ``` 62 | 63 | Display the corresponding status layout at the right time: 64 | 65 | ```java 66 | // Loading 67 | statusLayoutManager.showLoadingLayout(); 68 | // Empty data 69 | statusLayoutManager.showEmptyLayout(); 70 | // Error 71 | statusLayoutManager.showErrorLayout(); 72 | // success 73 | statusLayoutManager.showSuccessLayout(); 74 | ``` 75 | 76 | The above can satisfy most scenarios. 77 | 78 | ### Configuring the default layout 79 | 80 | The following APIs provide a way to modify the default layout. 81 | 82 | ```java 83 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 84 | 85 | // Set the prompt text for the layout in the default load 86 | .setDefaultLoadingText("loading...") 87 | 88 | // Set the prompt text for the default empty data layout 89 | .setDefaultEmptyText("Empty") 90 | // Set the image of the default empty data layout 91 | .setDefaultEmptyImg(R.mipmap.ic_launcher) 92 | // Set the text of the default empty data layout retry button 93 | .setDefaultEmptyRetryText("retry") 94 | // Set the text color of the default empty data layout retry button 95 | .setDefaultEmptyRetryTextColor(getResources().getColor(R.color.colorAccent)) 96 | // Set the default empty data layout retry button to display 97 | .setDefaultEmptyRetryVisible(false) 98 | 99 | // Set the prompt text for the default error layout 100 | .setDefaultErrorText(R.string.app_name) 101 | // Set the image of the default error layout 102 | .setDefaultErrorImg(R.mipmap.ic_launcher) 103 | // Set the text of the default error layout retry button 104 | .setDefaultErrorRetryText("Reload") 105 | // Set the text color of the default error layout retry button 106 | .setDefaultErrorRetryTextColor(getResources().getColor(R.color.colorPrimaryDark)) 107 | // Set default error layout retry button to display 108 | .setDefaultErrorRetryVisible(true) 109 | 110 | // Set the default layout background, including loading medium and empty data and error layout 111 | .setDefaultLayoutsBackgroundColor(Color.WHITE) 112 | .build(); 113 | ``` 114 | 115 | ### Custom default layout 116 | 117 | ```java 118 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 119 | // Set the layout in load 120 | .setLoadingLayout(inflate(R.layout.layout_loading)) 121 | // Set empty data layout 122 | .setEmptyLayout(inflate(R.layout.layout_empty)) 123 | // Set the error layout 124 | .setErrorLayout(inflate(R.layout.layout_error)) 125 | 126 | // Set the layout in load 127 | .setLoadingLayout(R.layout.layout_loading) 128 | // Set empty data layout 129 | .setEmptyLayout(R.layout.layout_empty) 130 | // Set the error layout 131 | .setErrorLayout(R.layout.layout_error) 132 | 133 | // Set empty data layout retry button ID 134 | .setEmptyRetryID(R.id.tv_empty) 135 | // Set error layout retry button ID 136 | .setErrorRetryID(R.id.tv_error) 137 | .build(); 138 | ``` 139 | 140 | ### Show custom state layout 141 | 142 | `statusLayoutManager#showCustomLayout()`There are several overloading methods. The following takes the most parameters as an example: 143 | 144 | ```java 145 | /** 146 | * Show custom state layout 147 | * 148 | * @param customLayoutID Custom layout ID 149 | * @param clickViewID Retry button ID 150 | * @return Custom state layout 151 | */ 152 | statusLayoutManager.showCustomLayout(R.layout.layout_custome, R.id.tv_customer, R.id.tv_customer1); 153 | ``` 154 | 155 | The `clickViewID` parameter indicates the id of the View to which you want to add a click event. 156 | 157 | ### onClick listener 158 | 159 | ```java 160 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 161 | 162 | // Set retry event listener 163 | .setOnStatusLayoutClickListener(new OnStatusLayoutClickListener() { 164 | @Override 165 | public void onEmptyChildClick(View view) { 166 | 167 | } 168 | 169 | @Override 170 | public void onErrorChildClick(View view) { 171 | 172 | } 173 | 174 | @Override 175 | public void onCustomerChildClick(View view) { 176 | 177 | } 178 | } 179 | }) 180 | .build(); 181 | ``` 182 | 183 | You can also use the default implementation class of `OnStatusLayoutClickListener` , like this: 184 | 185 | ```java 186 | statusLayoutManager = new StatusLayoutManager.Builder(recyclerView) 187 | // Set retry event listener 188 | .setOnStatusChildClickListener(new DefaultOnStatusChildClickListener() { 189 | @Override 190 | public void onEmptyChildClick(View view) { 191 | 192 | } 193 | 194 | @Override 195 | public void onErrorChildClick(View view) { 196 | 197 | } 198 | }) 199 | .build(); 200 | ``` 201 | 202 | ## Known issue 203 | 204 | #### 1. StatusLayoutManager#Builder(View view):parameter requirements 205 | 206 | If you use `android.support.v4.widget.SwipeRefreshLayout`, it is recommended to pass the `SwipeRefreshLayout` object directly as the `view` to be replaced to the `Builder` constructor instead of its child view. 207 | 208 | ## License 209 | 210 | [MIT License](https://github.com/Bakumon/StatusLayoutManager/blob/master/LICENSE) 211 | 212 | ## Sponsor 213 | 214 | [![Gitads.io](https://i.loli.net/2020/06/17/2SiZPMuoEQzjh1k.png)](https://images.gitads.io/StatusLayoutManager) 215 | -------------------------------------------------------------------------------- /apk/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/apk/app-release.apk -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "me.bakumon.statuslayoutmanager" 7 | minSdkVersion 19 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | signingConfigs { 14 | debug { 15 | storeFile file("key_debug") 16 | storePassword "123456" 17 | keyAlias "key" 18 | keyPassword "123456" 19 | } 20 | release { 21 | storeFile file("key_release") 22 | storePassword project.hasProperty('STOREPASS') ? STOREPASS : '' 23 | keyAlias project.hasProperty('KEYALIAS') ? KEYALIAS : '' 24 | keyPassword project.hasProperty('KEYPASS') ? KEYPASS : '' 25 | } 26 | } 27 | buildTypes { 28 | debug { 29 | signingConfig signingConfigs.debug 30 | } 31 | release { 32 | minifyEnabled false 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | signingConfig signingConfigs.release 35 | } 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation fileTree(include: ['*.jar'], dir: 'libs') 41 | implementation 'com.android.support:appcompat-v7:26.1.0' 42 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 43 | testImplementation 'junit:junit:4.12' 44 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 45 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 46 | compile project(path: ':library') 47 | implementation 'com.android.support:recyclerview-v7:26.1.0' 48 | implementation 'com.github.bumptech.glide:glide:3.7.0' 49 | implementation 'de.hdodenhof:circleimageview:1.3.0' 50 | } 51 | -------------------------------------------------------------------------------- /app/key_debug: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/key_debug -------------------------------------------------------------------------------- /app/key_release: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/key_release -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/me/bakumon/statuslayoutmanager/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package me.bakumon.statuslayoutmanager; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("me.bakumon.statuslayoutmanager", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/me/bakumon/statuslayoutmanager/MainActivity.java: -------------------------------------------------------------------------------- 1 | package me.bakumon.statuslayoutmanager; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.support.annotation.LayoutRes; 6 | import android.support.v4.widget.SwipeRefreshLayout; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.LayoutInflater; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.widget.Toast; 14 | 15 | import me.bakumon.statuslayoutmanager.library.OnStatusChildClickListener; 16 | import me.bakumon.statuslayoutmanager.library.StatusLayoutManager; 17 | 18 | /** 19 | * 示例界面 20 | * 21 | * @author Bakumon https://bakumon.me 22 | * @date 2017/12/22 23 | */ 24 | public class MainActivity extends AppCompatActivity { 25 | 26 | private StatusLayoutManager statusLayoutManager; 27 | private LayoutInflater inflater; 28 | 29 | private RecyclerView recyclerView; 30 | private SwipeRefreshLayout swipeRefresh; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_main); 36 | 37 | swipeRefresh = findViewById(R.id.swipeRefresh); 38 | swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 39 | @Override 40 | public void onRefresh() { 41 | swipeRefresh.setRefreshing(false); 42 | } 43 | }); 44 | recyclerView = findViewById(R.id.rv_content); 45 | 46 | setupStatusLayoutManager(); 47 | 48 | statusLayoutManager.showLoadingLayout(); 49 | getData(1500); 50 | } 51 | 52 | private void setupStatusLayoutManager() { 53 | statusLayoutManager = new StatusLayoutManager.Builder(swipeRefresh) 54 | 55 | // 设置默认布局属性 56 | // .setDefaultEmptyText("空白了,哈哈哈哈") 57 | // .setDefaultEmptyImg(R.mipmap.ic_launcher) 58 | // .setDefaultEmptyClickViewText("retry") 59 | // .setDefaultEmptyClickViewTextColor(getResources().getColor(R.color.colorAccent)) 60 | // .setDefaultEmptyClickViewVisible(false) 61 | // 62 | // .setDefaultErrorText(R.string.app_name) 63 | // .setDefaultErrorImg(R.mipmap.ic_launcher) 64 | // .setDefaultErrorClickViewText("重试一波") 65 | // .setDefaultErrorClickViewTextColor(getResources().getColor(R.color.colorPrimaryDark)) 66 | // .setDefaultErrorClickViewVisible(true) 67 | // 68 | // .setDefaultLayoutsBackgroundColor(Color.WHITE) 69 | 70 | // 自定义布局 71 | // .setLoadingLayout(inflate(R.layout.layout_loading)) 72 | // .setEmptyLayout(inflate(R.layout.layout_empty)) 73 | // .setErrorLayout(inflate(R.layout.layout_error)) 74 | // 75 | // .setLoadingLayout(R.layout.layout_loading) 76 | // .setEmptyLayout(R.layout.layout_empty) 77 | // .setErrorLayout(R.layout.layout_error) 78 | // 79 | // .setEmptyClickViewID(R.id.tv_empty) 80 | // .setErrorClickViewID(R.id.tv_error) 81 | 82 | // 设置重试事件监听器 83 | .setOnStatusChildClickListener(new OnStatusChildClickListener() { 84 | @Override 85 | public void onEmptyChildClick(View view) { 86 | Toast.makeText(MainActivity.this, R.string.reload_empty, Toast.LENGTH_SHORT).show(); 87 | statusLayoutManager.showLoadingLayout(); 88 | getData(1000); 89 | } 90 | 91 | @Override 92 | public void onErrorChildClick(View view) { 93 | Toast.makeText(MainActivity.this, R.string.reload_error, Toast.LENGTH_SHORT).show(); 94 | statusLayoutManager.showLoadingLayout(); 95 | getData(1000); 96 | } 97 | 98 | @Override 99 | public void onCustomerChildClick(View view) { 100 | if (view.getId() == R.id.tv_customer) { 101 | Toast.makeText(MainActivity.this, R.string.request_access, Toast.LENGTH_SHORT).show(); 102 | } else if (view.getId() == R.id.tv_customer1) { 103 | Toast.makeText(MainActivity.this, R.string.switch_account, Toast.LENGTH_SHORT).show(); 104 | } 105 | 106 | } 107 | }) 108 | .build(); 109 | } 110 | 111 | private void getData(final long time) { 112 | getWindow().getDecorView().postDelayed(new Runnable() { 113 | @Override 114 | public void run() { 115 | recyclerView.setAdapter(new SimpleRecyclerViewAdapter()); 116 | statusLayoutManager.showSuccessLayout(); 117 | } 118 | }, time); 119 | } 120 | 121 | private View inflate(@LayoutRes int resource) { 122 | if (inflater == null) { 123 | inflater = LayoutInflater.from(this); 124 | } 125 | return inflater.inflate(resource, null); 126 | } 127 | 128 | @Override 129 | public boolean onCreateOptionsMenu(Menu menu) { 130 | getMenuInflater().inflate(R.menu.status, menu); 131 | return true; 132 | } 133 | 134 | @Override 135 | public boolean onOptionsItemSelected(MenuItem item) { 136 | switch (item.getItemId()) { 137 | case R.id.menu_status_loading: 138 | // 加载中 139 | statusLayoutManager.showLoadingLayout(); 140 | break; 141 | case R.id.menu_status_empty: 142 | // 空数据 143 | statusLayoutManager.showEmptyLayout(); 144 | break; 145 | case R.id.menu_status_error: 146 | // 加载失败 147 | statusLayoutManager.showErrorLayout(); 148 | break; 149 | case R.id.menu_status_success: 150 | // 加载成功,显示原布局 151 | statusLayoutManager.showSuccessLayout(); 152 | break; 153 | case R.id.menu_status_customer: 154 | // 自定义状态布局 155 | statusLayoutManager.showCustomLayout(R.layout.layout_custome, R.id.tv_customer, R.id.tv_customer1); 156 | break; 157 | default: 158 | break; 159 | } 160 | return super.onOptionsItemSelected(item); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/me/bakumon/statuslayoutmanager/SimpleData.java: -------------------------------------------------------------------------------- 1 | package me.bakumon.statuslayoutmanager; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | 7 | /** 8 | * 列表数据提供 9 | * 10 | * @author Bakumon https://bakumon.me 11 | * @date 2017/12/22 12 | */ 13 | public class SimpleData { 14 | 15 | public static class Song { 16 | public Song(String songName, int drawableResID) { 17 | this.songName = songName; 18 | this.drawableResID = drawableResID; 19 | } 20 | 21 | public String songName; 22 | public int drawableResID; 23 | } 24 | 25 | public static List getRandomSonList(int amount) { 26 | ArrayList list = new ArrayList<>(amount); 27 | Random random = new Random(); 28 | while (list.size() < amount) { 29 | list.add(new Song(SONG_NAMES[random.nextInt(SONG_NAMES.length)], 30 | SONG_DRAWABLE[random.nextInt(SONG_DRAWABLE.length)])); 31 | } 32 | return list; 33 | } 34 | 35 | private static final int[] SONG_DRAWABLE = { 36 | R.drawable.jay_1, R.drawable.jay_2, R.drawable.jay_3, R.drawable.jay_4, R.drawable.jay_5 37 | }; 38 | 39 | private static final String[] SONG_NAMES = { 40 | "告白气球", "晴天", "稻香", "七里香", "算什么男人", 41 | "搁浅", "最长的电影", "开不了口", "彩虹", "不能说的秘密", "甜甜的", 42 | "一路向北", "简单爱", "回到过去", "说好的幸福呢", "安静", 43 | "夜曲", "枫", "退后", "青花瓷", "烟花易冷", 44 | "蒲公英的约定", "给我一首歌的时间", "明明就", "龙卷风", "以父之名", 45 | "半岛铁盒", "发如雪", "珊瑚海", "爱情废柴", "轨迹", "借口", 46 | "黑色毛衣", "听妈妈的话", "不该", "我不配", "说了再见", "星晴", "手写的从前", 47 | "东风破", "可爱女人", "夜的第七章", "蜗牛🐌", "世界末日", 48 | "红尘客栈", "爱在西元前", "听见下雨的声音", "屋顶", "一点点", "园游会" 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/me/bakumon/statuslayoutmanager/SimpleRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package me.bakumon.statuslayoutmanager; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.bumptech.glide.Glide; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * 列表适配器 16 | * 17 | * @author Bakumon 18 | * @date 2017/12/22 19 | */ 20 | 21 | public class SimpleRecyclerViewAdapter extends RecyclerView.Adapter { 22 | 23 | private List mSongs; 24 | 25 | static class ViewHolder extends RecyclerView.ViewHolder { 26 | final ImageView mImageView; 27 | final TextView mTextView; 28 | 29 | ViewHolder(View view) { 30 | super(view); 31 | mImageView = view.findViewById(R.id.avatar); 32 | mTextView = view.findViewById(android.R.id.text1); 33 | } 34 | } 35 | 36 | public SimpleRecyclerViewAdapter() { 37 | mSongs = SimpleData.getRandomSonList(30); 38 | } 39 | 40 | @Override 41 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 42 | View view = LayoutInflater.from(parent.getContext()) 43 | .inflate(R.layout.item_list, parent, false); 44 | return new ViewHolder(view); 45 | } 46 | 47 | @Override 48 | public void onBindViewHolder(final ViewHolder holder, int position) { 49 | holder.mTextView.setText(mSongs.get(position).songName); 50 | Glide.with(holder.mImageView.getContext()) 51 | .load(mSongs.get(position).drawableResID) 52 | .fitCenter() 53 | .into(holder.mImageView); 54 | } 55 | 56 | @Override 57 | public int getItemCount() { 58 | return mSongs.size(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/jay_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/drawable-nodpi/jay_1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/jay_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/drawable-nodpi/jay_2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/jay_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/drawable-nodpi/jay_3.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/jay_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/drawable-nodpi/jay_4.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/jay_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/drawable-nodpi/jay_5.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_customer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/drawable-xxhdpi/ic_customer.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_network_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/drawable-xxhdpi/ic_network_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_btn_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 19 | 20 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_custome.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 24 | 25 | 29 | 30 | 41 | 42 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/menu/status.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 18 | 22 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-en/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | StatusLayoutManager 3 | Loading 4 | Empty 5 | Error 6 | Success 7 | Customer 8 | Sorry,\nyou don\'t have permission to view it. 9 | Request 10 | Switch 11 | Empty-customer 12 | Error-customer 13 | Loading-customer 14 | Click Retry (empty) 15 | Click Retry (error) 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | StatusLayoutManager 3 | 加载中 4 | 空数据 5 | 出错 6 | 加载成功 7 | 自定义状态布局 8 | 对不起,\n您暂时没有权限查看 9 | 申请权限 10 | 切换账号 11 | 无数据-自定义 12 | 出错啦-自定义 13 | 加载中-自定义 14 | 点击重试(空数据) 15 | 点击重试(出错) 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/me/bakumon/statuslayoutmanager/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package me.bakumon.statuslayoutmanager; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.1' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gif/status_layout_manager.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/gif/status_layout_manager.gif -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 18 10:42:35 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group = 'com.github.bakumon' 4 | 5 | android { 6 | compileSdkVersion 26 7 | 8 | defaultConfig { 9 | minSdkVersion 11 10 | targetSdkVersion 26 11 | versionCode 4 12 | versionName "1.0.4" 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compileOnly 'com.android.support:support-annotations:26.1.0' 25 | } 26 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /library/src/main/java/me/bakumon/statuslayoutmanager/library/DefaultOnStatusChildClickListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Bakumon. https://github.com/Bakumon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package me.bakumon.statuslayoutmanager.library; 24 | 25 | import android.view.View; 26 | 27 | /** 28 | * @author Bakumon 29 | * @date 2017/12/22 30 | * @see OnStatusChildClickListener 的默认实现类 31 | * @since v1.0.1 32 | */ 33 | public class DefaultOnStatusChildClickListener implements OnStatusChildClickListener { 34 | @Override 35 | public void onEmptyChildClick(View view) { 36 | 37 | } 38 | 39 | @Override 40 | public void onErrorChildClick(View view) { 41 | 42 | } 43 | 44 | @Override 45 | public void onCustomerChildClick(View view) { 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /library/src/main/java/me/bakumon/statuslayoutmanager/library/OnStatusChildClickListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Bakumon. https://github.com/Bakumon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package me.bakumon.statuslayoutmanager.library; 24 | 25 | import android.view.View; 26 | 27 | /** 28 | * 状态布局中 view 的点击事件 29 | * 30 | * @author Bakumon 31 | * @date 2017/12/19 32 | * @since v1.0.0 33 | */ 34 | public interface OnStatusChildClickListener { 35 | 36 | /** 37 | * 空数据布局子 View 被点击 38 | * 39 | * @param view 被点击的 View 40 | * @since v1.0.0 41 | */ 42 | void onEmptyChildClick(View view); 43 | 44 | /** 45 | * 出错布局子 View 被点击 46 | * 47 | * @param view 被点击的 View 48 | * @since v1.0.0 49 | */ 50 | void onErrorChildClick(View view); 51 | 52 | /** 53 | * 自定义状态布局布局子 View 被点击 54 | * 55 | * @param view 被点击的 View 56 | * @since v1.0.0 57 | */ 58 | void onCustomerChildClick(View view); 59 | } 60 | -------------------------------------------------------------------------------- /library/src/main/java/me/bakumon/statuslayoutmanager/library/ReplaceLayoutHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Bakumon. https://github.com/Bakumon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package me.bakumon.statuslayoutmanager.library; 24 | 25 | import android.support.annotation.NonNull; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | /** 30 | * 替换布局帮助类 31 | * 32 | * @author Bakumon 33 | * @date 2017/12/18 34 | * @since v1.0.0 35 | */ 36 | public class ReplaceLayoutHelper { 37 | 38 | /** 39 | * 需要替换的 View 40 | */ 41 | private View contentLayout; 42 | /** 43 | * contentLayout 的布局参数 44 | */ 45 | private ViewGroup.LayoutParams params; 46 | /** 47 | * contentLayout 的父 ViewGroup 48 | */ 49 | private ViewGroup parentLayout; 50 | /** 51 | * contentLayout 在 parentLayout 中的位置 52 | */ 53 | private int viewIndex; 54 | /** 55 | * 当前显示的 View 56 | */ 57 | private View currentLayout; 58 | 59 | public ReplaceLayoutHelper(@NonNull View contentLayout) { 60 | this.contentLayout = contentLayout; 61 | getContentLayoutParams(); 62 | } 63 | 64 | /** 65 | * 获取 contentLayout 的参数信息 LayoutParams、Parent 66 | */ 67 | private void getContentLayoutParams() { 68 | this.params = contentLayout.getLayoutParams(); 69 | if (contentLayout.getParent() != null) { 70 | // 有直接的父控件 71 | this.parentLayout = (ViewGroup) contentLayout.getParent(); 72 | } else { 73 | // 认为 contentLayout 是 activity 的跟布局 74 | // 所以它的父控件就是 android.R.id.content 75 | this.parentLayout = contentLayout.getRootView().findViewById(android.R.id.content); 76 | } 77 | if (parentLayout == null) { 78 | // 以上两种方法还没有获取到父控件 79 | // contentLayout 非 activity 的跟布局 80 | // 父控件就是自己 81 | if (contentLayout instanceof ViewGroup) { 82 | parentLayout = (ViewGroup) contentLayout; 83 | this.viewIndex = 0; 84 | } else { 85 | // 否则,contentLayout 是一个非 ViewGroup 的跟布局 86 | // 该情况,没有办法替换布局,因此不支持 87 | throw new IllegalArgumentException("Parameter error:StatusLayoutManager#Build#with(),The argument cannot be a root layout of a non-ViewGroup."); 88 | } 89 | } else { 90 | int count = parentLayout.getChildCount(); 91 | for (int index = 0; index < count; index++) { 92 | if (contentLayout == parentLayout.getChildAt(index)) { 93 | // 获取 contentLayout 在 parentLayout 中的位置 94 | this.viewIndex = index; 95 | break; 96 | } 97 | } 98 | } 99 | this.currentLayout = this.contentLayout; 100 | } 101 | 102 | public void showStatusLayout(View view) { 103 | if (view == null) { 104 | return; 105 | } 106 | if (currentLayout != view) { 107 | currentLayout = view; 108 | ViewGroup parent = (ViewGroup) view.getParent(); 109 | // 去除 view 的 父 view,才能添加到别的 ViewGroup 中 110 | if (parent != null) { 111 | parent.removeView(view); 112 | } 113 | // 替换 = 移除 + 添加 114 | parentLayout.removeViewAt(viewIndex); 115 | parentLayout.addView(view, viewIndex, params); 116 | } 117 | } 118 | 119 | public void restoreLayout() { 120 | showStatusLayout(contentLayout); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /library/src/main/java/me/bakumon/statuslayoutmanager/library/StatusLayoutManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Bakumon. https://github.com/Bakumon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package me.bakumon.statuslayoutmanager.library; 24 | 25 | import android.support.annotation.CheckResult; 26 | import android.support.annotation.DrawableRes; 27 | import android.support.annotation.IdRes; 28 | import android.support.annotation.LayoutRes; 29 | import android.support.annotation.NonNull; 30 | import android.support.annotation.StringRes; 31 | import android.text.TextUtils; 32 | import android.view.LayoutInflater; 33 | import android.view.View; 34 | import android.widget.ImageView; 35 | import android.widget.TextView; 36 | 37 | /** 38 | * 状态布局管理器 39 | * 40 | * @author Bakumon 41 | * @date 2017/12/18 42 | * @since v1.0.0 43 | */ 44 | public class StatusLayoutManager { 45 | 46 | /** 47 | * 三种默认布局 ID 48 | */ 49 | private static final int DEFAULT_LOADING_LAYOUT_ID = R.layout.layout_status_layout_manager_loading; 50 | private static final int DEFAULT_EMPTY_LAYOUT_ID = R.layout.layout_status_layout_manager_empty; 51 | private static final int DEFAULT_ERROR_LAYOUT_ID = R.layout.layout_status_layout_manager_error; 52 | 53 | /** 54 | * 默认布局中可点击的 view ID 55 | */ 56 | private static final int DEFAULT_EMPTY_CLICKED_ID = R.id.status_layout_manager_bt_status_empty_click; 57 | private static final int DEFAULT_ERROR_CLICKED_ID = R.id.status_layout_manager_bt_status_error_click; 58 | 59 | /** 60 | * 默认颜色 61 | */ 62 | private static final int DEFAULT_CLICKED_TEXT_COLOR = R.color.status_layout_manager_click_view_text_color; 63 | private static final int DEFAULT_BACKGROUND_COLOR = R.color.status_layout_manager_background_color; 64 | 65 | /** 66 | * 默认图片 67 | */ 68 | private static final int DEFAULT_EMPTY_IMG_ID = R.drawable.status_layout_manager_ic_empty; 69 | private static final int DEFAULT_ERROR_IMG_ID = R.drawable.status_layout_manager_ic_error; 70 | 71 | private View contentLayout; 72 | 73 | @LayoutRes 74 | private int loadingLayoutID; 75 | private View loadingLayout; 76 | private String loadingText; 77 | 78 | @IdRes 79 | private int emptyClickViewId; 80 | @LayoutRes 81 | private int emptyLayoutID; 82 | private View emptyLayout; 83 | private String emptyText; 84 | private String emptyClickViewText; 85 | private int emptyClickViewTextColor; 86 | private boolean isEmptyClickViewVisible; 87 | @DrawableRes 88 | private int emptyImgID; 89 | 90 | @IdRes 91 | private int errorClickViewId; 92 | @LayoutRes 93 | private int errorLayoutID; 94 | private View errorLayout; 95 | private String errorText; 96 | private String errorClickViewText; 97 | private int errorClickViewTextColor; 98 | private boolean isErrorClickViewVisible; 99 | @DrawableRes 100 | private int errorImgID; 101 | 102 | private int defaultBackgroundColor; 103 | 104 | private OnStatusChildClickListener onStatusChildClickListener; 105 | 106 | private ReplaceLayoutHelper replaceLayoutHelper; 107 | 108 | private LayoutInflater inflater; 109 | 110 | private StatusLayoutManager(Builder builder) { 111 | this.contentLayout = builder.contentLayout; 112 | 113 | this.loadingLayoutID = builder.loadingLayoutID; 114 | this.loadingLayout = builder.loadingLayout; 115 | this.loadingText = builder.loadingText; 116 | 117 | this.emptyClickViewId = builder.emptyClickViewId; 118 | this.emptyLayoutID = builder.emptyLayoutID; 119 | this.emptyLayout = builder.emptyLayout; 120 | this.emptyText = builder.emptyText; 121 | this.emptyClickViewText = builder.emptyClickViewText; 122 | this.emptyClickViewTextColor = builder.emptyClickViewTextColor; 123 | this.isEmptyClickViewVisible = builder.isEmptyClickViewVisible; 124 | this.emptyImgID = builder.emptyImgID; 125 | 126 | this.errorClickViewId = builder.errorClickViewId; 127 | this.errorLayoutID = builder.errorLayoutID; 128 | this.errorLayout = builder.errorLayout; 129 | this.errorText = builder.errorText; 130 | this.errorClickViewText = builder.errorClickViewText; 131 | this.errorClickViewTextColor = builder.errorClickViewTextColor; 132 | this.isErrorClickViewVisible = builder.isErrorClickViewVisible; 133 | this.errorImgID = builder.errorImgID; 134 | 135 | this.defaultBackgroundColor = builder.defaultBackgroundColor; 136 | 137 | this.onStatusChildClickListener = builder.onStatusChildClickListener; 138 | 139 | this.replaceLayoutHelper = new ReplaceLayoutHelper(contentLayout); 140 | } 141 | 142 | private View inflate(@LayoutRes int resource) { 143 | if (inflater == null) { 144 | inflater = LayoutInflater.from(contentLayout.getContext()); 145 | } 146 | return inflater.inflate(resource, null); 147 | } 148 | 149 | /////////////////////////////////////////// 150 | /////////////////原有布局//////////////////// 151 | /////////////////////////////////////////// 152 | 153 | /** 154 | * 显示原有布局 155 | * 156 | * @since v1.0.0 157 | */ 158 | public void showSuccessLayout() { 159 | replaceLayoutHelper.restoreLayout(); 160 | } 161 | 162 | /////////////////////////////////////////// 163 | ////////////////加载中布局/////////////////// 164 | /////////////////////////////////////////// 165 | 166 | /** 167 | * 创建加载中布局 168 | */ 169 | private void createLoadingLayout() { 170 | if (loadingLayout == null) { 171 | loadingLayout = inflate(loadingLayoutID); 172 | } 173 | if (loadingLayoutID == DEFAULT_LOADING_LAYOUT_ID) { 174 | loadingLayout.setBackgroundColor(defaultBackgroundColor); 175 | } 176 | if (!TextUtils.isEmpty(loadingText)) { 177 | TextView loadingTextView = loadingLayout.findViewById(R.id.status_layout_manager_tv_status_loading_content); 178 | if (loadingTextView != null) { 179 | loadingTextView.setText(loadingText); 180 | } 181 | } 182 | } 183 | 184 | /** 185 | * 获取加载中布局 186 | * 187 | * @return 加载中布局 188 | * @since v1.0.0 189 | */ 190 | public View getLoadingLayout() { 191 | createLoadingLayout(); 192 | return loadingLayout; 193 | } 194 | 195 | /** 196 | * 显示加载中布局 197 | * 198 | * @since v1.0.0 199 | */ 200 | public void showLoadingLayout() { 201 | createLoadingLayout(); 202 | replaceLayoutHelper.showStatusLayout(loadingLayout); 203 | } 204 | 205 | /////////////////////////////////////////// 206 | ////////////////空数据布局/////////////////// 207 | /////////////////////////////////////////// 208 | 209 | /** 210 | * 创建空数据布局 211 | */ 212 | private void createEmptyLayout() { 213 | if (emptyLayout == null) { 214 | emptyLayout = inflate(emptyLayoutID); 215 | } 216 | if (emptyLayoutID == DEFAULT_EMPTY_LAYOUT_ID) { 217 | emptyLayout.setBackgroundColor(defaultBackgroundColor); 218 | } 219 | 220 | // 点击事件回调 221 | View view = emptyLayout.findViewById(emptyClickViewId); 222 | if (view != null && onStatusChildClickListener != null) { 223 | // 设置点击按钮点击时事件回调 224 | view.setOnClickListener(new View.OnClickListener() { 225 | @Override 226 | public void onClick(View view) { 227 | onStatusChildClickListener.onEmptyChildClick(view); 228 | } 229 | }); 230 | } 231 | 232 | // 设置默认空数据布局的提示文本 233 | if (!TextUtils.isEmpty(emptyText)) { 234 | TextView emptyTextView = emptyLayout.findViewById(R.id.status_layout_manager_tv_status_empty_content); 235 | if (emptyTextView != null) { 236 | emptyTextView.setText(emptyText); 237 | } 238 | } 239 | 240 | // 设置默认空数据布局的图片 241 | ImageView emptyImageView = emptyLayout.findViewById(R.id.status_layout_manager_iv_status_empty_img); 242 | if (emptyImageView != null) { 243 | emptyImageView.setImageResource(emptyImgID); 244 | } 245 | 246 | TextView emptyClickViewTextView = emptyLayout.findViewById(DEFAULT_EMPTY_CLICKED_ID); 247 | if (emptyClickViewTextView != null) { 248 | // 设置点击按钮的文本和可见性 249 | if (isEmptyClickViewVisible) { 250 | emptyClickViewTextView.setVisibility(View.VISIBLE); 251 | if (!TextUtils.isEmpty(emptyClickViewText)) { 252 | emptyClickViewTextView.setText(emptyClickViewText); 253 | } 254 | emptyClickViewTextView.setTextColor(emptyClickViewTextColor); 255 | } else { 256 | emptyClickViewTextView.setVisibility(View.GONE); 257 | } 258 | } 259 | } 260 | 261 | /** 262 | * 获取空数据布局 263 | * 264 | * @return 空数据布局 265 | * @since v1.0.0 266 | */ 267 | public View getEmptyLayout() { 268 | createEmptyLayout(); 269 | return emptyLayout; 270 | } 271 | 272 | /** 273 | * 显示空数据布局 274 | * 275 | * @since v1.0.0 276 | */ 277 | public void showEmptyLayout() { 278 | createEmptyLayout(); 279 | replaceLayoutHelper.showStatusLayout(emptyLayout); 280 | } 281 | 282 | /////////////////////////////////////////// 283 | /////////////////出错布局//////////////////// 284 | /////////////////////////////////////////// 285 | 286 | /** 287 | * 创建出错布局 288 | */ 289 | private void createErrorLayout() { 290 | if (errorLayout == null) { 291 | errorLayout = inflate(errorLayoutID); 292 | } 293 | if (errorLayoutID == DEFAULT_ERROR_LAYOUT_ID) { 294 | errorLayout.setBackgroundColor(defaultBackgroundColor); 295 | } 296 | 297 | View view = errorLayout.findViewById(errorClickViewId); 298 | if (view != null && onStatusChildClickListener != null) { 299 | // 设置点击按钮点击时事件回调 300 | view.setOnClickListener(new View.OnClickListener() { 301 | @Override 302 | public void onClick(View view) { 303 | onStatusChildClickListener.onErrorChildClick(view); 304 | } 305 | }); 306 | } 307 | 308 | // 设置默认出错布局的提示文本 309 | if (!TextUtils.isEmpty(errorText)) { 310 | TextView errorTextView = errorLayout.findViewById(R.id.status_layout_manager_tv_status_error_content); 311 | if (errorTextView != null) { 312 | errorTextView.setText(errorText); 313 | } 314 | } 315 | 316 | // 设置默认出错布局的图片 317 | ImageView errorImageView = errorLayout.findViewById(R.id.status_layout_manager_iv_status_error_image); 318 | if (errorImageView != null) { 319 | errorImageView.setImageResource(errorImgID); 320 | } 321 | 322 | TextView errorClickViewTextView = errorLayout.findViewById(DEFAULT_ERROR_CLICKED_ID); 323 | if (errorClickViewTextView != null) { 324 | // 设置点击按钮的文本和可见性 325 | if (isErrorClickViewVisible) { 326 | errorClickViewTextView.setVisibility(View.VISIBLE); 327 | if (!TextUtils.isEmpty(errorClickViewText)) { 328 | errorClickViewTextView.setText(errorClickViewText); 329 | } 330 | errorClickViewTextView.setTextColor(errorClickViewTextColor); 331 | } else { 332 | errorClickViewTextView.setVisibility(View.GONE); 333 | } 334 | } 335 | } 336 | 337 | /** 338 | * 获取出错布局 339 | * 340 | * @return 出错布局 341 | * @since v1.0.0 342 | */ 343 | public View getErrorLayout() { 344 | createErrorLayout(); 345 | return errorLayout; 346 | } 347 | 348 | /** 349 | * 显示出错布局 350 | * 351 | * @since v1.0.0 352 | */ 353 | public void showErrorLayout() { 354 | createErrorLayout(); 355 | replaceLayoutHelper.showStatusLayout(errorLayout); 356 | } 357 | 358 | /////////////////////////////////////////// 359 | ////////////////自定义布局/////////////////// 360 | /////////////////////////////////////////// 361 | 362 | /** 363 | * 显示自定义状态布局 364 | * 365 | * @param customLayout 自定义布局 366 | * @since v1.0.0 367 | */ 368 | public void showCustomLayout(@NonNull View customLayout) { 369 | replaceLayoutHelper.showStatusLayout(customLayout); 370 | } 371 | 372 | /** 373 | * 显示自定义状态布局 374 | * 375 | * @param customLayoutID 自定义状态布局 ID 376 | * @return 通过 customLayoutID 生成的 View 377 | * @since v1.0.0 378 | */ 379 | public View showCustomLayout(@LayoutRes int customLayoutID) { 380 | View customerView = inflate(customLayoutID); 381 | showCustomLayout(customerView); 382 | return customerView; 383 | } 384 | 385 | /** 386 | * 显示自定义状态布局 387 | * 388 | * @param customLayout 自定义布局 389 | * @param clickViewID 可点击 View ID 390 | * @since v1.0.0 391 | */ 392 | public void showCustomLayout(@NonNull View customLayout, @IdRes int... clickViewID) { 393 | replaceLayoutHelper.showStatusLayout(customLayout); 394 | if (onStatusChildClickListener == null) { 395 | return; 396 | } 397 | 398 | for (int aClickViewID : clickViewID) { 399 | View clickView = customLayout.findViewById(aClickViewID); 400 | if (clickView == null) { 401 | return; 402 | } 403 | 404 | // 设置点击按钮点击时事件回调 405 | clickView.setOnClickListener(new View.OnClickListener() { 406 | @Override 407 | public void onClick(View view) { 408 | onStatusChildClickListener.onCustomerChildClick(view); 409 | } 410 | }); 411 | } 412 | } 413 | 414 | /** 415 | * 显示自定义状态布局 416 | * 417 | * @param customLayoutID 自定义布局 ID 418 | * @param clickViewID 点击按钮 ID 419 | * @since v1.0.0 420 | */ 421 | public View showCustomLayout(@LayoutRes int customLayoutID, @IdRes int... clickViewID) { 422 | View customLayout = inflate(customLayoutID); 423 | showCustomLayout(customLayout, clickViewID); 424 | return customLayout; 425 | } 426 | 427 | public static final class Builder { 428 | 429 | private View contentLayout; 430 | 431 | @LayoutRes 432 | private int loadingLayoutID; 433 | private View loadingLayout; 434 | private String loadingText; 435 | 436 | @IdRes 437 | private int emptyClickViewId; 438 | @LayoutRes 439 | private int emptyLayoutID; 440 | private View emptyLayout; 441 | private String emptyText; 442 | private String emptyClickViewText; 443 | private int emptyClickViewTextColor; 444 | private boolean isEmptyClickViewVisible; 445 | @DrawableRes 446 | private int emptyImgID; 447 | 448 | @IdRes 449 | private int errorClickViewId; 450 | @LayoutRes 451 | private int errorLayoutID; 452 | private View errorLayout; 453 | private String errorText; 454 | private String errorClickViewText; 455 | private int errorClickViewTextColor; 456 | private boolean isErrorClickViewVisible; 457 | @DrawableRes 458 | private int errorImgID; 459 | 460 | private int defaultBackgroundColor; 461 | 462 | private OnStatusChildClickListener onStatusChildClickListener; 463 | 464 | /** 465 | * 创建状态布局 Build 对象 466 | * 467 | * @param contentLayout 原有布局,内容布局 468 | * @since v1.0.0 469 | */ 470 | public Builder(@NonNull View contentLayout) { 471 | this.contentLayout = contentLayout; 472 | // 设置默认布局 473 | this.loadingLayoutID = DEFAULT_LOADING_LAYOUT_ID; 474 | this.emptyLayoutID = DEFAULT_EMPTY_LAYOUT_ID; 475 | this.errorLayoutID = DEFAULT_ERROR_LAYOUT_ID; 476 | // 默认布局图片 477 | this.emptyImgID = DEFAULT_EMPTY_IMG_ID; 478 | this.errorImgID = DEFAULT_ERROR_IMG_ID; 479 | // 设置默认点击点击view id 480 | this.emptyClickViewId = DEFAULT_EMPTY_CLICKED_ID; 481 | this.errorClickViewId = DEFAULT_ERROR_CLICKED_ID; 482 | // 设置默认点击按钮属性 483 | this.isEmptyClickViewVisible = true; 484 | this.emptyClickViewTextColor = contentLayout.getContext().getResources().getColor(DEFAULT_CLICKED_TEXT_COLOR); 485 | this.isErrorClickViewVisible = true; 486 | this.errorClickViewTextColor = contentLayout.getContext().getResources().getColor(DEFAULT_CLICKED_TEXT_COLOR); 487 | // 设置默认背景色 488 | this.defaultBackgroundColor = contentLayout.getContext().getResources().getColor(DEFAULT_BACKGROUND_COLOR); 489 | } 490 | 491 | /////////////////////////////////////////// 492 | ////////////////加载中布局/////////////////// 493 | /////////////////////////////////////////// 494 | 495 | /** 496 | * 设置加载中布局 497 | * 498 | * @param loadingLayoutID 加载中布局 ID 499 | * @return 状态布局 Build 对象 500 | * @since v1.0.0 501 | */ 502 | public Builder setLoadingLayout(@LayoutRes int loadingLayoutID) { 503 | this.loadingLayoutID = loadingLayoutID; 504 | return this; 505 | } 506 | 507 | /** 508 | * 设置加载中布局 509 | * 510 | * @param loadingLayout 加载中布局 511 | * @return 状态布局 Build 对象 512 | * @since v1.0.0 513 | */ 514 | public Builder setLoadingLayout(@NonNull View loadingLayout) { 515 | this.loadingLayout = loadingLayout; 516 | return this; 517 | } 518 | 519 | /** 520 | * 设置默认加载中布局提示文本 521 | * 522 | * @param loadingText 加载中布局提示文本 523 | * @return 状态布局 Build 对象 524 | * @since v1.0.0 525 | */ 526 | public Builder setDefaultLoadingText(String loadingText) { 527 | this.loadingText = loadingText; 528 | return this; 529 | } 530 | 531 | /** 532 | * 设置默认加载中布局提示文本 533 | * 534 | * @param loadingTextStrID 加载中布局提示文本 ID 535 | * @return 状态布局 Build 对象 536 | * @since v1.0.0 537 | */ 538 | public Builder setDefaultLoadingText(@StringRes int loadingTextStrID) { 539 | this.loadingText = contentLayout.getContext().getResources().getString(loadingTextStrID); 540 | return this; 541 | } 542 | 543 | 544 | /////////////////////////////////////////// 545 | ////////////////空数据布局/////////////////// 546 | /////////////////////////////////////////// 547 | 548 | /** 549 | * 设置空数据布局 550 | * 551 | * @param emptyLayoutResId 空数据布局 ID 552 | * @return 状态布局 Build 对象 553 | * @since v1.0.0 554 | */ 555 | public Builder setEmptyLayout(@LayoutRes int emptyLayoutResId) { 556 | this.emptyLayoutID = emptyLayoutResId; 557 | return this; 558 | } 559 | 560 | /** 561 | * 设置空数据布局 562 | * 563 | * @param emptyLayout 空数据布局 564 | * @return 状态布局 Build 对象 565 | * @since v1.0.0 566 | */ 567 | public Builder setEmptyLayout(@NonNull View emptyLayout) { 568 | this.emptyLayout = emptyLayout; 569 | return this; 570 | } 571 | 572 | /** 573 | * 设置空数据布局点击按钮 ID 574 | * 575 | * @param emptyClickViewResId 空数据布局点击按钮 ID 576 | * @return 状态布局 Build 对象 577 | * @since v1.0.0 578 | */ 579 | public Builder setEmptyClickViewID(@IdRes int emptyClickViewResId) { 580 | this.emptyClickViewId = emptyClickViewResId; 581 | return this; 582 | } 583 | 584 | /** 585 | * 设置默认空数据布局点击按钮文本 586 | * 587 | * @param emptyClickViewText 点击按钮文本 588 | * @return 状态布局 Build 对象 589 | * @since v1.0.0 590 | */ 591 | public Builder setDefaultEmptyClickViewText(String emptyClickViewText) { 592 | this.emptyClickViewText = emptyClickViewText; 593 | return this; 594 | } 595 | 596 | /** 597 | * 设置默认空数据布局点击按钮文本 598 | * 599 | * @param emptyClickViewTextID 点击按钮文本 ID 600 | * @return 状态布局 Build 对象 601 | * @since v1.0.0 602 | */ 603 | public Builder setDefaultEmptyClickViewText(@StringRes int emptyClickViewTextID) { 604 | this.emptyClickViewText = contentLayout.getContext().getResources().getString(emptyClickViewTextID); 605 | return this; 606 | } 607 | 608 | /** 609 | * 设置默认空数据布局点击按钮文本颜色 610 | * 611 | * @param emptyClickViewTextColor 点击按钮文本颜色 612 | * @return 状态布局 Build 对象 613 | * @since v1.0.0 614 | */ 615 | public Builder setDefaultEmptyClickViewTextColor(int emptyClickViewTextColor) { 616 | this.emptyClickViewTextColor = emptyClickViewTextColor; 617 | return this; 618 | } 619 | 620 | /** 621 | * 设置默认空数据布局点击按钮是否可见 622 | * 623 | * @param isEmptyClickViewVisible true:可见 false:不可见 624 | * @return 状态布局 Build 对象 625 | * @since v1.0.0 626 | */ 627 | public Builder setDefaultEmptyClickViewVisible(boolean isEmptyClickViewVisible) { 628 | this.isEmptyClickViewVisible = isEmptyClickViewVisible; 629 | return this; 630 | } 631 | 632 | /** 633 | * 设置空数据布局图片 634 | * 635 | * @param emptyImgID 空数据布局图片 ID 636 | * @return 状态布局 Build 对象 637 | * @since v1.0.0 638 | */ 639 | public Builder setDefaultEmptyImg(@DrawableRes int emptyImgID) { 640 | this.emptyImgID = emptyImgID; 641 | return this; 642 | } 643 | 644 | /////////////////////////////////////////// 645 | /////////////////出错布局//////////////////// 646 | /////////////////////////////////////////// 647 | 648 | /** 649 | * 设置空数据布局提示文本 650 | * 651 | * @param emptyText 空数据布局提示文本 652 | * @return 状态布局 Build 对象 653 | * @since v1.0.0 654 | */ 655 | public Builder setDefaultEmptyText(String emptyText) { 656 | this.emptyText = emptyText; 657 | return this; 658 | } 659 | 660 | /** 661 | * 设置空数据布局提示文本 662 | * 663 | * @param emptyTextStrID 空数据布局提示文本 ID 664 | * @return 状态布局 Build 对象 665 | * @since v1.0.0 666 | */ 667 | public Builder setDefaultEmptyText(@StringRes int emptyTextStrID) { 668 | this.emptyText = contentLayout.getContext().getResources().getString(emptyTextStrID); 669 | return this; 670 | } 671 | 672 | 673 | /** 674 | * 设置出错布局 675 | * 676 | * @param errorLayoutResId 出错布局 ID 677 | * @return 状态布局 Build 对象 678 | * @since v1.0.0 679 | */ 680 | public Builder setErrorLayout(@LayoutRes int errorLayoutResId) { 681 | this.errorLayoutID = errorLayoutResId; 682 | return this; 683 | } 684 | 685 | /** 686 | * 设置出错布局 687 | * 688 | * @param errorLayout 出错布局 689 | * @return 状态布局 Build 对象 690 | * @since v1.0.0 691 | */ 692 | public Builder setErrorLayout(@NonNull View errorLayout) { 693 | this.errorLayout = errorLayout; 694 | return this; 695 | } 696 | 697 | /** 698 | * 设置出错布局点击按钮 ID 699 | * 700 | * @param errorClickViewResId 出错布局点击按钮 ID 701 | * @return 状态布局 Build 对象 702 | * @since v1.0.0 703 | */ 704 | public Builder setErrorClickViewID(@IdRes int errorClickViewResId) { 705 | this.errorClickViewId = errorClickViewResId; 706 | return this; 707 | } 708 | 709 | /** 710 | * 设置出错布局提示文本 711 | * 712 | * @param errorText 出错布局提示文本 713 | * @return 状态布局 Build 对象 714 | * @since v1.0.0 715 | */ 716 | public Builder setDefaultErrorText(String errorText) { 717 | this.errorText = errorText; 718 | return this; 719 | } 720 | 721 | /** 722 | * 设置出错布局提示文本 723 | * 724 | * @param errorTextStrID 出错布局提示文本 ID 725 | * @return 状态布局 Build 对象 726 | * @since v1.0.0 727 | */ 728 | public Builder setDefaultErrorText(@StringRes int errorTextStrID) { 729 | this.errorText = contentLayout.getContext().getResources().getString(errorTextStrID); 730 | return this; 731 | } 732 | 733 | /** 734 | * 设置默认出错布局点击按钮文本 735 | * 736 | * @param errorClickViewText 点击按钮文本 737 | * @return 状态布局 Build 对象 738 | * @since v1.0.0 739 | */ 740 | public Builder setDefaultErrorClickViewText(String errorClickViewText) { 741 | this.errorClickViewText = errorClickViewText; 742 | return this; 743 | } 744 | 745 | /** 746 | * 设置默认出错布局点击按钮文本 747 | * 748 | * @param errorClickViewTextID 点击按钮文本 ID 749 | * @return 状态布局 Build 对象 750 | * @since v1.0.0 751 | */ 752 | public Builder setDefaultErrorClickViewText(@StringRes int errorClickViewTextID) { 753 | this.errorClickViewText = contentLayout.getContext().getResources().getString(errorClickViewTextID); 754 | return this; 755 | } 756 | 757 | /** 758 | * 设置默认出错布局点击按钮文本颜色 759 | * 760 | * @param errorClickViewTextColor 点击按钮文本颜色 761 | * @return 状态布局 Build 对象 762 | * @since v1.0.0 763 | */ 764 | public Builder setDefaultErrorClickViewTextColor(int errorClickViewTextColor) { 765 | this.errorClickViewTextColor = errorClickViewTextColor; 766 | return this; 767 | } 768 | 769 | /** 770 | * 设置出错布局点击按钮可见行 771 | * 772 | * @param isErrorClickViewVisible true:可见 false:不可见 773 | * @return 状态布局 Build 对象 774 | * @since v1.0.0 775 | */ 776 | public Builder setDefaultErrorClickViewVisible(boolean isErrorClickViewVisible) { 777 | this.isErrorClickViewVisible = isErrorClickViewVisible; 778 | return this; 779 | } 780 | 781 | /** 782 | * 设置出错布局图片 783 | * 784 | * @param errorImgID 出错布局图片 ID 785 | * @return 状态布局 Build 对象 786 | * @since v1.0.0 787 | */ 788 | public Builder setDefaultErrorImg(@DrawableRes int errorImgID) { 789 | this.errorImgID = errorImgID; 790 | return this; 791 | } 792 | 793 | /** 794 | * 设置默认布局的背景颜色,包括加载中、空数据和出错布局 795 | * 796 | * @param defaultBackgroundColor 默认布局的背景颜色 797 | * @return 状态布局 Build 对象 798 | * @since v1.0.0 799 | */ 800 | public Builder setDefaultLayoutsBackgroundColor(int defaultBackgroundColor) { 801 | this.defaultBackgroundColor = defaultBackgroundColor; 802 | return this; 803 | } 804 | 805 | /** 806 | * 设置点击事件监听器 807 | * 808 | * @param listener 点击事件监听器 809 | * @return 状态布局 Build 对象 810 | * @since v1.0.0 811 | */ 812 | public Builder setOnStatusChildClickListener(OnStatusChildClickListener listener) { 813 | this.onStatusChildClickListener = listener; 814 | return this; 815 | } 816 | 817 | /** 818 | * 创建状态布局管理器 819 | * 820 | * @return 状态布局管理器 821 | * @since v1.0.0 822 | */ 823 | @NonNull 824 | @CheckResult 825 | public StatusLayoutManager build() { 826 | return new StatusLayoutManager(this); 827 | } 828 | 829 | } 830 | } 831 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/status_layout_manager_ic_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/library/src/main/res/drawable-xxhdpi/status_layout_manager_ic_empty.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/status_layout_manager_ic_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bakumon/StatusLayoutManager/61e29941f00de1d19afaa033c87c37fdf4571ed4/library/src/main/res/drawable-xxhdpi/status_layout_manager_ic_error.png -------------------------------------------------------------------------------- /library/src/main/res/layout/layout_status_layout_manager_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 25 | 26 | 37 | 38 | -------------------------------------------------------------------------------- /library/src/main/res/layout/layout_status_layout_manager_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 25 | 26 | 37 | 38 | -------------------------------------------------------------------------------- /library/src/main/res/layout/layout_status_layout_manager_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /library/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 加载中… 4 | 还没有内容 5 | 似乎出了点问题 6 | 重新加载 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/values-zh-rHK/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 加載中… 4 | 還沒有內容 5 | 似乎出了點問題 6 | 重新加載 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 載入中… 4 | 還沒有內容 5 | 似乎出了點問題 6 | 重新載入 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #EBEBEB 4 | #BFBFBF 5 | #0084FF 6 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Loading… 4 | Empty 5 | Error 6 | Reload 7 | 8 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library' 2 | --------------------------------------------------------------------------------