├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── compiler.xml
├── encodings.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── jarRepositories.xml
├── kotlinc.xml
├── misc.xml
├── smartfox_info.xml
└── vcs.xml
├── README.md
├── ScreenShot
├── S20829-17471726.png
├── S20829-17472742.png
├── S20829-17474084.png
└── S20829-17475072.png
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── xiaobin
│ │ └── bindingadapter
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── xiaobin
│ │ │ └── bindingadapter
│ │ │ ├── bean
│ │ │ └── ChatListBean.kt
│ │ │ ├── ui
│ │ │ ├── EmptyDemoActivity.java
│ │ │ ├── StartActivity.java
│ │ │ ├── base
│ │ │ │ └── BaseActivity.java
│ │ │ ├── grid
│ │ │ │ ├── GridMultiActivity.java
│ │ │ │ └── GridSingleActivity.java
│ │ │ ├── linear
│ │ │ │ ├── LinearMultiActivity.java
│ │ │ │ └── LinearSingleActivity.java
│ │ │ ├── pageState
│ │ │ │ ├── MyEmptyPageView.java
│ │ │ │ └── MyLoadMoreView.java
│ │ │ └── staggered
│ │ │ │ ├── StaggeredMultiActivity.java
│ │ │ │ └── StaggeredSingleActivity.java
│ │ │ └── utils
│ │ │ └── BindAdapterUtils.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── error_image.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_svg_ban.xml
│ │ ├── ic_svg_ok.xml
│ │ └── shap_button.xml
│ │ ├── layout
│ │ ├── activity_base.xml
│ │ ├── activity_empty.xml
│ │ ├── item_grid.xml
│ │ ├── item_head.xml
│ │ ├── item_linear.xml
│ │ ├── item_staggered.xml
│ │ ├── item_staggered2.xml
│ │ ├── item_staggered_head.xml
│ │ ├── item_start.xml
│ │ ├── layout_my_loading_more.xml
│ │ ├── layout_my_page_empty.xml
│ │ ├── layout_my_page_err.xml
│ │ └── layout_my_page_loading.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
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── xiaobin
│ └── bindingadapter
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── quickbindadapter
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── xiaobin
│ │ └── quickbindadapter
│ │ ├── BindHolder.kt
│ │ ├── ItemData.kt
│ │ ├── QuickBindAdapter.kt
│ │ ├── QuickBindPagingAdapter.kt
│ │ ├── holder
│ │ ├── EmptyViewHolder.kt
│ │ └── LoadViewHolder.kt
│ │ ├── interfaces
│ │ └── StaggeredFullSpan.kt
│ │ ├── paging
│ │ └── EmptyPageAdapter.kt
│ │ └── view
│ │ ├── BaseLoadView.kt
│ │ ├── BasePageStateView.kt
│ │ ├── DefaultEmptyStatePage.kt
│ │ └── DefaultLoadView.kt
│ └── res
│ ├── layout
│ ├── mc_xb_item_loadmore.xml
│ ├── mc_xb_layout_page_empty.xml
│ ├── mc_xb_layout_page_error.xml
│ ├── mc_xb_layout_page_loading.xml
│ └── mc_xb_layout_place_page.xml
│ └── values
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.idea/smartfox_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QuickBindAdapter
2 |
3 | [](https://jitpack.io/#Matchas-xiaobin/QuickBindAdapter)
4 |
5 | 使用Databinding简单快速实现RecyclerView多布局Adapter。只需要用这一个Adapter就够了。
6 |
7 | ### Demo的首页
8 | 
9 |
10 | ### 列表数据是空的时候显示的占位图
11 | 
12 |
13 | ### GridLayoutManager多种布局展示
14 | 
15 |
16 | ### StaggeredLayoutManager多种布局展示
17 | 
18 |
19 | ## 在项目中引用
20 |
21 | ### Gradle
22 |
23 | Step 1. Add it in your root build.gradle at the end of repositories
24 |
25 | allprojects {
26 | repositories {
27 | ...
28 | maven { url 'https://jitpack.io' }
29 | }
30 | }
31 |
32 | Step 2. Add the dependency
33 |
34 | dependencies {
35 | implementation 'com.github.Matchas-xiaobin:QuickBindAdapter:3.0.8'
36 | }
37 |
38 | ### API多次大量改动,如果已经在使用老版本了,请注意斟酌是否升级版本:
39 |
40 | 请在demo中查看具体用法
41 | 请注意:必须开启dataBinding功能才能使用这个
42 | 请确认在项目模块下添加了如下代码:
43 | android {
44 | ...
45 | buildFeatures {
46 | dataBinding true
47 | }
48 | ...
49 | }
50 |
51 | ### 在代码中使用:
52 |
53 | QuickBindAdapter adapter = new QuickBindAdapter();
54 | adapter.bind()
55 | adapter.setxxx()
56 | ...
57 |
58 | Kotlin:
59 | 1.委托
60 | private val adapter: QuickBindAdapter by lazy {
61 | QuickBindAdapter().apply{
62 | bind(,,)
63 | addClicks(,,)
64 | ...
65 | }
66 | }
67 | 2.正常使用
68 | val adapter = QuickBinAdapter().apply{
69 | bind(,,)
70 | addClicks(,,)
71 | ...
72 | }
73 |
74 | ### 绑定数据类型和布局 有几种布局,就绑定几次,每一种布局要对应一种数据类型
75 |
76 | 三个参数分别为: 数据类型 , 对应的布局 , 这个数据要设置到的属性
77 | adapter.bind(DataBean.class, R.layout.item_child, BR.data);
78 | adapter.bind(String.class, R.layout.item_group, BR.data);
79 |
80 | 绑定布局的时候,可以不绑定数据要设置到哪个属性
81 | 一般用来穿插一些固定内容不变化的布局,或者想用代码动态配置布局的,建议配合QuickBind一起使用:
82 | adapter.bind(class, layoutId);
83 |
84 | 多布局注意:
85 | 如果使用瀑布流StaggeredGridLayoutManager,假设你的spanCount为3,如果你想实现标题直接占满一行,
86 | 则需要将标题的这个实体类实现StaggeredFullSpan接口
87 | 并且瀑布流无法像gridLayoutManager那样自定义占几个spanCount
88 |
89 | ### 新增空数据时展示全屏占位图;
90 |
91 | 使用默认的空数据时展示的占位图控制器
92 | adapter.setEmptyView(context);
93 | 使用自定义的空数据时展示的占位图控制器,需要继承BasePlaceholder
94 | adapter.setEmptyView(MyEmptyPageView());
95 |
96 | 展示 加载中 占位图:
97 | adapter.showLoadPage()
98 | adapter.showLoadPage(true)//强制显示 加载中 页面->如果有数据,则清空数据
99 | 展示 内容为空 占位图:
100 | adapter.showEmptyPage()
101 | adapter.showEmptyPage(true)//强制显示 空数据 页面->如果有数据,则清空数据
102 | 展示 加载错误 占位图:
103 | adapter.showErrorPage()
104 | adapter.showErrorPage(true)//强制显示 加载错误 页面->如果有数据,则清空数据
105 |
106 | ### QuickBind接口,用于只使用Databinding绑定控件,手动写复杂逻辑;
107 |
108 | adapter.setQuickBind((binding, itemData, position) -> {
109 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
110 | //如果是多布局,则需要做下判断:
111 | if (binding instanceof ItemStartBinding) {
112 | // R.layout.item_start 类型 的item
113 | }
114 | //也可以这样:
115 | if (itemData instanceof String) {
116 | // String 类型 的item
117 | }
118 | });
119 |
120 | ### 添加子控件点击事件 对应每一种数据类型的item,添加其子控件的点击事件
121 |
122 | adapter.addClicks(ChatListBean.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
123 |
124 | adapter.addClicks(String.class, R.id.tv_name);
125 |
126 | ### 点击事件:
127 |
128 | adapter.setOnItemClickListener(OnItemClickListener)
129 |
130 | adapter.setOnItemChildClickListener(OnItemClickListener)
131 |
132 | ### 长按事件:
133 |
134 | adapter.setOnItemLongClickListener(OnItemLongClickListener)
135 |
136 | adapter.setOnItemChildLongClickListener(OnItemLongClickListener)
137 |
138 | ### 获取item数据的api;
139 |
140 | 所有数据
141 | ItemData dataList = adapter.getListData();
142 |
143 | 单个数据
144 | Object itemData = adapter.getItemData(position);
145 |
146 | ### 设置新数据:
147 |
148 | dataList 如果是需要多布局,建议使用ItemData 添加数据。
149 |
150 | 例如:
151 | ItemData dataList = new ItemData();
152 | dataList.add("Group");
153 | ChatListBean item;
154 | for (int i = 0; i < 5; i++) {
155 | item = new ChatListBean();
156 | item.setId(String.valueOf(i));
157 | dataList.add(item);
158 | }
159 | adapter.setNewData(dataList);
160 | 或者你也可以直接:
161 | adapter.addData("Group")
162 | ChatListBean item;
163 | for (int i = 0; i < 5; i++) {
164 | item = new ChatListBean();
165 | item.setId(String.valueOf(i));
166 | adapter.addData(item);
167 | }
168 |
169 | ### 添加数据
170 |
171 | 添加单个
172 | adapter.addData(object);
173 | 添加单个,并且让这个新加的处于可见范围
174 | adapter.addData(object, true);
175 |
176 | 添加多个到列表后面
177 | adapter.addData(arrayList);
178 | 添加多个到列表后面,并且让新增的第一个处于可见范围
179 | adapter.addData(arrayList, true);
180 |
181 | ### 插入数据到某个位置API;
182 |
183 | 插入单个数据到指定位置
184 | adapter.insertData(index, itemData);
185 | 插入单个数据到指定位置,并且让新插入的处于可见范围
186 | adapter.insertData(index, itemData, true);
187 |
188 | 从指定位置插入多个数据
189 | adapter.insertDatas(index, arraysData);
190 | 从指定位置插入多个数据,并且让新增的第一个处于可见范围
191 | adapter.insertDatas(index, arraysData, true);
192 |
193 | ### 移除单个
194 |
195 | adapter.remove(position);
196 |
197 | ### 替换单个
198 |
199 | //替换指定item
200 | adapter.replace(position, object);
201 | //替换指定item,并使其可见
202 | adapter.replace(position, object, true);
203 |
204 | ### 移动位置
205 |
206 | //移动某个item到另一个位置
207 | movedPositions(fromPosition, toPosition);
208 | //移动某个item到另一个位置,并且让这个item的新位置处于可见
209 | movedPositions(fromPosition, toPosition, true);
210 |
211 | ### 加载更多功能;
212 |
213 | 使用方式:
214 | adapter.setOnLoadMoreListener(@NonNull OnLoadMoreListener onLoadMoreListener);
215 | 加载成功:
216 | adapter.loadMoreSuccess();
217 | 加载失败:
218 | adapter.loadMoreFailed();
219 | 加载完成,没有更多数据了:
220 | adapter.loadMoreSuccessAndNoMore();
221 | 主动调用加载更多:
222 | adapter.loadMore();
223 | 禁止 触底自动加载更多 关闭后,列表滑动到底后,需要用户点击才会触发加载更多
224 | adapter.setAutoLoadMore(false);
225 | 关闭加载更多功能:
226 | adapter.setOnLoadMoreListener(null as (()->Unit)?)
227 | 或者(3.2.3版本开始支持):
228 | adapter.removeOnLoadMoreListener()
229 | 设置是否开启 当列表数据没有充满rv的情况下,也自动加载更多
230 | * 如果关闭了 触底自动加载更多,那么这个方法将不在起作用
231 | adapter.enableLoadMoreWhenPageNotFull(true);
232 |
233 | - 修改 正在加载 文字
234 | - 修改 加载成功 文字
235 | - 修改 加载失败 文字
236 | - 修改 没有更多数据了 文字
237 | 使用方式:
238 | DefaultLoadView loadView = new DefaultLoadView(context)
239 | loadView.setOnLoadingText("加载中...")
240 | loadView.setOnWaitLoadingText("点击加载更多")
241 | ...
242 | adapter.setLoadView(loadView);
243 |
244 | DefaultLoadView是内置的默认的。
245 | 自定义的布局需要继承BaseLoadView去实现。
246 |
--------------------------------------------------------------------------------
/ScreenShot/S20829-17471726.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/ScreenShot/S20829-17471726.png
--------------------------------------------------------------------------------
/ScreenShot/S20829-17472742.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/ScreenShot/S20829-17472742.png
--------------------------------------------------------------------------------
/ScreenShot/S20829-17474084.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/ScreenShot/S20829-17474084.png
--------------------------------------------------------------------------------
/ScreenShot/S20829-17475072.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/ScreenShot/S20829-17475072.png
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-kapt'
5 | }
6 |
7 | android {
8 | compileSdk 34
9 |
10 | defaultConfig {
11 | applicationId "com.xiaobin.bindingadapter"
12 | minSdk 14
13 | targetSdk 34
14 | versionCode 1
15 | versionName "1.0"
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 | compileOptions {
19 | sourceCompatibility JavaVersion.VERSION_17
20 | targetCompatibility JavaVersion.VERSION_17
21 | }
22 | buildFeatures {
23 | dataBinding true
24 | }
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
29 | }
30 | }
31 | namespace 'com.xiaobin.bindingadapter'
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 | implementation 'androidx.appcompat:appcompat:1.6.1'
37 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
38 | testImplementation 'junit:junit:4.13.2'
39 | androidTestImplementation 'androidx.test:runner:1.5.2'
40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
41 | implementation 'androidx.recyclerview:recyclerview:1.3.1'
42 | implementation 'com.github.bumptech.glide:glide:4.15.1'
43 | annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
44 | implementation project(path: ':quickbindadapter')
45 | implementation "androidx.core:core-ktx:1.10.1"
46 | //noinspection GradleDependency
47 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
48 | implementation 'androidx.cardview:cardview:1.0.0'
49 | }
50 | repositories {
51 | mavenCentral()
52 | }
53 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/xiaobin/bindingadapter/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.InstrumentationRegistry;
6 | import androidx.test.runner.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getTargetContext();
24 |
25 | assertEquals("com.xiaobin.bindingadapter", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/bean/ChatListBean.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.bean
2 |
3 | import com.xiaobin.bindingadapter.R
4 | import com.xiaobin.quickbindadapter.interfaces.StaggeredFullSpan
5 |
6 | /**
7 | * @author 小斌
8 | * @data 2019/7/30
9 | */
10 | class ChatListBean {
11 | private var id: String? = null
12 | val drawableId: Int
13 | get() = R.mipmap.ic_launcher
14 | val name: String
15 | get() = "这个用户没有名字。ID:$id"
16 | val time: String
17 | get() = "16:16"
18 | val message: String
19 | get() = "这个人想和你聊天。"
20 |
21 | fun getId(): String {
22 | return if (id == null) "" else id!!
23 | }
24 |
25 | fun setId(id: String?) {
26 | this.id = id ?: ""
27 | }
28 | }
29 |
30 | class ChatListBean2 {
31 | private var id: String? = null
32 | val drawableId: Int
33 | get() = R.mipmap.ic_launcher
34 | val name: String
35 | get() = "这个用户没有名字。ID:$id"
36 | val time: String
37 | get() = "16:16"
38 | val message: String
39 | get() = "这个人想和你聊天。"
40 |
41 | fun getId(): String {
42 | return if (id == null) "" else id!!
43 | }
44 |
45 | fun setId(id: String?) {
46 | this.id = id ?: ""
47 | }
48 | }
49 |
50 | data class StaggeredHeadBean(
51 | val title: String
52 | ) : StaggeredFullSpan
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/EmptyDemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 | import android.widget.Toast;
6 |
7 | import androidx.recyclerview.widget.LinearLayoutManager;
8 |
9 | import com.xiaobin.bindingadapter.BR;
10 | import com.xiaobin.bindingadapter.R;
11 | import com.xiaobin.bindingadapter.bean.ChatListBean;
12 | import com.xiaobin.bindingadapter.databinding.ActivityEmptyBinding;
13 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
14 | import com.xiaobin.bindingadapter.ui.pageState.MyEmptyPageView;
15 | import com.xiaobin.quickbindadapter.view.DefaultEmptyStatePage;
16 | import com.xiaobin.quickbindadapter.view.PageState;
17 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
18 |
19 | /**
20 | * @author 小斌
21 | * @data 2019/8/20
22 | **/
23 | public class EmptyDemoActivity extends BaseActivity {
24 |
25 | private QuickBindAdapter bindAdapter;
26 |
27 | @Override
28 | protected boolean showBackButton() {
29 | return true;
30 | }
31 |
32 | @Override
33 | protected int getLayoutId() {
34 | return R.layout.activity_empty;
35 | }
36 |
37 | @Override
38 | protected String getActionTitle() {
39 | return "空数据占位布局";
40 | }
41 |
42 | @Override
43 | protected void initView(Bundle savedInstanceState) {
44 | //修改默认初始显示无数据, 不修改则是加载中
45 | DefaultEmptyStatePage defaultEmptyStatePage = new DefaultEmptyStatePage(this);
46 | defaultEmptyStatePage.setDefaultPage(PageState.Empty);
47 | bindAdapter = QuickBindAdapter.Companion.create()
48 | .bind(ChatListBean.class, R.layout.item_linear, BR.data)
49 | .setEmptyView(defaultEmptyStatePage);//设置默认的无数据时占位布局
50 |
51 | binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
52 | binding.recyclerView.setAdapter(bindAdapter);
53 | }
54 |
55 | public void addData(View view) {
56 | int i = bindAdapter.getDataCount() % 2;
57 | if (i == 0) {
58 | ChatListBean item = new ChatListBean();
59 | item.setId("嘿咻");
60 | bindAdapter.addData(item, true);
61 | } else {
62 | bindAdapter.addData(12, true);
63 | }
64 | }
65 |
66 | public void reduceData(View view) {
67 | if (bindAdapter.getItemCount() > 0)
68 | bindAdapter.remove(bindAdapter.getItemCount() - 1);
69 | }
70 |
71 | public void loadPage(View view) {
72 | bindAdapter.showLoadPage(true);
73 | }
74 |
75 | public void errPage(View view) {
76 | bindAdapter.showErrorPage(true);
77 | }
78 |
79 | public void myPageState(View view) {
80 | Toast.makeText(this, "使用自定义布局", Toast.LENGTH_SHORT).show();
81 | bindAdapter.setEmptyView(new MyEmptyPageView());
82 | //在上面initView中,已经设置了默认的布局,所以这里可以重新设置一下adapter达到这个布局的目的
83 | binding.recyclerView.setAdapter(bindAdapter);
84 | }
85 |
86 | public void defaultPageState(View view) {
87 | Toast.makeText(this, "使用默认布局", Toast.LENGTH_SHORT).show();
88 | bindAdapter.setEmptyView(new DefaultEmptyStatePage(this));
89 | //在上面initView中,已经设置了默认的布局,所以这里可以重新设置一下adapter达到这个布局的目的
90 | binding.recyclerView.setAdapter(bindAdapter);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/StartActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Color;
5 | import android.os.Bundle;
6 |
7 | import androidx.recyclerview.widget.LinearLayoutManager;
8 |
9 | import com.xiaobin.bindingadapter.BR;
10 | import com.xiaobin.bindingadapter.R;
11 | import com.xiaobin.bindingadapter.databinding.ActivityBaseBinding;
12 | import com.xiaobin.bindingadapter.databinding.ItemStartBinding;
13 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
14 | import com.xiaobin.bindingadapter.ui.grid.GridMultiActivity;
15 | import com.xiaobin.bindingadapter.ui.grid.GridSingleActivity;
16 | import com.xiaobin.bindingadapter.ui.linear.LinearMultiActivity;
17 | import com.xiaobin.bindingadapter.ui.linear.LinearSingleActivity;
18 | import com.xiaobin.bindingadapter.ui.staggered.StaggeredMultiActivity;
19 | import com.xiaobin.bindingadapter.ui.staggered.StaggeredSingleActivity;
20 | import com.xiaobin.quickbindadapter.view.DefaultEmptyStatePage;
21 | import com.xiaobin.quickbindadapter.view.DefaultLoadView;
22 | import com.xiaobin.quickbindadapter.view.DefaultLoadViewConfigsBean;
23 | import com.xiaobin.quickbindadapter.view.DefaultPlacePageConfigsBean;
24 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
25 |
26 | import java.util.Iterator;
27 | import java.util.LinkedList;
28 |
29 | /**
30 | * @author 小斌
31 | * @data 2019/7/31
32 | **/
33 | public class StartActivity extends BaseActivity {
34 |
35 | @Override
36 | protected int getLayoutId() {
37 | return R.layout.activity_base;
38 | }
39 |
40 | @Override
41 | protected void initView(Bundle savedInstanceState) {
42 | /**
43 | * 全局配置默认加载更多布局的配置,仅对使用默认加载更多布局生效,尽可能早配置。
44 | * 可以放在Application里配置,这是优先级最低的
45 | * 配置这个不会影响在创建adapter时再自定义配置:
46 | * 比如:
47 | * DefaultLoadView view = new DefaultLoadView()
48 | * view.setNoMoreDataText("")
49 | * view.setOnFailedText("")
50 | * ...
51 | * adapter.setLoadView(view)
52 | */
53 | DefaultLoadView.Companion.createGlobalConfig(() -> {
54 | DefaultLoadViewConfigsBean data = new DefaultLoadViewConfigsBean();
55 | data.setNoMoreDataText("没有更多数据啦~~");
56 | data.setOnFailedText("加载失败555~");
57 | data.setOnLoadingText("正在超级努力的加载更多啦~~");
58 | data.setOnSuccessText("加载成功啦,哒哒哒~");
59 | data.setOnLoadingTextColor(Color.GREEN);
60 | data.setOnFailedTextColor(Color.RED);
61 | data.setOnSuccessTextColor(Color.BLUE);
62 | data.setNoMoreDataTextColor(Color.LTGRAY);
63 | return data;
64 | });
65 | /**
66 | * 同理配置全局占位页
67 | * 仅对使用默认 占位页(DefaultPlaceholder) 控件生效
68 | */
69 | DefaultEmptyStatePage.Companion.createGlobalConfig(() -> {
70 | DefaultPlacePageConfigsBean data = new DefaultPlacePageConfigsBean();
71 | data.setEmptyText("似乎是空的");
72 | data.setErrorText("获取失败!\n请检查网络!");
73 | return data;
74 | });
75 | binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
76 | QuickBindAdapter adapter = new QuickBindAdapter(this);
77 |
78 | DefaultLoadView defaultLoadItem = new DefaultLoadView(this);
79 | defaultLoadItem.setNoMoreDataText("我滴任务完成啦~");
80 | defaultLoadItem.setOnWaitLoadingText("快碰我,我要更多!~");
81 | defaultLoadItem.setOnFailedText("跟我玩鹰滴是吧!");
82 | defaultLoadItem.setOnLoadingText("客官请稍等片刻~");
83 | defaultLoadItem.setOnSuccessText("轻轻松松~");
84 | //如果不设置字体颜色,会应用全局配置里的字体颜色。
85 | // defaultLoadItem.setOnLoadingTextColor();
86 | //设置默认的加载更多布局
87 | adapter.setLoadMoreItemView(defaultLoadItem);
88 | //是否启用 触底 自动触发 加载更多
89 | adapter.setAutoLoadMore(true);
90 | //是否启用 列表内容没有充满布局时 触发 加载更多
91 | //setAutoLoadMore(false)的时候,即使enableLoadMoreWhenPageNotFull(true)也不会生效
92 | adapter.enableLoadMoreWhenPageNotFull(true);
93 | //设置默认的 空列表 占位布局控制器
94 | adapter.setEmptyView(new DefaultEmptyStatePage(this));
95 | //绑定数据类型和对应的布局
96 | adapter.bind(String.class, R.layout.item_start, BR.data);
97 | //使用额外的item内容处理
98 | adapter.setQuickBind((binding, itemData, position) -> {
99 | //如果你想要在这里或者是在adapter中,写逻辑代码,可以这样:也可以单独写个类 实现 QuickBind 接口,然后传入这里
100 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
101 | //如果是多布局,则需要做下判断:
102 | if (binding instanceof ItemStartBinding) {
103 | // R.layout.item_start 类型布局,在这里面写这个布局的逻辑代码
104 | }
105 | //也可以这样:
106 | if (itemData instanceof String) {
107 | // R.layout.item_start 类型布局
108 | }
109 | return null;
110 | });
111 | //绑定列表item的点击事件
112 | adapter.setOnItemClickListener((adapter1, view, data, position) -> {
113 | Intent intent = new Intent();
114 | Class> mClass;
115 | switch (position) {
116 | case 1:
117 | //点击了 LinearLayout多布局 + 加载更多 这个item
118 | mClass = LinearMultiActivity.class;
119 | break;
120 | case 2:
121 | //点击了 GridLayout单布局 这个item
122 | mClass = GridSingleActivity.class;
123 | break;
124 | case 3:
125 | //点击了 GridLayout多布局 + 加载更多 这个item
126 | mClass = GridMultiActivity.class;
127 | break;
128 | case 4:
129 | //点击了 StaggeredGridLayout单布局 这个item
130 | mClass = StaggeredSingleActivity.class;
131 | break;
132 | case 5:
133 | //点击了 StaggeredGridLayout多布局 + 加载更多 这个item
134 | mClass = StaggeredMultiActivity.class;
135 | break;
136 | case 6:
137 | //点击了 空数据占位布局 这个item
138 | mClass = EmptyDemoActivity.class;
139 | break;
140 | default:
141 | //点击了 LinearLayout单布局 这个item
142 | mClass = LinearSingleActivity.class;
143 | break;
144 | }
145 | intent.setClass(this, mClass);
146 | startActivity(intent);
147 | return null;
148 | });
149 |
150 | //数据,这里为了模拟加载更多效果,使用LinkedList逐个加载数据
151 | LinkedList linkedList = new LinkedList<>();
152 | linkedList.add("LinearLayout单布局");
153 | linkedList.add("LinearLayout多布局 + 加载更多");
154 | linkedList.add("GridLayout单布局");
155 | linkedList.add("GridLayout多布局 + 加载更多");
156 | linkedList.add("StaggeredGridLayout单布局");
157 | linkedList.add("StaggeredGridLayout多布局 + 加载更多");
158 | linkedList.add("空数据占位布局");
159 | Iterator iterator = linkedList.iterator();
160 | //一秒后,添加一条数据
161 | binding.getRoot().postDelayed(() -> {
162 | adapter.addData(iterator.next());
163 | }, 1000);
164 |
165 | /**
166 | * 配置加载更多监听,如果配置了这个,即使没有调用过adapter.setLoadMoreItemView()方法,则自动使用默认的加载布局
167 | * 如果配置加载更多方法,等同于也调用了adapter.setLoadMoreItemView(new DefaultLoadView(this));
168 | */
169 | // adapter.setLoadMoreItemView(new DefaultLoadView(this));//设置加载更多的item样式
170 | adapter.setOnLoadMoreListener(() -> {//设置加载更多监听,触发加载更多的时候,则会执行里面的内容
171 | //每隔0.3s,增加一条数据
172 | binding.getRoot().postDelayed(() -> {
173 | if (iterator.hasNext()) {
174 | //建议是先调用loadMoreSuccess再设置数据
175 | adapter.addData(iterator.next());
176 | adapter.loadMoreSuccess();
177 | } else {
178 | adapter.loadMoreSuccessAndNoMore();
179 | }
180 | }, 300);
181 | return null;
182 | });
183 | //给列表绑定adapter适配器
184 | binding.recyclerView.setAdapter(adapter);
185 |
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.base;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.MenuItem;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 | import androidx.appcompat.app.ActionBar;
10 | import androidx.appcompat.app.AppCompatActivity;
11 | import androidx.databinding.DataBindingUtil;
12 | import androidx.databinding.ViewDataBinding;
13 |
14 | import com.xiaobin.bindingadapter.R;
15 |
16 | /**
17 | * @author 小斌
18 | * @data 2019/7/31
19 | **/
20 | public abstract class BaseActivity extends AppCompatActivity {
21 |
22 | protected VD binding;
23 |
24 | protected boolean showBackButton() {
25 | return false;
26 | }
27 |
28 | protected abstract int getLayoutId();
29 |
30 | protected String getActionTitle() {
31 | return getString(R.string.app_name);
32 | }
33 |
34 | protected abstract void initView(Bundle savedInstanceState);
35 |
36 | @Override
37 | protected void onCreate(@Nullable Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | binding = DataBindingUtil.inflate(LayoutInflater.from(this), getLayoutId(), null, false);
40 | setContentView(binding.getRoot());
41 |
42 | initView(savedInstanceState);
43 |
44 | ActionBar actionBar = getSupportActionBar();
45 | if (actionBar != null) {
46 | actionBar.setTitle(getActionTitle());
47 | if (showBackButton()) {
48 | actionBar.setDisplayHomeAsUpEnabled(true);
49 | } else {
50 | actionBar.setDisplayHomeAsUpEnabled(false);
51 | }
52 | }
53 | }
54 |
55 | @Override
56 | public boolean onOptionsItemSelected(@NonNull MenuItem item) {
57 | if (item.getItemId() == android.R.id.home) {
58 | finish();
59 | return true;
60 | }
61 | return super.onOptionsItemSelected(item);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/grid/GridMultiActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.grid;
2 |
3 | import android.os.Bundle;
4 | import android.widget.Toast;
5 |
6 | import androidx.recyclerview.widget.GridLayoutManager;
7 |
8 | import com.xiaobin.bindingadapter.BR;
9 | import com.xiaobin.bindingadapter.R;
10 | import com.xiaobin.bindingadapter.bean.ChatListBean;
11 | import com.xiaobin.bindingadapter.databinding.ActivityBaseBinding;
12 | import com.xiaobin.bindingadapter.databinding.ItemGridBinding;
13 | import com.xiaobin.bindingadapter.databinding.ItemHeadBinding;
14 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
15 | import com.xiaobin.quickbindadapter.ItemData;
16 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
17 |
18 | /**
19 | * GridLayoutManager 多布局示例
20 | *
21 | * @author xiaobin
22 | */
23 | public class GridMultiActivity extends BaseActivity {
24 |
25 | @Override
26 | protected boolean showBackButton() {
27 | return true;
28 | }
29 |
30 | @Override
31 | protected int getLayoutId() {
32 | return R.layout.activity_base;
33 | }
34 |
35 | @Override
36 | protected String getActionTitle() {
37 | return "GridLayout多布局";
38 | }
39 |
40 | @Override
41 | protected void initView(Bundle savedInstanceState) {
42 | QuickBindAdapter adapter = new QuickBindAdapter(this);
43 | //绑定数据类型和布局
44 | adapter.bind(ChatListBean.class, R.layout.item_grid, BR.data);
45 | adapter.bind(String.class, R.layout.item_head, BR.data);
46 | //添加子控件点击事件
47 | adapter.addClicks(ChatListBean.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
48 | adapter.addClicks(String.class, R.id.tv_name);
49 | GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
50 | gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
51 | @Override
52 | public int getSpanSize(int position) {
53 | Object itemData = adapter.getItemData(position);
54 | if (itemData instanceof ChatListBean) {
55 | return 1;
56 | } else if (itemData instanceof String) {
57 | return 3;
58 | }
59 | return 1;
60 | }
61 | });
62 | //倒序排列
63 | // gridLayoutManager.setReverseLayout(true);
64 | //务必先调用setLayoutManager
65 | binding.recyclerView.setLayoutManager(gridLayoutManager);
66 | binding.recyclerView.setAdapter(adapter);
67 |
68 | adapter.setOnLoadMoreListener(() -> {
69 | Toast.makeText(this, "加载更多触发", Toast.LENGTH_SHORT).show();
70 | binding.getRoot().postDelayed(() -> {
71 | ItemData dataList = new ItemData();
72 | ChatListBean item;
73 | for (int i = 0; i < 15; i++) {
74 | switch (i) {
75 | case 0:
76 | dataList.add("分组一");
77 | break;
78 | case 3:
79 | dataList.add("分组二");
80 | break;
81 | case 5:
82 | dataList.add("分组三");
83 | break;
84 | case 7:
85 | dataList.add("分组四");
86 | break;
87 | default:
88 | break;
89 | }
90 | item = new ChatListBean();
91 | item.setId(String.valueOf(i));
92 | dataList.add(item);
93 | }
94 | adapter.addData(dataList, true);
95 | if (adapter.getItemCount() > 50) {
96 | adapter.loadMoreSuccessAndNoMore();
97 | } else {
98 | adapter.loadMoreSuccess();
99 | }
100 | }, 1500);
101 | return null;
102 | });
103 |
104 | //如果你想要在这里或者是在adapter中,写逻辑代码,可以这样:也可以单独写个类 实现 QuickCovert接口,然后传入这里
105 | adapter.setQuickBind((binding, itemData, position) -> {
106 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
107 | //如果是多布局,则在这里需要判断布局类型,参考:
108 | if (binding instanceof ItemGridBinding) {
109 | // R.layout.item_grid 类型布局
110 | } else if (binding instanceof ItemHeadBinding) {
111 | // R.layout.item_head 类型布局
112 | }
113 | //也可以这样:
114 | if (itemData instanceof ChatListBean) {
115 | // R.layout.item_grid 类型布局
116 | } else if (itemData instanceof String) {
117 | // R.layout.item_head 类型布局
118 | }
119 | return null;
120 | });
121 | //绑定item的点击事件
122 | adapter.setOnItemClickListener((adapter1, view, data, position) -> {
123 | //item点击事件
124 | if (data instanceof ChatListBean) {
125 | ChatListBean mData = (ChatListBean) data;
126 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条", Toast.LENGTH_SHORT).show();
127 | } else if (data instanceof String) {
128 | Toast.makeText(this, "点击的是 " + data + " 分组条", Toast.LENGTH_SHORT).show();
129 | }
130 | return null;
131 | });
132 | adapter.setOnItemChildClickListener((adapter1, view, data, position) -> {
133 | //item上子控件 点击事件
134 | int viewId = view.getId();
135 | if (data instanceof ChatListBean) {
136 | ChatListBean mData = (ChatListBean) data;
137 | if (viewId == R.id.iv_image) {
138 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 头像", Toast.LENGTH_SHORT).show();
139 | } else if (viewId == R.id.tv_name) {
140 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 名字", Toast.LENGTH_SHORT).show();
141 | } else if (viewId == R.id.tv_message) {
142 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 消息", Toast.LENGTH_SHORT).show();
143 | }
144 | } else if (data instanceof String) {
145 | Toast.makeText(this, "点击的是 " + data + " 分组条的 TextView", Toast.LENGTH_SHORT).show();
146 | }
147 | return null;
148 | });
149 |
150 | //数据方式一:
151 | ItemData dataList = new ItemData();
152 | ChatListBean item;
153 | for (int i = 0; i < 15; i++) {
154 | switch (i) {
155 | case 0:
156 | dataList.add("分组一");
157 | break;
158 | case 3:
159 | dataList.add("分组二");
160 | break;
161 | case 5:
162 | dataList.add("分组三");
163 | break;
164 | case 7:
165 | dataList.add("分组四");
166 | break;
167 | default:
168 | break;
169 | }
170 | item = new ChatListBean();
171 | item.setId(String.valueOf(i));
172 | dataList.add(item);
173 | }
174 | adapter.setNewData(dataList);
175 |
176 | //数据方式二:
177 | // List dataList2 = new ArrayList<>();
178 | // for (int i = 0; i < 15; i++) {
179 | // dataList2.add(new ChatListBean());
180 | // }
181 | // adapter.setNewData(dataList2);
182 |
183 | //移除数据:
184 | // adapter.remove(adapter.getItemCount() - 1);
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/grid/GridSingleActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.grid;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.widget.Toast;
6 |
7 | import androidx.recyclerview.widget.GridLayoutManager;
8 |
9 | import com.xiaobin.bindingadapter.BR;
10 | import com.xiaobin.bindingadapter.R;
11 | import com.xiaobin.bindingadapter.bean.ChatListBean;
12 | import com.xiaobin.bindingadapter.databinding.ActivityBaseBinding;
13 | import com.xiaobin.bindingadapter.databinding.ItemGridBinding;
14 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
15 | import com.xiaobin.quickbindadapter.ItemData;
16 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
17 |
18 | /**
19 | * GridLayoutManager 示例
20 | * @author xiaobin
21 | */
22 | public class GridSingleActivity extends BaseActivity {
23 |
24 | @Override
25 | protected boolean showBackButton() {
26 | return true;
27 | }
28 |
29 | @Override
30 | protected int getLayoutId() {
31 | return R.layout.activity_base;
32 | }
33 |
34 | @Override
35 | protected String getActionTitle() {
36 | return "GridLayout单布局";
37 | }
38 |
39 | @Override
40 | protected void initView(Bundle savedInstanceState) {
41 | QuickBindAdapter adapter = new QuickBindAdapter(this);
42 | //绑定数据类型和布局
43 | adapter.bind(ChatListBean.class, R.layout.item_grid, BR.data);
44 | //添加子控件点击事件
45 | adapter.addClicks(ChatListBean.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
46 |
47 | GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
48 | gridLayoutManager.setReverseLayout(false);
49 | binding.recyclerView.setLayoutManager(gridLayoutManager);
50 | binding.recyclerView.setAdapter(adapter);
51 | //如果你想要在这里或者是在adapter中,写逻辑代码,可以这样:也可以单独写个类 实现 QuickCovert接口,然后传入这里
52 | adapter.setQuickBind((binding, itemData, position) -> {
53 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
54 | ItemGridBinding mBinding = (ItemGridBinding) binding;
55 | mBinding.tvName.setTextColor(Color.RED);
56 | return null;
57 | });
58 | //绑定item的点击事件
59 | adapter.setOnItemClickListener((adapter1, view, data, position) -> {
60 | //item点击事件
61 | ChatListBean itemData = (ChatListBean) data;
62 | Toast.makeText(this, "ID: " + itemData.getId(), Toast.LENGTH_SHORT).show();
63 | return null;
64 | });
65 | adapter.setOnItemChildClickListener((adapter1, view, data, position) -> {
66 | //item上子控件 点击事件
67 | ChatListBean itemData = (ChatListBean) data;
68 | int viewId = view.getId();
69 | if (viewId == R.id.iv_image) {
70 | Toast.makeText(this, "点击的是ID: " + itemData.getId() + " 的 头像", Toast.LENGTH_SHORT).show();
71 | } else if (viewId == R.id.tv_name) {
72 | Toast.makeText(this, "点击的是ID: " + itemData.getId() + " 的 名字", Toast.LENGTH_SHORT).show();
73 | } else if (viewId == R.id.tv_message) {
74 | Toast.makeText(this, "点击的是ID: " + itemData.getId() + " 的 消息", Toast.LENGTH_SHORT).show();
75 | }
76 | return null;
77 | });
78 |
79 | //数据方式一:
80 | ItemData dataList = new ItemData();
81 | ChatListBean item;
82 | for (int i = 0; i < 15; i++) {
83 | item = new ChatListBean();
84 | item.setId(String.valueOf(i));
85 | dataList.add(item);
86 | }
87 | adapter.setNewData(dataList);
88 |
89 | //数据方式二:
90 | // List dataList2 = new ArrayList<>();
91 | // for (int i = 0; i < 15; i++) {
92 | // item = new ChatListBean();
93 | // item.setId(String.valueOf(i));
94 | // dataList2.add(item);
95 | // }
96 | // adapter.setNewData(dataList2);
97 |
98 | //添加数据:
99 | item = new ChatListBean();
100 | item.setId("嘿咻");
101 | adapter.addData(item);
102 |
103 | //移除数据:
104 | // adapter.remove(adapter.getItemCount() - 1);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/linear/LinearMultiActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.linear;
2 |
3 | import android.os.Bundle;
4 | import android.util.Log;
5 | import android.view.View;
6 | import android.widget.Toast;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.recyclerview.widget.LinearLayoutManager;
10 | import androidx.recyclerview.widget.RecyclerView;
11 |
12 | import com.xiaobin.bindingadapter.BR;
13 | import com.xiaobin.bindingadapter.R;
14 | import com.xiaobin.bindingadapter.bean.ChatListBean;
15 | import com.xiaobin.bindingadapter.databinding.ActivityBaseBinding;
16 | import com.xiaobin.bindingadapter.databinding.ItemHeadBinding;
17 | import com.xiaobin.bindingadapter.databinding.ItemLinearBinding;
18 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
19 | import com.xiaobin.quickbindadapter.ItemData;
20 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
21 |
22 | /**
23 | * LinearLayoutManager 多布局示例
24 | *
25 | * @author xiaobin
26 | */
27 | public class LinearMultiActivity extends BaseActivity {
28 |
29 | @Override
30 | protected boolean showBackButton() {
31 | return true;
32 | }
33 |
34 | @Override
35 | protected int getLayoutId() {
36 | return R.layout.activity_base;
37 | }
38 |
39 | @Override
40 | protected String getActionTitle() {
41 | return "LinearLayout多布局";
42 | }
43 |
44 | @Override
45 | protected void initView(Bundle savedInstanceState) {
46 | QuickBindAdapter adapter = new QuickBindAdapter(this);
47 | //绑定数据类型和布局
48 | adapter.bind(ChatListBean.class, R.layout.item_linear, BR.data);
49 | adapter.bind(String.class, R.layout.item_head, BR.data);
50 | //添加子控件点击事件
51 | adapter.addClicks(ChatListBean.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
52 | adapter.addClicks(String.class, R.id.tv_name);
53 |
54 | //务必先调用setLayoutManager
55 | binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
56 | binding.recyclerView.setAdapter(adapter);
57 | adapter.setOnLoadMoreListener(() -> {
58 | Toast.makeText(this, "加载更多触发", Toast.LENGTH_SHORT).show();
59 | binding.getRoot().postDelayed(() -> {
60 | ItemData dataList = new ItemData();
61 | ChatListBean item;
62 | for (int i = 0; i < 15; i++) {
63 | switch (i) {
64 | case 0:
65 | dataList.add("分组一");
66 | break;
67 | case 3:
68 | dataList.add("分组二");
69 | break;
70 | case 5:
71 | dataList.add("分组三");
72 | break;
73 | case 7:
74 | dataList.add("分组四");
75 | break;
76 | default:
77 | break;
78 | }
79 | item = new ChatListBean();
80 | item.setId(String.valueOf(i));
81 | dataList.add(item);
82 | }
83 | adapter.addData(dataList);
84 | Log.d("TTTTT", "initView: 拿到数据,调用addData()");
85 | if (adapter.getItemCount() > 50) {
86 | adapter.loadMoreSuccessAndNoMore();
87 | } else {
88 | adapter.loadMoreSuccess();
89 | }
90 | }, 1500);
91 | return null;
92 | });
93 | //如果你想要在这里或者是在adapter中,写逻辑代码,可以这样:也可以单独写个类 实现 QuickCovert接口,然后传入这里
94 | adapter.setQuickBind((binding, itemData, position) -> {
95 | //如果是多布局,则在这里需要判断布局类型,参考:
96 | if (binding instanceof ItemLinearBinding) {
97 | // R.layout.item_linear 类型布局,在这里面写这个布局的逻辑代码
98 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
99 | } else if (binding instanceof ItemHeadBinding) {
100 | // R.layout.item_head 类型布局
101 | }
102 | //也可以这样:
103 | if (itemData instanceof ChatListBean) {
104 | // R.layout.item_linear 类型布局
105 | } else if (itemData instanceof String) {
106 | // R.layout.item_head 类型布局
107 | }
108 | return null;
109 | });
110 | //绑定item的点击事件
111 | adapter.setOnItemClickListener((adapter1, view, data, position) -> {
112 | //item点击事件
113 | if (data instanceof ChatListBean) {
114 | ChatListBean mData = (ChatListBean) data;
115 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条", Toast.LENGTH_SHORT).show();
116 | } else if (data instanceof String) {
117 | Toast.makeText(this, "点击的是 " + data + " 分组条", Toast.LENGTH_SHORT).show();
118 | }
119 | return null;
120 | });
121 | adapter.setOnItemChildClickListener((adapter1, view, data, position) -> {
122 | //item上子控件 点击事件
123 | int viewId = view.getId();
124 | if (data instanceof ChatListBean) {
125 | ChatListBean mData = (ChatListBean) data;
126 | if (viewId == R.id.iv_image) {
127 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 头像", Toast.LENGTH_SHORT).show();
128 | } else if (viewId == R.id.tv_name) {
129 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 名字", Toast.LENGTH_SHORT).show();
130 | } else if (viewId == R.id.tv_message) {
131 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 消息", Toast.LENGTH_SHORT).show();
132 | }
133 | } else if (data instanceof String) {
134 | Toast.makeText(this, "点击的是 " + data + " 分组条的 TextView", Toast.LENGTH_SHORT).show();
135 | }
136 |
137 | return null;
138 | });
139 |
140 | //数据方式一:
141 | ItemData dataList = new ItemData();
142 | ChatListBean item;
143 | for (int i = 0; i < 15; i++) {
144 | switch (i) {
145 | case 0:
146 | dataList.add("分组一");
147 | break;
148 | case 3:
149 | dataList.add("分组二");
150 | break;
151 | case 5:
152 | dataList.add("分组三");
153 | break;
154 | case 7:
155 | dataList.add("分组四");
156 | break;
157 | default:
158 | break;
159 | }
160 | item = new ChatListBean();
161 | item.setId(String.valueOf(i));
162 | dataList.add(item);
163 | }
164 | adapter.setNewData(dataList);
165 |
166 | //数据方式二:
167 | // List dataList2 = new ArrayList<>();
168 | // for (int i = 0; i < 15; i++) {
169 | // dataList2.add(new ChatListBean());
170 | // }
171 | // adapter.setNewData(dataList2);
172 |
173 | //添加数据:
174 | adapter.addData("分组五");
175 | adapter.addData(new ChatListBean());
176 |
177 | //移除数据:
178 | // adapter.remove(adapter.getItemCount() - 1);
179 | }
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/linear/LinearSingleActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.linear;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.widget.Toast;
6 |
7 | import androidx.recyclerview.widget.LinearLayoutManager;
8 |
9 | import com.xiaobin.bindingadapter.BR;
10 | import com.xiaobin.bindingadapter.R;
11 | import com.xiaobin.bindingadapter.bean.ChatListBean;
12 | import com.xiaobin.bindingadapter.databinding.ActivityBaseBinding;
13 | import com.xiaobin.bindingadapter.databinding.ItemLinearBinding;
14 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
15 | import com.xiaobin.quickbindadapter.ItemData;
16 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
17 |
18 | /**
19 | * LinearLayoutManager 示例
20 | *
21 | * @author xiaobin
22 | */
23 | public class LinearSingleActivity extends BaseActivity {
24 |
25 | @Override
26 | protected boolean showBackButton() {
27 | return true;
28 | }
29 |
30 | @Override
31 | protected int getLayoutId() {
32 | return R.layout.activity_base;
33 | }
34 |
35 | @Override
36 | protected String getActionTitle() {
37 | return "LinearLayout单布局";
38 | }
39 |
40 | @Override
41 | protected void initView(Bundle savedInstanceState) {
42 | QuickBindAdapter adapter = new QuickBindAdapter(this);
43 | //绑定数据类型和布局
44 | adapter.bind(ChatListBean.class, R.layout.item_linear, BR.data);
45 | //添加子控件点击事件
46 | adapter.addClicks(ChatListBean.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
47 | binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
48 | binding.recyclerView.setAdapter(adapter);
49 | //如果你想要在这里或者是在adapter中,写逻辑代码,可以这样:也可以单独写个类 实现 QuickCovert接口,然后传入这里
50 | adapter.setQuickBind((binding, itemData, position) -> {
51 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
52 | ItemLinearBinding mBinding = (ItemLinearBinding) binding;
53 | mBinding.tvName.setTextColor(Color.RED);
54 | return null;
55 | });
56 | //绑定item的点击事件
57 | adapter.setOnItemClickListener((adapter1, view, data, position) -> {
58 | //item点击事件
59 | ChatListBean itemData = (ChatListBean) data;
60 | Toast.makeText(this, "Index: " + position + ", ID: " + itemData.getId(), Toast.LENGTH_SHORT).show();
61 | return null;
62 | });
63 | adapter.setOnItemChildClickListener((adapter1, view, data, position) -> {
64 | //item上子控件 点击事件
65 | ChatListBean itemData = (ChatListBean) data;
66 | int viewId = view.getId();
67 | if (viewId == R.id.iv_image) {
68 | Toast.makeText(this, "点击的是Index: " + position + ", ID: " + itemData.getId() + " 的 头像", Toast.LENGTH_SHORT).show();
69 | } else if (viewId == R.id.tv_name) {
70 | Toast.makeText(this, "点击的是Index: " + position + ", ID: " + itemData.getId() + " 的 名字", Toast.LENGTH_SHORT).show();
71 | } else if (viewId == R.id.tv_message) {
72 | Toast.makeText(this, "点击的是Index: " + position + ", ID: " + itemData.getId() + " 的 消息", Toast.LENGTH_SHORT).show();
73 | }
74 | return null;
75 | });
76 | adapter.setOnItemLongClickListener((adapter1, view, data, position) -> {
77 | //item长按事件,长按删除这个item
78 | // ChatListBean itemData = (ChatListBean) data;
79 | // adapter.remove(position);
80 | // Toast.makeText(this, "删除index: " + position + ", ID:" + itemData.getId(), Toast.LENGTH_SHORT).show();
81 |
82 | //item长按事件,在这个位置插入一个新的item
83 | ChatListBean newData = new ChatListBean();
84 | String s = String.valueOf(System.currentTimeMillis());
85 | newData.setId(s.substring(s.length() -3));
86 | adapter.insertData(position, newData);
87 | Toast.makeText(this, "插入index: " + position + ", ID:" + newData.getId(), Toast.LENGTH_SHORT).show();
88 | return true;
89 | });
90 |
91 | //数据方式一:
92 | ItemData dataList = new ItemData();
93 | ChatListBean item;
94 | for (int i = 0; i < 15; i++) {
95 | item = new ChatListBean();
96 | item.setId(String.valueOf(i));
97 | dataList.add(item);
98 | }
99 | adapter.setNewData(dataList);
100 |
101 | //数据方式二:
102 | // List dataList2 = new ArrayList<>();
103 | // for (int i = 0; i < 15; i++) {
104 | // item = new ChatListBean();
105 | // item.setId(String.valueOf(i));
106 | // dataList2.add(item);
107 | // }
108 | // adapter.setNewData(dataList2);
109 |
110 | //添加数据:
111 | item = new ChatListBean();
112 | item.setId("嘿咻");
113 | adapter.addData(item);
114 |
115 | //移除数据:
116 | // adapter.remove(adapter.getItemCount() - 1);
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/pageState/MyEmptyPageView.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.pageState;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.xiaobin.bindingadapter.R;
6 | import com.xiaobin.bindingadapter.databinding.LayoutMyPageEmptyBinding;
7 | import com.xiaobin.bindingadapter.databinding.LayoutMyPageErrBinding;
8 | import com.xiaobin.bindingadapter.databinding.LayoutMyPageLoadingBinding;
9 | import com.xiaobin.quickbindadapter.view.BasePageStateView;
10 | import com.xiaobin.quickbindadapter.view.PageState;
11 |
12 | /**
13 | * 自定义空数据占位布局
14 | */
15 | public class MyEmptyPageView extends BasePageStateView<
16 | LayoutMyPageEmptyBinding,
17 | LayoutMyPageErrBinding,
18 | LayoutMyPageLoadingBinding> {
19 |
20 | public MyEmptyPageView() {
21 | super(R.layout.layout_my_page_empty,
22 | R.layout.layout_my_page_err,
23 | R.layout.layout_my_page_loading);
24 | }
25 |
26 | @Override
27 | protected void onPageCreate(@NonNull PageState action) {
28 |
29 | }
30 |
31 | @Override
32 | protected void onActionCall(@NonNull PageState action) {
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/pageState/MyLoadMoreView.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.pageState;
2 |
3 | import android.view.View;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.Nullable;
7 |
8 | import com.xiaobin.bindingadapter.R;
9 | import com.xiaobin.bindingadapter.databinding.LayoutMyLoadingMoreBinding;
10 | import com.xiaobin.quickbindadapter.view.BaseLoadView;
11 |
12 | /**
13 | * 自定义加载更多item
14 | */
15 | public class MyLoadMoreView extends BaseLoadView {
16 |
17 | public MyLoadMoreView() {
18 | super(R.layout.layout_my_loading_more);
19 | }
20 |
21 | @Override
22 | protected void initView(@Nullable LayoutMyLoadingMoreBinding loadView) {
23 | if (loadView == null) return;
24 | loadView.ivImage.setVisibility(View.GONE);
25 | loadView.progress.setVisibility(View.VISIBLE);
26 | loadView.tvLabel.setText("正在努力加载中。。。");
27 | }
28 |
29 | protected void onLoading(LayoutMyLoadingMoreBinding loadView) {
30 | if (loadView == null) return;
31 | loadView.ivImage.setVisibility(View.GONE);
32 | loadView.progress.setVisibility(View.VISIBLE);
33 | loadView.tvLabel.setText("正在努力加载中。。。");
34 | }
35 |
36 | protected void onNoMoreData(LayoutMyLoadingMoreBinding loadView) {
37 | if (loadView == null) return;
38 | loadView.ivImage.setVisibility(View.VISIBLE);
39 | loadView.ivImage.setImageResource(R.drawable.ic_svg_ok);
40 | loadView.progress.setVisibility(View.GONE);
41 | loadView.tvLabel.setText("全都加载完了");
42 | }
43 |
44 | protected void onLoadSuccess(LayoutMyLoadingMoreBinding loadView) {
45 | if (loadView == null) return;
46 | loadView.ivImage.setVisibility(View.VISIBLE);
47 | loadView.ivImage.setImageResource(R.drawable.ic_svg_ok);
48 | loadView.progress.setVisibility(View.GONE);
49 | loadView.tvLabel.setText("加载成功");
50 | }
51 |
52 | protected void onLoadFailed(LayoutMyLoadingMoreBinding loadView) {
53 | if (loadView == null) return;
54 | loadView.ivImage.setVisibility(View.VISIBLE);
55 | loadView.ivImage.setImageResource(R.drawable.error_image);
56 | loadView.progress.setVisibility(View.GONE);
57 | loadView.tvLabel.setText("加载失败");
58 | }
59 |
60 | protected void onWaitLoading(LayoutMyLoadingMoreBinding loadView) {
61 | if (loadView == null) return;
62 | loadView.ivImage.setVisibility(View.VISIBLE);
63 | loadView.ivImage.setImageResource(R.drawable.error_image);
64 | loadView.progress.setVisibility(View.GONE);
65 | loadView.tvLabel.setText("点击加载更多");
66 | }
67 |
68 | @Override
69 | protected void onStateChange(LayoutMyLoadingMoreBinding loadView, @NonNull LoadMoreState state) {
70 | switch (state) {
71 | case LOADING:
72 | onLoading(loadView);
73 | break;
74 | case SUCCESS:
75 | onLoadSuccess(loadView);
76 | break;
77 | case FAILED:
78 | onLoadFailed(loadView);
79 | break;
80 | case NO_MORE:
81 | onNoMoreData(loadView);
82 | break;
83 | case WAIT_LOADING:
84 | onWaitLoading(loadView);
85 | break;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/staggered/StaggeredMultiActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.staggered;
2 |
3 | import android.os.Bundle;
4 | import android.widget.Toast;
5 |
6 | import androidx.recyclerview.widget.StaggeredGridLayoutManager;
7 |
8 | import com.xiaobin.bindingadapter.BR;
9 | import com.xiaobin.bindingadapter.R;
10 | import com.xiaobin.bindingadapter.bean.ChatListBean;
11 | import com.xiaobin.bindingadapter.bean.ChatListBean2;
12 | import com.xiaobin.bindingadapter.bean.StaggeredHeadBean;
13 | import com.xiaobin.bindingadapter.databinding.ActivityBaseBinding;
14 | import com.xiaobin.bindingadapter.databinding.ItemStaggered2Binding;
15 | import com.xiaobin.bindingadapter.databinding.ItemStaggeredBinding;
16 | import com.xiaobin.bindingadapter.databinding.ItemStaggeredHeadBinding;
17 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
18 | import com.xiaobin.quickbindadapter.ItemData;
19 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
20 |
21 | /**
22 | * StaggeredLayoutManager 多布局示例
23 | *
24 | * @author xiaobin
25 | */
26 | public class StaggeredMultiActivity extends BaseActivity {
27 |
28 | @Override
29 | protected boolean showBackButton() {
30 | return true;
31 | }
32 |
33 | @Override
34 | protected int getLayoutId() {
35 | return R.layout.activity_base;
36 | }
37 |
38 | @Override
39 | protected String getActionTitle() {
40 | return "StaggeredGridLayout多布局";
41 | }
42 |
43 | @Override
44 | protected void initView(Bundle savedInstanceState) {
45 | QuickBindAdapter adapter = new QuickBindAdapter(this);
46 | //绑定数据类型和布局
47 | adapter.bind(ChatListBean.class, R.layout.item_staggered, BR.data);
48 | adapter.bind(ChatListBean2.class, R.layout.item_staggered2, BR.data);
49 | /**
50 | * 这是一个横跨整个屏幕宽度的头部
51 | * 因为StaggeredGridLayoutManager没有提供GridLayoutManager的spanLookup接口
52 | * 所以这里想要实现这个效果,只需要将需要当做头部的布局所对应的数据包装一下实体类并且务必实现StaggeredFullSpan接口
53 | * 否则不生效
54 | * 参考:StaggeredHeadBean 类
55 | */
56 | adapter.bind(StaggeredHeadBean.class, R.layout.item_staggered_head, BR.data);
57 | //添加子控件点击事件
58 | adapter.addClicks(ChatListBean.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
59 | adapter.addClicks(ChatListBean2.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
60 | adapter.addClicks(StaggeredHeadBean.class, R.id.tv_name);
61 |
62 | //务必先调用setLayoutManager
63 | binding.recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
64 | binding.recyclerView.setAdapter(adapter);
65 |
66 | adapter.setOnLoadMoreListener(() -> {
67 | Toast.makeText(this, "加载更多触发", Toast.LENGTH_SHORT).show();
68 | binding.getRoot().postDelayed(() -> {
69 | adapter.addData(createSomeData(), true);
70 | if (adapter.getItemCount() > 50) {
71 | adapter.loadMoreSuccessAndNoMore();
72 | } else {
73 | adapter.loadMoreSuccess();
74 | }
75 | }, 1500);
76 | return null;
77 | });
78 |
79 | //如果你想要在这里或者是在adapter中,写逻辑代码,可以这样:也可以单独写个类 实现 QuickCovert接口,然后传入这里
80 | adapter.setQuickBind((binding, itemData, position) -> {
81 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
82 | //如果是多布局,则在这里需要判断布局类型,参考:
83 | if (binding instanceof ItemStaggeredBinding) {
84 | // R.layout.item_staggered 类型布局
85 | } else if (binding instanceof ItemStaggered2Binding) {
86 | // R.layout.item_staggered2 类型布局
87 | } else if (binding instanceof ItemStaggeredHeadBinding) {
88 | // R.layout.item_staggered_head 类型布局
89 | }
90 | //也可以这样:
91 | if (itemData instanceof ChatListBean) {
92 | // R.layout.item_staggered 类型布局
93 | } else if (itemData instanceof ChatListBean2) {
94 | // R.layout.item_staggered2 类型布局
95 | } else if (itemData instanceof StaggeredHeadBean) {
96 | // R.layout.item_staggered_head 类型布局
97 | }
98 | return null;
99 | });
100 | //绑定item的点击事件
101 | adapter.setOnItemClickListener((adapter1, view, data, position) -> {
102 | //item点击事件
103 | if (data instanceof ChatListBean) {
104 | ChatListBean mData = (ChatListBean) data;
105 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条", Toast.LENGTH_SHORT).show();
106 | } else if (data instanceof ChatListBean2) {
107 | ChatListBean2 mData = (ChatListBean2) data;
108 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条", Toast.LENGTH_SHORT).show();
109 | } else if (data instanceof StaggeredHeadBean) {
110 | StaggeredHeadBean mData = (StaggeredHeadBean) data;
111 | Toast.makeText(this, "点击的是 " + mData.getTitle() + " 分组条", Toast.LENGTH_SHORT).show();
112 | }
113 | return null;
114 | });
115 | adapter.setOnItemChildClickListener((adapter1, view, data, position) -> {
116 | //item上子控件 点击事件
117 | int viewId = view.getId();
118 | if (data instanceof ChatListBean) {
119 | ChatListBean mData = (ChatListBean) data;
120 | if (viewId == R.id.iv_image) {
121 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 头像", Toast.LENGTH_SHORT).show();
122 | } else if (viewId == R.id.tv_name) {
123 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 名字", Toast.LENGTH_SHORT).show();
124 | } else if (viewId == R.id.tv_message) {
125 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 消息", Toast.LENGTH_SHORT).show();
126 | }
127 | } else if (data instanceof ChatListBean2) {
128 | ChatListBean2 mData = (ChatListBean2) data;
129 | if (viewId == R.id.iv_image) {
130 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 头像", Toast.LENGTH_SHORT).show();
131 | } else if (viewId == R.id.tv_name) {
132 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 名字", Toast.LENGTH_SHORT).show();
133 | } else if (viewId == R.id.tv_message) {
134 | Toast.makeText(this, "点击的是 " + mData.getId() + " 消息条的 消息", Toast.LENGTH_SHORT).show();
135 | }
136 | } else if (data instanceof StaggeredHeadBean) {
137 | StaggeredHeadBean mData = (StaggeredHeadBean) data;
138 | Toast.makeText(this, "点击的是 " + mData.getTitle() + " 分组条的 TextView", Toast.LENGTH_SHORT).show();
139 | }
140 |
141 | return null;
142 | });
143 |
144 | //数据方式一:
145 | adapter.setNewData(createSomeData());
146 |
147 | //数据方式二:
148 | // List dataList2 = new ArrayList<>();
149 | // for (int i = 0; i < 15; i++) {
150 | // dataList2.add(new ChatListBean());
151 | // }
152 | // adapter.setNewData(dataList2);
153 |
154 | //添加数据:
155 | adapter.addData("分组五");
156 | adapter.addData(new ChatListBean());
157 |
158 | //移除数据:
159 | // adapter.remove(adapter.getItemCount() - 1);
160 | }
161 |
162 | private ItemData createSomeData() {
163 | ItemData dataList = new ItemData();
164 | for (int i = 0; i < 15; i++) {
165 | switch (i) {
166 | case 0:
167 | dataList.add(new StaggeredHeadBean("分组一"));
168 | break;
169 | case 3:
170 | dataList.add(new StaggeredHeadBean("分组二"));
171 | break;
172 | case 5:
173 | dataList.add(new StaggeredHeadBean("分组三"));
174 | break;
175 | case 7:
176 | dataList.add(new StaggeredHeadBean("分组四"));
177 | break;
178 | default:
179 | break;
180 | }
181 | if (i % 3 == 0) {
182 | ChatListBean2 item = new ChatListBean2();
183 | item.setId(String.valueOf(i));
184 | dataList.add(item);
185 | } else {
186 | ChatListBean item = new ChatListBean();
187 | item.setId(String.valueOf(i));
188 | dataList.add(item);
189 | }
190 | }
191 | return dataList;
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/ui/staggered/StaggeredSingleActivity.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.ui.staggered;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.widget.Toast;
6 |
7 | import androidx.recyclerview.widget.StaggeredGridLayoutManager;
8 |
9 | import com.xiaobin.bindingadapter.BR;
10 | import com.xiaobin.bindingadapter.R;
11 | import com.xiaobin.bindingadapter.bean.ChatListBean;
12 | import com.xiaobin.bindingadapter.databinding.ActivityBaseBinding;
13 | import com.xiaobin.bindingadapter.databinding.ItemStaggeredBinding;
14 | import com.xiaobin.bindingadapter.ui.base.BaseActivity;
15 | import com.xiaobin.quickbindadapter.ItemData;
16 | import com.xiaobin.quickbindadapter.QuickBindAdapter;
17 |
18 | /**
19 | * StaggeredLayoutManager 示例
20 | * @author xiaobin
21 | */
22 | public class StaggeredSingleActivity extends BaseActivity {
23 |
24 | @Override
25 | protected boolean showBackButton() {
26 | return true;
27 | }
28 |
29 | @Override
30 | protected int getLayoutId() {
31 | return R.layout.activity_base;
32 | }
33 |
34 | @Override
35 | protected String getActionTitle() {
36 | return "StaggeredGridLayout单布局";
37 | }
38 |
39 | @Override
40 | protected void initView(Bundle savedInstanceState) {
41 | QuickBindAdapter adapter = new QuickBindAdapter(this);
42 | //绑定数据类型和布局
43 | adapter.bind(ChatListBean.class, R.layout.item_staggered, BR.data);
44 | //添加子控件点击事件
45 | adapter.addClicks(ChatListBean.class, R.id.iv_image, R.id.tv_name, R.id.tv_message);
46 |
47 | binding.recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
48 | binding.recyclerView.setAdapter(adapter);
49 | //如果你想要在这里或者是在adapter中,写逻辑代码,可以这样:也可以单独写个类 实现 QuickCovert接口,然后传入这里
50 | adapter.setQuickBind((binding, itemData, position) -> {
51 | // binding 是这个item本身,itemData 是这个item的数据,position 是这个item所在列表中的位置
52 | ItemStaggeredBinding mBinding = (ItemStaggeredBinding) binding;
53 | mBinding.tvName.setTextColor(Color.RED);
54 | return null;
55 | });
56 | //绑定item的点击事件
57 | adapter.setOnItemClickListener((adapter1, view, data, position) -> {
58 | //item点击事件
59 | ChatListBean itemData = (ChatListBean) data;
60 | Toast.makeText(this, "ID: " + itemData.getId(), Toast.LENGTH_SHORT).show();
61 | return null;
62 | });
63 | adapter.setOnItemChildClickListener((adapter1, view, data, position) -> {
64 | //item上子控件 点击事件
65 | ChatListBean itemData = (ChatListBean) data;
66 | int viewId = view.getId();
67 | if (viewId == R.id.iv_image) {
68 | Toast.makeText(this, "点击的是ID: " + itemData.getId() + " 的 头像", Toast.LENGTH_SHORT).show();
69 | } else if (viewId == R.id.tv_name) {
70 | Toast.makeText(this, "点击的是ID: " + itemData.getId() + " 的 名字", Toast.LENGTH_SHORT).show();
71 | } else if (viewId == R.id.tv_message) {
72 | Toast.makeText(this, "点击的是ID: " + itemData.getId() + " 的 消息", Toast.LENGTH_SHORT).show();
73 | }
74 | return null;
75 | });
76 |
77 | //数据方式一:
78 | ItemData dataList = new ItemData();
79 | ChatListBean item;
80 | for (int i = 0; i < 15; i++) {
81 | item = new ChatListBean();
82 | item.setId(String.valueOf(i));
83 | dataList.add(item);
84 | }
85 | adapter.setNewData(dataList);
86 |
87 | //数据方式二:
88 | // List dataList2 = new ArrayList<>();
89 | // for (int i = 0; i < 15; i++) {
90 | // item = new ChatListBean();
91 | // item.setId(String.valueOf(i));
92 | // dataList2.add(item);
93 | // }
94 | // adapter.setNewData(dataList2);
95 |
96 | //添加数据:
97 | item = new ChatListBean();
98 | item.setId("嘿咻");
99 | adapter.addData(item);
100 |
101 | //移除数据:
102 | // adapter.remove(adapter.getItemCount() - 1);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xiaobin/bindingadapter/utils/BindAdapterUtils.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter.utils;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.drawable.ColorDrawable;
5 | import android.view.View;
6 | import android.widget.ImageView;
7 |
8 | import androidx.annotation.DrawableRes;
9 | import androidx.core.content.ContextCompat;
10 | import androidx.databinding.BindingAdapter;
11 |
12 | import com.bumptech.glide.Glide;
13 | import com.bumptech.glide.request.RequestOptions;
14 |
15 | /**
16 | * @author 小斌
17 | * @data 2019/7/30
18 | **/
19 | public class BindAdapterUtils {
20 |
21 | @BindingAdapter("circleImage")
22 | public static void circleImage(View view, String url) {
23 | Glide.with(view.getContext())
24 | .load(url)
25 | .apply(RequestOptions.circleCropTransform()
26 | .error(new ColorDrawable(Color.WHITE))
27 | .fallback(new ColorDrawable(Color.RED)))
28 | .into((ImageView) view);
29 | }
30 |
31 | @BindingAdapter("circleImage")
32 | public static void circleImage(View view, @DrawableRes int ids) {
33 | Glide.with(view.getContext())
34 | .load(ContextCompat.getDrawable(view.getContext(), ids))
35 | .apply(RequestOptions.circleCropTransform()
36 | .error(new ColorDrawable(Color.WHITE))
37 | .fallback(new ColorDrawable(Color.RED)))
38 | .into((ImageView) view);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/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/error_image.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_svg_ban.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_svg_ok.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shap_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_base.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
19 |
20 |
34 |
35 |
49 |
50 |
65 |
66 |
80 |
81 |
95 |
96 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_grid.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
19 |
20 |
23 |
24 |
33 |
34 |
50 |
51 |
63 |
64 |
76 |
77 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_head.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
19 |
20 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_linear.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
19 |
20 |
23 |
24 |
33 |
34 |
49 |
50 |
61 |
62 |
74 |
75 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_staggered.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
19 |
20 |
23 |
24 |
34 |
35 |
51 |
52 |
64 |
65 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_staggered2.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
19 |
20 |
23 |
24 |
34 |
35 |
51 |
52 |
64 |
65 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_staggered_head.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
19 |
20 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
20 |
21 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_my_loading_more.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
24 |
25 |
33 |
34 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_my_page_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
24 |
25 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_my_page_err.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
25 |
26 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_my_page_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
24 |
25 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/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/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | QuickBindAdapter
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/xiaobin/bindingadapter/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.xiaobin.bindingadapter;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.9.0'
5 | repositories {
6 | maven { url = "https://jitpack.io" }
7 | maven { url = "https://maven.google.com" }
8 | google()
9 | mavenCentral()
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:8.1.1'
13 | // classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
15 | // NOTE: Do not place your application dependencies here; they belong
16 | // in the individual module build.gradle files
17 | }
18 | }
19 |
20 | allprojects {
21 | repositories {
22 | maven { url = "https://jitpack.io" }
23 | maven { url = "https://maven.google.com" }
24 | google()
25 | mavenCentral()
26 | }
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | android.defaults.buildfeatures.buildconfig=true
21 | android.nonTransitiveRClass=false
22 | android.nonFinalResIds=false
23 |
24 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matcha-xiaobin/QuickBindAdapter/5dc0b680f921e32535a13f9098817267866276eb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 19 15:38:08 CST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/quickbindadapter/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/quickbindadapter/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | id 'kotlin-kapt'
5 | }
6 | group = 'com.github.Matchas-xiaobin'
7 |
8 | android {
9 | namespace 'com.xiaobin.quickbindadapter'
10 | compileSdk 34
11 |
12 | defaultConfig {
13 | minSdk 14
14 | targetSdk 34
15 | }
16 |
17 | compileOptions {
18 | sourceCompatibility JavaVersion.VERSION_17
19 | targetCompatibility JavaVersion.VERSION_17
20 | }
21 | buildFeatures {
22 | dataBinding true
23 | }
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 | implementation("androidx.core:core-ktx:1.10.1")
35 | implementation("androidx.appcompat:appcompat:1.6.1")
36 | implementation("androidx.recyclerview:recyclerview:1.3.1")
37 | implementation("androidx.paging:paging-runtime-ktx:3.2.0")
38 | }
39 |
--------------------------------------------------------------------------------
/quickbindadapter/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 |
--------------------------------------------------------------------------------
/quickbindadapter/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/BindHolder.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter
2 |
3 | import android.view.View
4 | import androidx.databinding.ViewDataBinding
5 | import androidx.lifecycle.LifecycleOwner
6 | import androidx.recyclerview.widget.RecyclerView.ViewHolder
7 |
8 | /**
9 | * @author 小斌
10 | * @data 2019/6/24
11 | */
12 | open class BindHolder : ViewHolder {
13 |
14 | var binding: ViewDataBinding? = null
15 | private set
16 |
17 | var fullSpan: Boolean = false
18 | private set
19 |
20 | constructor(view: View) : super(view)
21 |
22 | constructor(
23 | binding: ViewDataBinding,
24 | lifecycleOwner: LifecycleOwner? = null,
25 | fullSpan: Boolean = false
26 | ) : super(binding.root) {
27 | this.binding = binding
28 | this.fullSpan = fullSpan
29 | if (lifecycleOwner is LifecycleOwner) {
30 | binding.lifecycleOwner = lifecycleOwner
31 | } else {
32 | binding.lifecycleOwner = null
33 | }
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/ItemData.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter
2 |
3 | import java.util.ArrayList
4 |
5 | /**
6 | * @author 小斌
7 | * @data 2019/7/10
8 | */
9 | class ItemData : ArrayList {
10 | constructor() {}
11 | constructor(initialCapacity: Int) : super(initialCapacity) {}
12 | constructor(c: Collection<*>) : super(c) {}
13 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/QuickBindPagingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter
2 |
3 | import android.content.Context
4 | import android.view.Gravity
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.TextView
9 | import androidx.annotation.LayoutRes
10 | import androidx.core.view.ViewCompat
11 | import androidx.databinding.DataBindingUtil
12 | import androidx.databinding.ViewDataBinding
13 | import androidx.lifecycle.LifecycleOwner
14 | import androidx.paging.PagingDataAdapter
15 | import androidx.recyclerview.widget.ConcatAdapter
16 | import androidx.recyclerview.widget.DiffUtil
17 | import androidx.recyclerview.widget.GridLayoutManager
18 | import androidx.recyclerview.widget.LinearLayoutManager
19 | import androidx.recyclerview.widget.RecyclerView
20 | import androidx.recyclerview.widget.StaggeredGridLayoutManager
21 | import com.xiaobin.quickbindadapter.interfaces.StaggeredFullSpan
22 | import com.xiaobin.quickbindadapter.paging.EmptyPageAdapter
23 | import com.xiaobin.quickbindadapter.view.BaseLoadView
24 | import com.xiaobin.quickbindadapter.view.BasePageStateView
25 | import kotlinx.coroutines.CoroutineDispatcher
26 | import kotlinx.coroutines.Dispatchers
27 |
28 | /**
29 | * Paging 分页专用适配器,增删改需要修改数据源头的方式实现
30 | * 这里只提供与QuickBindAdapter一样的多种布局实现
31 | * 由于技术有限,这里限制了数据类型
32 | * 加载更多条请使用 扩展方法: withLoadStateHeaderAndFooter 实现
33 | * @see withLoadStateHeaderAndFooter
34 | * @see withLoadStateHeader
35 | * @see withLoadStateFooter
36 | *
37 | * 如果泛型 T 定义了 具体的类型将失去多布局的主要意义,建议使用 Any
38 | * 当然,固定一种数据类型也可以使用这个适配器,可以方便随时切换不同的布局
39 | * 比如从一行1个item,变成1行2个、3个甚至更多个,不同数量使用不同的布局适配
40 | */
41 | class QuickBindPagingAdapter(
42 | itemCallback: DiffUtil.ItemCallback,
43 | private val lifecycleOwner: LifecycleOwner? = null,
44 | mainDispatcher: CoroutineDispatcher = Dispatchers.Main,
45 | workerDispatcher: CoroutineDispatcher = Dispatchers.Default,
46 | ) : PagingDataAdapter(itemCallback, mainDispatcher, workerDispatcher) {
47 |
48 | // 布局ID - 数据类型 互相绑定
49 | private val itemViewTypes = HashMap, Int>()
50 | private val itemBindVal = HashMap, Int>()
51 |
52 | private val UNKNOWN_VIEW_TYPE = -101
53 |
54 | private val UNKNOWN_VIEW_TYPE_TEXT = "Undefined data type!"
55 |
56 | private var mRecyclerView: RecyclerView? = null
57 |
58 | private var curEmptyAdapter: EmptyPageAdapter? = null
59 | private var concatAdapter: ConcatAdapter? = null
60 |
61 | /**
62 | * 请给RecyclerView设置这个adapter, 参考:
63 | * recyclerView.adapter = pagingAdapter.adapter
64 | */
65 | val adapter: RecyclerView.Adapter<*>
66 | get() {
67 | return if (concatAdapter == null) this else concatAdapter!!
68 | }
69 |
70 | /**
71 | * 启用状态View, 包括 底部加载更多状态,以及占满RV的空数据缺省页
72 | * 请在调用这个方法后,重新给RecyclerView设置适配器
73 | * 请设置 this.adapter
74 | * @see adapter 给 recyclerView设置的适配器
75 | * @param context 上下文对象
76 | * @param emptyStatePage 自定义 空数据占位 View
77 | * @param loadStateItem 自定义底部 加载更多状态 View
78 | * @param enableEmptyPage 是否显示 空数据占位 布局
79 | * @param enableLoadMoreItem 是否显示底部 加载更多状态 View
80 | * @param retry 点击 重新加载 数据回调
81 | */
82 | fun enableStatusView(
83 | context: Context,
84 | emptyStatePage: BasePageStateView<*, *, *>? = null,
85 | loadStateItem: BaseLoadView<*>? = null,
86 | enableEmptyPage: Boolean = true,
87 | enableLoadMoreItem: Boolean = false,
88 | retry: () -> Unit = {}
89 | ) {
90 | val getItemCount: () -> Int = {
91 | itemCount
92 | }
93 | curEmptyAdapter?.let {
94 | concatAdapter?.removeAdapter(it)
95 | }
96 | curEmptyAdapter = EmptyPageAdapter(
97 | getItemCount,
98 | emptyStatePage,
99 | loadStateItem,
100 | lifecycleOwner,
101 | enableEmptyPage,
102 | enableLoadMoreItem,
103 | retry
104 | )
105 | curEmptyAdapter?.let {
106 | concatAdapter = withLoadStateFooter(it)
107 | }
108 | }
109 |
110 | /**
111 | * 绑定 数据类型 和 视图ID
112 | */
113 | fun bind(clazz: Class<*>, @LayoutRes layoutId: Int, brId: Int = 0) {
114 | itemViewTypes[clazz] = layoutId
115 | itemBindVal[clazz] = brId
116 | refreshScreenItems(clazz)
117 | }
118 |
119 | /**
120 | * 刷新当前可见Item
121 | */
122 | private fun refreshScreenItems(clazz: Class<*>?) {
123 | mRecyclerView?.let { recyclerView ->
124 | recyclerView.layoutManager?.let { layoutManager ->
125 | var start = 0
126 | var end = Math.max(0, itemCount - 1)
127 | when (layoutManager) {
128 | is LinearLayoutManager -> {
129 | start = layoutManager.findFirstVisibleItemPosition()
130 | end = layoutManager.findLastVisibleItemPosition()
131 | }
132 |
133 | is StaggeredGridLayoutManager -> {
134 | val firstIndexArray = layoutManager.findFirstVisibleItemPositions(null)
135 | val lastIndexArray = layoutManager.findLastVisibleItemPositions(null)
136 | if (firstIndexArray.isNotEmpty() && lastIndexArray.isNotEmpty()) {
137 | firstIndexArray.sort()
138 | lastIndexArray.sort()
139 | start = firstIndexArray.first()
140 | end = lastIndexArray.last()
141 | }
142 | }
143 | }
144 | for (position in start..end) {
145 | if (position < itemCount) {
146 | if (clazz == null || getItem(position)?.javaClass == clazz) {
147 | notifyItemChanged(position)
148 | }
149 | }
150 | }
151 | }
152 | }
153 | }
154 |
155 | override fun getItemViewType(position: Int): Int {
156 | if (position >= itemCount) return super.getItemViewType(position)
157 | val data = getItem(position) ?: return UNKNOWN_VIEW_TYPE
158 | return itemViewTypes[data::class.java] ?: UNKNOWN_VIEW_TYPE
159 | }
160 |
161 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindHolder {
162 | var mClass: Class<*>? = null
163 | if (viewType != UNKNOWN_VIEW_TYPE) {
164 | for (itemViewType in itemViewTypes) {
165 | if (itemViewType.value == viewType) {
166 | mClass = itemViewType.key
167 | break
168 | }
169 | }
170 | }
171 | return if (mClass != null) {
172 | val isFullSpan = StaggeredFullSpan::class.java.isAssignableFrom(mClass)
173 | val binding = DataBindingUtil.inflate(
174 | LayoutInflater.from(parent.context),
175 | viewType,
176 | parent,
177 | false
178 | )
179 | BindHolder(
180 | binding = binding,
181 | lifecycleOwner = null,
182 | fullSpan = isFullSpan
183 | )
184 | } else {
185 | BindHolder(TextView(parent.context).apply {
186 | text = UNKNOWN_VIEW_TYPE_TEXT
187 | gravity = Gravity.CENTER_VERTICAL
188 | ViewCompat.setPaddingRelative(this, 12, 12, 12, 12)
189 | layoutParams = ViewGroup.LayoutParams(
190 | ViewGroup.LayoutParams.MATCH_PARENT,
191 | ViewGroup.LayoutParams.WRAP_CONTENT
192 | )
193 | })
194 | }
195 | }
196 |
197 | override fun onBindViewHolder(
198 | holder: BindHolder,
199 | position: Int,
200 | payloads: MutableList
201 | ) {
202 | if (payloads.isEmpty()) {
203 | onBindViewHolder(holder, position)
204 | return
205 | }
206 | val itemData = getItem(position) ?: return
207 | if (quickBindPayloads != null) {
208 | quickBindPayloads?.invoke(holder.binding, itemData, position, payloads)
209 | } else {
210 | super.onBindViewHolder(holder, position, payloads)
211 | }
212 | }
213 |
214 | override fun onBindViewHolder(holder: BindHolder, position: Int) {
215 | val itemViewType = getItemViewType(position)
216 | if (itemViewType == UNKNOWN_VIEW_TYPE) {
217 | return
218 | }
219 | val data = getItem(position) ?: return
220 | itemBindVal[data::class.java]?.let { brId ->
221 | if (brId != 0) {
222 | holder.binding?.setVariable(brId, data)
223 | }
224 | }
225 | quickBind?.invoke(holder.binding ?: return, data, position)
226 | if (onItemClickListener != null) {
227 | holder.binding?.root?.setOnClickListener {
228 | onItemClickListener?.invoke(this, data, position)
229 | }
230 | } else {
231 | holder.binding?.root?.setOnClickListener(null)
232 | }
233 | if (onItemLongClickListener != null) {
234 | holder.binding?.root?.setOnLongClickListener {
235 | return@setOnLongClickListener onItemLongClickListener?.invoke(
236 | this,
237 | data,
238 | position
239 | ) ?: false
240 | }
241 | } else {
242 | holder.binding?.root?.setOnLongClickListener(null)
243 | }
244 | if (onItemChildClickListener != null && childClicks.isNotEmpty()) {
245 | childClicks.forEach { viewId ->
246 | holder.binding?.root?.findViewById(viewId)?.setOnClickListener { childView ->
247 | onItemChildClickListener?.invoke(
248 | this,
249 | childView,
250 | data,
251 | position
252 | )
253 | }
254 | }
255 | } else {
256 | onItemChildClickListener = null
257 | }
258 | if (onItemChildLongClickListener != null && childLongClicks.isNotEmpty()) {
259 | childLongClicks.forEach { viewId ->
260 | holder.binding?.root?.findViewById(viewId)
261 | ?.setOnLongClickListener { childView ->
262 | return@setOnLongClickListener onItemChildLongClickListener?.invoke(
263 | this,
264 | childView,
265 | data,
266 | position
267 | ) ?: false
268 | }
269 | }
270 | } else {
271 | onItemChildLongClickListener = null
272 | }
273 | }
274 |
275 | override fun onViewAttachedToWindow(holder: BindHolder) {
276 | super.onViewAttachedToWindow(holder)
277 | //时刻检查spanSizeLookup是否需要覆盖自定义的
278 | refreshSpanSizeLookup()
279 | val lp = holder.itemView.layoutParams
280 | if (lp is StaggeredGridLayoutManager.LayoutParams) {
281 | if (holder.fullSpan) {
282 | lp.isFullSpan = true
283 | }
284 | }
285 | }
286 |
287 | override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
288 | super.onAttachedToRecyclerView(recyclerView)
289 | this.mRecyclerView = recyclerView
290 |
291 | //更新lookup
292 | refreshSpanSizeLookup()
293 | }
294 |
295 | private var customLookUp: GridLayoutManager.SpanSizeLookup? = null
296 |
297 | private val spanSizeLookUp = object : GridLayoutManager.SpanSizeLookup() {
298 | override fun getSpanSize(position: Int): Int {
299 | var itemViewType = adapter.getItemViewType(position)
300 | if (adapter is ConcatAdapter) {
301 | //获取真实的itemViewType
302 | itemViewType = (adapter as ConcatAdapter).getWrappedAdapterAndPosition(position)
303 | .first.getItemViewType(position)
304 | }
305 | return if (itemViewType == UNKNOWN_VIEW_TYPE
306 | || itemViewType == EmptyPageAdapter.LOAD_VIEW_TYPE
307 | || itemViewType == EmptyPageAdapter.EMPTY_VIEW_TYPE
308 | ) {
309 | (mRecyclerView?.layoutManager as? GridLayoutManager)?.spanCount ?: 1
310 | } else {
311 | customLookUp?.getSpanSize(position) ?: 1
312 | }
313 | }
314 | }
315 |
316 | /**
317 | * 刷新SpanSizeLookup
318 | */
319 | private fun refreshSpanSizeLookup() {
320 | mRecyclerView?.let { rv ->
321 | val layoutManager = rv.layoutManager
322 | if (layoutManager is GridLayoutManager) {
323 | val rvSpanSizeLookup = layoutManager.spanSizeLookup
324 | if (rvSpanSizeLookup != spanSizeLookUp) {
325 | //需要重新覆盖spanSizeLookup
326 | customLookUp = rvSpanSizeLookup
327 | layoutManager.spanSizeLookup = spanSizeLookUp
328 | }
329 | } else if (layoutManager == null) {
330 | throw NullPointerException("请在完成初始化前先给RecyclerView设置LayoutManager!")
331 | }
332 | }
333 | }
334 |
335 | var quickBind: ((mBinding: ViewDataBinding?, data: T, position: Int) -> Unit)? =
336 | null
337 |
338 | var quickBindPayloads: ((
339 | mBinding: ViewDataBinding?,
340 | data: T,
341 | position: Int,
342 | payloads: MutableList
343 | ) -> Unit)? = null
344 |
345 | var onItemClickListener: ((adapter: QuickBindPagingAdapter, data: T, position: Int) -> Unit)? =
346 | null
347 |
348 | var onItemLongClickListener: ((adapter: QuickBindPagingAdapter, data: T, position: Int) -> Boolean)? =
349 | null
350 |
351 | private var onItemChildClickListener: ((
352 | adapter: QuickBindPagingAdapter,
353 | view: View,
354 | data: T,
355 | position: Int
356 | ) -> Unit)? = null
357 |
358 | private val childClicks = mutableSetOf()
359 |
360 | private var onItemChildLongClickListener: ((
361 | adapter: QuickBindPagingAdapter,
362 | view: View,
363 | data: T,
364 | position: Int
365 | ) -> Boolean)? = null
366 |
367 | private val childLongClicks = mutableSetOf()
368 |
369 | fun onItemChildClickListener(
370 | vararg viewId: Int,
371 | onItemChildClickListener: ((adapter: QuickBindPagingAdapter, view: View, data: T, position: Int) -> Unit)?
372 | ) {
373 | if (onItemChildClickListener != null) {
374 | childClicks.addAll(viewId.asList())
375 | } else {
376 | childClicks.clear()
377 | }
378 | this.onItemChildClickListener = onItemChildClickListener
379 | }
380 |
381 | fun onItemChildLongClickListener(
382 | vararg viewId: Int,
383 | onItemChildLongClickListener: ((adapter: QuickBindPagingAdapter, view: View, data: T, position: Int) -> Boolean)?
384 | ) {
385 | if (onItemChildLongClickListener != null) {
386 | childLongClicks.addAll(viewId.asList())
387 | } else {
388 | childLongClicks.clear()
389 | }
390 | this.onItemChildLongClickListener = onItemChildLongClickListener
391 | }
392 |
393 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/holder/EmptyViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.holder
2 |
3 | import android.view.View
4 | import androidx.databinding.ViewDataBinding
5 | import androidx.lifecycle.LifecycleOwner
6 | import com.xiaobin.quickbindadapter.BindHolder
7 | import com.xiaobin.quickbindadapter.view.BasePageStateView
8 | import com.xiaobin.quickbindadapter.view.PageState
9 |
10 | class EmptyViewHolder : BindHolder {
11 |
12 | lateinit var emptyStatePageView: BasePageStateView<*, *, *>
13 |
14 | private constructor(view: View) : super(view)
15 |
16 | constructor(
17 | emptyStatePage: BasePageStateView<*, *, *>,
18 | binding: ViewDataBinding,
19 | lifecycleOwner: LifecycleOwner? = null
20 | ) : super(binding, lifecycleOwner) {
21 | emptyStatePageView = emptyStatePage
22 | }
23 |
24 | fun showLoadingPage() {
25 | emptyStatePageView.setPageState(PageState.Loading)
26 | }
27 |
28 | fun showErrorPage() {
29 | emptyStatePageView.setPageState(PageState.Error)
30 | }
31 |
32 | fun showEmptyPage() {
33 | emptyStatePageView.setPageState(PageState.Empty)
34 | }
35 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/holder/LoadViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.holder
2 |
3 | import androidx.databinding.ViewDataBinding
4 | import androidx.lifecycle.LifecycleOwner
5 | import com.xiaobin.quickbindadapter.BindHolder
6 | import com.xiaobin.quickbindadapter.view.BaseLoadView
7 |
8 | class LoadViewHolder(
9 | val loadStateView: BaseLoadView<*>,
10 | binding: ViewDataBinding,
11 | lifecycleOwner: LifecycleOwner? = null,
12 | ) : BindHolder(binding, lifecycleOwner) {
13 |
14 | fun onLoading() {
15 | loadStateView.isLoading()
16 | }
17 |
18 | fun onWait() {
19 | loadStateView.isWaitLoading()
20 | }
21 |
22 | fun onSuccess() {
23 | loadStateView.isLoadMoreSuccess()
24 | }
25 |
26 | fun onError() {
27 | loadStateView.isLoadMoreFailed()
28 | }
29 |
30 | fun noMore() {
31 | loadStateView.isNoMoreData()
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/interfaces/StaggeredFullSpan.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.interfaces
2 |
3 | interface StaggeredFullSpan {}
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/paging/EmptyPageAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.paging
2 |
3 | import android.view.ViewGroup
4 | import android.widget.Space
5 | import androidx.lifecycle.LifecycleOwner
6 | import androidx.paging.LoadState
7 | import androidx.paging.LoadStateAdapter
8 | import androidx.recyclerview.widget.RecyclerView
9 | import com.xiaobin.quickbindadapter.BindHolder
10 | import com.xiaobin.quickbindadapter.holder.EmptyViewHolder
11 | import com.xiaobin.quickbindadapter.holder.LoadViewHolder
12 | import com.xiaobin.quickbindadapter.view.BaseLoadView
13 | import com.xiaobin.quickbindadapter.view.BasePageStateView
14 | import com.xiaobin.quickbindadapter.view.DefaultEmptyStatePage
15 | import com.xiaobin.quickbindadapter.view.DefaultLoadView
16 |
17 | /**
18 | * 空数据占位布局
19 | */
20 | internal class EmptyPageAdapter(
21 | private val getCount: () -> Int,
22 | private val pageView: BasePageStateView<*, *, *>? = null,
23 | private val loadView: BaseLoadView<*>? = null,
24 | private val lifecycleOwner: LifecycleOwner? = null,
25 | private val enableEmptyPage: Boolean = true,
26 | private val enableLoadMoreItem: Boolean = false,
27 | private val retry: () -> Unit = {},
28 | ) : LoadStateAdapter() {
29 |
30 | companion object {
31 | const val LOAD_VIEW_TYPE = -1
32 | const val EMPTY_VIEW_TYPE = -2
33 | }
34 |
35 | private var mRecyclerView: RecyclerView? = null
36 |
37 | private fun showLoadItem(): Boolean {
38 | return getCount.invoke() > 0
39 | }
40 |
41 | override fun getStateViewType(loadState: LoadState): Int {
42 | return if (showLoadItem()) LOAD_VIEW_TYPE else EMPTY_VIEW_TYPE
43 | }
44 |
45 | override fun displayLoadStateAsItem(loadState: LoadState): Boolean {
46 | return enableLoadMoreItem || (enableEmptyPage && !showLoadItem())
47 | }
48 |
49 | override fun onBindViewHolder(holder: BindHolder, loadState: LoadState) {
50 | //两种布局,分别绑定
51 | if (holder is LoadViewHolder) {
52 | when (loadState) {
53 | is LoadState.Loading -> holder.onLoading()
54 | is LoadState.Error -> holder.onError()
55 | is LoadState.NotLoading -> {
56 | if (loadState.endOfPaginationReached) {
57 | holder.noMore()
58 | } else {
59 | holder.onWait()
60 | }
61 | }
62 | }
63 | } else if (holder is EmptyViewHolder) {
64 | when (loadState) {
65 | is LoadState.Error -> holder.showErrorPage()
66 | LoadState.Loading -> holder.showLoadingPage()
67 | is LoadState.NotLoading -> holder.showEmptyPage()
68 | }
69 | }
70 | }
71 |
72 | override fun onCreateViewHolder(
73 | parent: ViewGroup,
74 | loadState: LoadState
75 | ): BindHolder {
76 | return if (showLoadItem() && enableLoadMoreItem) {
77 | loadView?.createViewHolder(parent, lifecycleOwner)
78 | ?: DefaultLoadView.defaultLoadView(parent.context)
79 | .createViewHolder(parent, lifecycleOwner) {
80 | retry()
81 | }
82 | } else if (!showLoadItem() && enableEmptyPage) {
83 | pageView?.createViewHolder(parent, lifecycleOwner)
84 | ?: DefaultEmptyStatePage(parent.context).createViewHolder(parent, lifecycleOwner)
85 | } else {
86 | BindHolder(Space(parent.context))
87 | }
88 | }
89 |
90 | override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
91 | super.onAttachedToRecyclerView(recyclerView)
92 | mRecyclerView = recyclerView
93 | }
94 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/view/BaseLoadView.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.view
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.databinding.ViewDataBinding
7 | import androidx.lifecycle.LifecycleOwner
8 | import com.xiaobin.quickbindadapter.BindHolder
9 | import com.xiaobin.quickbindadapter.holder.LoadViewHolder
10 |
11 | abstract class BaseLoadView(private val layoutId: Int) {
12 |
13 | enum class LoadMoreState {
14 | LOADING, SUCCESS, FAILED, NO_MORE, WAIT_LOADING, NO_STATE
15 | }
16 |
17 | private var viewHolder: LoadViewHolder? = null
18 |
19 | //加载更多
20 | var loadMoreState = LoadMoreState.NO_STATE
21 | private set
22 |
23 | private var loadView: T? = null
24 |
25 | fun createViewHolder(
26 | parent: ViewGroup,
27 | lifecycleOwner: LifecycleOwner? = null,
28 | onClickLoadMore: () -> Unit = {}
29 | ): LoadViewHolder {
30 | loadView = DataBindingUtil.inflate(
31 | LayoutInflater.from(parent.context),
32 | layoutId,
33 | parent, false
34 | )
35 | initView(loadView)
36 | loadView!!.root.setOnClickListener {
37 | if (loadMoreState == LoadMoreState.NO_MORE ||
38 | loadMoreState == LoadMoreState.LOADING ||
39 | loadMoreState == LoadMoreState.SUCCESS ||
40 | loadView == null
41 | ) {
42 | //加载中,加载结束并且没有更多数据时,不允许触发点击事件
43 | return@setOnClickListener
44 | }
45 | onClickLoadMore()
46 | }
47 | //恢复状态
48 | when (loadMoreState) {
49 | LoadMoreState.LOADING -> isLoading(true)
50 | LoadMoreState.SUCCESS -> isLoadMoreSuccess(true)
51 | LoadMoreState.FAILED -> isLoadMoreFailed(true)
52 | LoadMoreState.NO_MORE -> isNoMoreData(true)
53 | LoadMoreState.WAIT_LOADING -> isWaitLoading(true)
54 | LoadMoreState.NO_STATE -> isWaitLoading(true)
55 | }
56 | viewHolder = LoadViewHolder(this, loadView!!, lifecycleOwner)
57 | return viewHolder!!
58 | }
59 |
60 | /**
61 | * 变更为 等待用户点击加载更多 状态
62 | * 禁止了自动加载更多的时候,才会调用这个方法
63 | */
64 | fun isWaitLoading(ignore: Boolean = false) {
65 | if (!ignore && loadMoreState == LoadMoreState.WAIT_LOADING) return
66 | loadMoreState = LoadMoreState.WAIT_LOADING
67 | loadView?.let {
68 | onStateChange(it, LoadMoreState.WAIT_LOADING)
69 | }
70 | }
71 |
72 | /**
73 | * 变更为 正在加载更多 状态
74 | */
75 | fun isLoading(ignore: Boolean = false) {
76 | if (!ignore && loadMoreState == LoadMoreState.LOADING) return
77 | loadMoreState = LoadMoreState.LOADING
78 | loadView?.let {
79 | onStateChange(it, LoadMoreState.LOADING)
80 | }
81 | }
82 |
83 | /**
84 | * 变更为 加载更多完成且无更多数据 状态
85 | */
86 | fun isNoMoreData(ignore: Boolean = false) {
87 | if (!ignore && loadMoreState == LoadMoreState.NO_MORE) return
88 | loadMoreState = LoadMoreState.NO_MORE
89 | loadView?.let {
90 | onStateChange(it, LoadMoreState.NO_MORE)
91 | }
92 | }
93 |
94 | /**
95 | * 变更为 加载更多成功 状态
96 | */
97 | fun isLoadMoreSuccess(ignore: Boolean = false) {
98 | if (!ignore && loadMoreState == LoadMoreState.SUCCESS) return
99 | loadMoreState = LoadMoreState.SUCCESS
100 | loadView?.let {
101 | onStateChange(it, LoadMoreState.SUCCESS)
102 | }
103 | }
104 |
105 | /**
106 | * 变更为 加载更多失败了 状态
107 | */
108 | fun isLoadMoreFailed(ignore: Boolean = false) {
109 | if (!ignore && loadMoreState == LoadMoreState.FAILED) return
110 | loadMoreState = LoadMoreState.FAILED
111 | loadView?.let {
112 | onStateChange(it, LoadMoreState.FAILED)
113 | }
114 | }
115 |
116 | protected abstract fun initView(loadView: T?)
117 |
118 | /**
119 | * 状态变化
120 | */
121 | protected abstract fun onStateChange(loadView: T, state: LoadMoreState)
122 |
123 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/view/BasePageStateView.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.view
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.databinding.DataBindingUtil
7 | import androidx.databinding.ViewDataBinding
8 | import androidx.lifecycle.LifecycleOwner
9 | import com.xiaobin.quickbindadapter.BindHolder
10 | import com.xiaobin.quickbindadapter.R
11 | import com.xiaobin.quickbindadapter.databinding.McXbLayoutPlacePageBinding
12 | import com.xiaobin.quickbindadapter.holder.EmptyViewHolder
13 |
14 | abstract class BasePageStateView<
15 | EmptyPage : ViewDataBinding,
16 | ErrorPage : ViewDataBinding,
17 | LoadingPage : ViewDataBinding
18 | >(
19 | private val emptyPageLayoutId: Int = 0,
20 | private val errorPageLayoutId: Int = 0,
21 | private val loadingPageLayoutId: Int = 0,
22 | ) {
23 |
24 | var emptyPageDecorView: McXbLayoutPlacePageBinding? = null
25 | private set
26 | private var lifecycleOwner: LifecycleOwner? = null
27 |
28 | private var currentPageState: PageState = PageState.Loading
29 |
30 | var emptyView: EmptyPage? = null
31 | var loadingView: LoadingPage? = null
32 | var errorView: ErrorPage? = null
33 |
34 | /**
35 | * 三种状态的页面不是一开始就创建了的,所以这个方法可以监听到哪个页面被首次创建
36 | */
37 | protected abstract fun onPageCreate(action: PageState)
38 |
39 | /**
40 | * 当切换状态的时候,这里可以监听到当前是哪个页面
41 | */
42 | protected abstract fun onActionCall(action: PageState)
43 |
44 | /**
45 | * 设置默认的页面,并切换到这个页面
46 | */
47 | fun setDefaultPage(pageState: PageState) {
48 | currentPageState = pageState
49 | }
50 |
51 | /**
52 | * 获取当前的页面状态
53 | */
54 | fun getCurrentPageState(): PageState {
55 | return currentPageState
56 | }
57 |
58 | fun createViewHolder(parent: ViewGroup, mLifecycleOwner: LifecycleOwner? = null): EmptyViewHolder {
59 | emptyView = null
60 | loadingView = null
61 | errorView = null
62 | emptyPageDecorView = DataBindingUtil.inflate(
63 | LayoutInflater.from(parent.context),
64 | R.layout.mc_xb_layout_place_page,
65 | parent,
66 | false
67 | )
68 | mLifecycleOwner?.apply {
69 | lifecycleOwner = this
70 | emptyPageDecorView!!.lifecycleOwner = this
71 | }
72 | setPageState(currentPageState)
73 | return EmptyViewHolder(this, emptyPageDecorView!!, mLifecycleOwner)
74 | }
75 |
76 | private fun createPageViewBinding(layoutId: Int): T? {
77 | if (layoutId == 0) return null
78 | val parent = emptyPageDecorView!!.flPlaceholder
79 | val pageBindingView: T = DataBindingUtil.inflate(
80 | LayoutInflater.from(parent.context),
81 | layoutId, parent, false
82 | )
83 | lifecycleOwner?.let {
84 | pageBindingView.lifecycleOwner = it
85 | }
86 | return pageBindingView
87 | }
88 |
89 | private fun getEmptyPage(): EmptyPage? {
90 | if (!check()) return null
91 | return if (emptyPageLayoutId == 0) {
92 | null
93 | } else if (emptyView != null) {
94 | lifecycleOwner?.let {
95 | emptyView!!.lifecycleOwner = it
96 | }
97 | emptyView
98 | } else {
99 | emptyView = createPageViewBinding(emptyPageLayoutId)
100 | onPageCreate(PageState.Empty)
101 | emptyView
102 | }
103 | }
104 |
105 | private fun getLoadingPage(): LoadingPage? {
106 | if (!check()) return null
107 | return if (loadingPageLayoutId == 0) {
108 | null
109 | } else if (loadingView != null) {
110 | lifecycleOwner?.let {
111 | loadingView!!.lifecycleOwner = it
112 | }
113 | loadingView
114 | } else {
115 | loadingView = createPageViewBinding(loadingPageLayoutId)
116 | onPageCreate(PageState.Loading)
117 | loadingView
118 | }
119 | }
120 |
121 | private fun getErrorPage(): ErrorPage? {
122 | if (!check()) return null
123 | return if (errorPageLayoutId == 0) {
124 | null
125 | } else if (errorView != null) {
126 | lifecycleOwner?.let {
127 | errorView!!.lifecycleOwner = it
128 | }
129 | errorView
130 | } else {
131 | errorView = createPageViewBinding(errorPageLayoutId)
132 | onPageCreate(PageState.Error)
133 | errorView
134 | }
135 | }
136 |
137 | fun refreshPage() {
138 | setPageState(currentPageState)
139 | }
140 |
141 | fun setPageState(action: PageState) {
142 | if (!check() && currentPageState != action) {
143 | setDefaultPage(action)
144 | return
145 | }
146 | val view: View? = when (action) {
147 | PageState.Empty -> {
148 | getEmptyPage()?.root
149 | }
150 | PageState.Error -> {
151 | getErrorPage()?.root
152 | }
153 | PageState.Loading -> {
154 | getLoadingPage()?.root
155 | }
156 | PageState.Finish -> {
157 | null
158 | }
159 | }
160 | emptyPageDecorView?.flPlaceholder?.removeAllViews()
161 | view?.apply {
162 | emptyPageDecorView?.flPlaceholder?.addView(this, -1, -1)
163 | }
164 | currentPageState = action
165 | onActionCall(action)
166 | }
167 |
168 | private fun check(): Boolean {
169 | if (emptyPageDecorView == null) {
170 | return false
171 | }
172 | return true
173 | }
174 | }
175 |
176 | enum class PageState {
177 | Loading, Empty, Error, Finish
178 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/view/DefaultEmptyStatePage.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.view
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import androidx.annotation.ColorInt
6 | import com.xiaobin.quickbindadapter.R
7 | import com.xiaobin.quickbindadapter.databinding.*
8 |
9 | class DefaultPlacePageConfigsBean : Cloneable {
10 | var emptyText: String = ""
11 | var errorText: String = ""
12 |
13 | @ColorInt
14 | var emptyTextColor: Int = Color.LTGRAY
15 |
16 | @ColorInt
17 | var errorTextColor: Int = Color.RED
18 |
19 | public override fun clone(): DefaultPlacePageConfigsBean {
20 | return try {
21 | return super.clone() as? DefaultPlacePageConfigsBean
22 | ?: DefaultPlacePageConfigsBean()
23 | } catch (e: CloneNotSupportedException) {
24 | DefaultPlacePageConfigsBean()
25 | }
26 | }
27 | }
28 |
29 | /**
30 | * 默认的 状态 页面
31 | */
32 | class DefaultEmptyStatePage(private val context: Context) :
33 | BasePageStateView(
34 | R.layout.mc_xb_layout_page_empty,
35 | R.layout.mc_xb_layout_page_error,
36 | R.layout.mc_xb_layout_page_loading,
37 | ) {
38 |
39 | companion object {
40 | //全局配置
41 | var globalConfig: DefaultPlacePageConfigsBean? = null
42 | private set
43 |
44 | /**
45 | * 调用set方法,全局替换默认状态页为自定义的状态页
46 | * 如果统一设置为自定义的状态页,那么就不需要配置全局配置项了,因为那个是给默认状态页用的
47 | */
48 | var defaultStatePage: (Context) -> BasePageStateView<*, *, *> = {
49 | DefaultEmptyStatePage(it)
50 | }
51 |
52 | //创建全局配置
53 | fun createGlobalConfig(result: () -> DefaultPlacePageConfigsBean) {
54 | globalConfig = result.invoke()
55 | }
56 |
57 | //创建全局配置
58 | fun createGlobalConfig(result: DefaultPlacePageConfigsBean) {
59 | globalConfig = result
60 | }
61 | }
62 |
63 | init {
64 | if (globalConfig == null) {
65 | createGlobalConfig(DefaultPlacePageConfigsBean().apply {
66 | emptyText = context.getString(R.string.place_onEmpty)
67 | errorText = context.getString(R.string.place_onError)
68 | emptyTextColor = Color.LTGRAY
69 | errorTextColor = Color.RED
70 | })
71 | }
72 | }
73 |
74 | private var config: DefaultPlacePageConfigsBean = globalConfig!!.clone()
75 | set(value) {
76 | field = if (value === globalConfig) {
77 | //如果是设置的全局配置,则克隆一份
78 | value.clone()
79 | } else {
80 | value
81 | }
82 | }
83 |
84 | override fun onPageCreate(action: PageState) {
85 | when (action) {
86 | PageState.Empty -> {
87 | emptyView?.apply {
88 | tvEmpty.setTextColor(config.emptyTextColor)
89 | tvEmpty.text = config.emptyText
90 | }
91 |
92 | }
93 | PageState.Error -> {
94 | errorView?.apply {
95 | tvError.setTextColor(config.errorTextColor)
96 | tvError.text = config.errorText
97 | }
98 | }
99 | PageState.Loading -> {
100 | }
101 | PageState.Finish -> {
102 | }
103 | }
104 | }
105 |
106 | override fun onActionCall(action: PageState) {
107 | }
108 |
109 | fun setErrorPageTextColor(@ColorInt textColor: Int) {
110 | config.errorTextColor = textColor
111 | errorView?.apply {
112 | tvError.setTextColor(config.errorTextColor)
113 | }
114 | }
115 |
116 | fun setErrorPageText(text: String) {
117 | config.errorText = text
118 | errorView?.apply {
119 | tvError.text = config.errorText
120 | }
121 | }
122 |
123 | fun setEmptyPageTextColor(@ColorInt textColor: Int) {
124 | config.emptyTextColor = textColor
125 | emptyView?.apply {
126 | tvEmpty.setTextColor(config.emptyTextColor)
127 | }
128 | }
129 |
130 | fun setEmptyPageText(text: String) {
131 | config.emptyText = text
132 | emptyView?.apply {
133 | tvEmpty.text = config.emptyText
134 | }
135 | }
136 |
137 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/java/com/xiaobin/quickbindadapter/view/DefaultLoadView.kt:
--------------------------------------------------------------------------------
1 | package com.xiaobin.quickbindadapter.view
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import android.graphics.Color
6 | import android.os.Build
7 | import androidx.annotation.ColorInt
8 | import com.xiaobin.quickbindadapter.R
9 | import com.xiaobin.quickbindadapter.databinding.McXbItemLoadmoreBinding
10 |
11 | class DefaultLoadViewConfigsBean : Cloneable {
12 | var onLoadingText: String = ""
13 | var onFailedText: String = ""
14 | var onWaitLoadingText: String = ""
15 | var noMoreDataText: String = ""
16 | var onSuccessText: String = ""
17 |
18 | @ColorInt
19 | var onLoadingTextColor: Int = Color.LTGRAY
20 |
21 | @ColorInt
22 | var onWaitLoadingTextColor: Int = Color.LTGRAY
23 |
24 | @ColorInt
25 | var onFailedTextColor: Int = Color.LTGRAY
26 |
27 | @ColorInt
28 | var noMoreDataTextColor: Int = Color.LTGRAY
29 |
30 | @ColorInt
31 | var onSuccessTextColor: Int = Color.LTGRAY
32 |
33 | public override fun clone(): DefaultLoadViewConfigsBean {
34 | return try {
35 | return super.clone() as? DefaultLoadViewConfigsBean
36 | ?: DefaultLoadViewConfigsBean()
37 | } catch (e: CloneNotSupportedException) {
38 | DefaultLoadViewConfigsBean()
39 | }
40 | }
41 | }
42 |
43 | /**
44 | * 默认的加载更多Item布局
45 | */
46 | class DefaultLoadView(private val context: Context) :
47 | BaseLoadView(R.layout.mc_xb_item_loadmore) {
48 |
49 | companion object {
50 | //全局配置
51 | var globalConfig: DefaultLoadViewConfigsBean? = null
52 | private set
53 |
54 | /**
55 | * 调用set方法,全局替换默认加载更多样式为自定义的加载更多样式
56 | * 如果统一设置为自定义的加载更多样式,那么就不需要配置全局配置项了,因为那个是给默认加载更多样式用的
57 | */
58 | var defaultLoadView: (Context) -> BaseLoadView<*> = {
59 | DefaultLoadView(it)
60 | }
61 |
62 | //创建全局配置
63 | fun createGlobalConfig(result: () -> DefaultLoadViewConfigsBean) {
64 | globalConfig = result.invoke()
65 | }
66 |
67 | //创建全局配置
68 | fun createGlobalConfig(result: DefaultLoadViewConfigsBean) {
69 | globalConfig = result
70 | }
71 |
72 | }
73 |
74 | init {
75 | if (globalConfig == null) {
76 | createGlobalConfig(DefaultLoadViewConfigsBean().apply {
77 | onLoadingText = context.getString(R.string.load_onLoading)
78 | onFailedText = context.getString(R.string.load_onFailed)
79 | onWaitLoadingText = context.getString(R.string.load_onWaitLoading)
80 | noMoreDataText = context.getString(R.string.load_noMoreData)
81 | onSuccessText = context.getString(R.string.load_onSuccess)
82 | onLoadingTextColor = Color.LTGRAY
83 | onWaitLoadingTextColor = Color.LTGRAY
84 | onFailedTextColor = Color.LTGRAY
85 | noMoreDataTextColor = Color.LTGRAY
86 | onSuccessTextColor = Color.LTGRAY
87 | })
88 | }
89 | }
90 |
91 | private var config: DefaultLoadViewConfigsBean = globalConfig!!.clone()
92 | set(value) {
93 | field = if (value === globalConfig) {
94 | //如果是设置的全局配置,则克隆一份
95 | value.clone()
96 | } else {
97 | value
98 | }
99 | }
100 |
101 | fun setOnWaitLoadingText(text: String) {
102 | config.onWaitLoadingText = text
103 | }
104 |
105 | fun setOnLoadingText(text: String) {
106 | config.onLoadingText = text
107 | }
108 |
109 | fun setOnFailedText(text: String) {
110 | config.onFailedText = text
111 | }
112 |
113 | fun setNoMoreDataText(text: String) {
114 | config.noMoreDataText = text
115 | }
116 |
117 | fun setOnSuccessText(text: String) {
118 | config.onSuccessText = text
119 | }
120 |
121 | fun setOnWaitLoadingTextColor(@ColorInt color: Int) {
122 | config.onWaitLoadingTextColor = color
123 | }
124 |
125 | fun setOnLoadingTextColor(@ColorInt color: Int) {
126 | config.onLoadingTextColor = color
127 | }
128 |
129 | fun setOnFailedTextColor(@ColorInt color: Int) {
130 | config.onFailedTextColor = color
131 | }
132 |
133 | fun setNoMoreDataTextColor(@ColorInt color: Int) {
134 | config.noMoreDataTextColor = color
135 | }
136 |
137 | fun setOnSuccessTextColor(@ColorInt color: Int) {
138 | config.onSuccessTextColor = color
139 | }
140 |
141 | override fun initView(loadView: McXbItemLoadmoreBinding?) {
142 | }
143 |
144 | override fun onStateChange(loadView: McXbItemLoadmoreBinding, state: LoadMoreState) {
145 | when (state) {
146 | LoadMoreState.LOADING -> {
147 | loadView.loading = true
148 | loadView.text = config.onLoadingText
149 | loadView.tvText.setTextColor(config.onLoadingTextColor)
150 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
151 | loadView.progress.progressTintList =
152 | ColorStateList.valueOf(config.onLoadingTextColor)
153 | }
154 | }
155 | LoadMoreState.SUCCESS -> {
156 | loadView.loading = false
157 | var text = config.onSuccessText
158 | if (text.isBlank()) {
159 | text = context.getString(R.string.load_onSuccess)
160 | }
161 | loadView.text = text
162 | loadView.tvText.setTextColor(config.onSuccessTextColor)
163 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
164 | loadView.progress.progressTintList =
165 | ColorStateList.valueOf(config.onSuccessTextColor)
166 | }
167 | }
168 | LoadMoreState.FAILED -> {
169 | loadView.loading = false
170 | var text = config.onFailedText
171 | if (text.isBlank()) {
172 | text = context.getString(R.string.load_onFailed)
173 | }
174 | loadView.text = text
175 | loadView.tvText.setTextColor(config.onFailedTextColor)
176 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
177 | loadView.progress.progressTintList =
178 | ColorStateList.valueOf(config.onFailedTextColor)
179 | }
180 | }
181 | LoadMoreState.NO_MORE -> {
182 | loadView.loading = false
183 | var text = config.noMoreDataText
184 | if (text.isBlank()) {
185 | text = context.getString(R.string.load_noMoreData)
186 | }
187 | loadView.text = text
188 | loadView.tvText.setTextColor(config.noMoreDataTextColor)
189 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
190 | loadView.progress.progressTintList =
191 | ColorStateList.valueOf(config.noMoreDataTextColor)
192 | }
193 | }
194 | LoadMoreState.WAIT_LOADING -> {
195 | loadView.loading = false
196 | var text = config.onWaitLoadingText
197 | if (text.isBlank()) {
198 | text = context.getString(R.string.load_onWaitLoading)
199 | }
200 | loadView.text = text
201 | loadView.tvText.setTextColor(config.onWaitLoadingTextColor)
202 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
203 | loadView.progress.progressTintList =
204 | ColorStateList.valueOf(config.onWaitLoadingTextColor)
205 | }
206 | }
207 | else -> {}
208 | }
209 | }
210 |
211 | }
--------------------------------------------------------------------------------
/quickbindadapter/src/main/res/layout/mc_xb_item_loadmore.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
16 |
17 |
18 |
23 |
24 |
30 |
31 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/quickbindadapter/src/main/res/layout/mc_xb_layout_page_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/quickbindadapter/src/main/res/layout/mc_xb_layout_page_error.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/quickbindadapter/src/main/res/layout/mc_xb_layout_page_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/quickbindadapter/src/main/res/layout/mc_xb_layout_place_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/quickbindadapter/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 正在加载中
5 | 加载失败了,点击重试
6 | 点击加载更多
7 | 没有更多了
8 | 完成加载
9 |
10 | 没有内容
11 | 获取失败
12 |
13 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':quickbindadapter'
2 |
--------------------------------------------------------------------------------