├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
└── misc.xml
├── README.md
├── anyby
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jay
│ │ └── anyby
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── jay
│ │ └── anyby
│ │ ├── api
│ │ └── InterfaceApiDelegate.kt
│ │ ├── sp
│ │ ├── PreferencesExt.kt
│ │ └── SPDelegate.kt
│ │ └── vm
│ │ ├── ActivityVMDelegate.kt
│ │ ├── FragmentVMDelegate.kt
│ │ └── VMDelegate.kt
│ └── test
│ └── java
│ └── com
│ └── jay
│ └── anyby
│ └── ExampleUnitTest.kt
├── build.gradle
├── common.gradle
├── config.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── settings.gradle
├── simple
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jay
│ │ └── vbhelper
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── jay
│ │ │ └── vbhelper
│ │ │ └── simple
│ │ │ ├── SimpleActivity.kt
│ │ │ ├── delagate_vb
│ │ │ ├── CustomViewWithDelegate.kt
│ │ │ ├── MainActivityWithDelegate.kt
│ │ │ ├── SecondFragmentWithDelegate.kt
│ │ │ └── TextAdapterWithDelegate.kt
│ │ │ ├── delegate_api
│ │ │ ├── ApiTestActivity.kt
│ │ │ └── ProxyTest.kt
│ │ │ ├── delegate_sp
│ │ │ ├── SPTestActivity.kt
│ │ │ ├── TokenHolder.kt
│ │ │ └── UserHolder.kt
│ │ │ ├── delegate_vm
│ │ │ ├── MyViewModel.kt
│ │ │ ├── User.kt
│ │ │ └── ViewModelTestActivity.kt
│ │ │ ├── inflate_test
│ │ │ └── InflateTestActivity.kt
│ │ │ └── normal_use_vb
│ │ │ ├── CustomView.kt
│ │ │ ├── FirstFragment.kt
│ │ │ ├── FirstViewModel.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── SecondFragment.kt
│ │ │ └── TextAdapter.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_api_test.xml
│ │ ├── activity_inflate_test.xml
│ │ ├── activity_main.xml
│ │ ├── activity_simple.xml
│ │ ├── activity_vm_test.xml
│ │ ├── content_main.xml
│ │ ├── fragment_first.xml
│ │ ├── fragment_second.xml
│ │ ├── info_layout_info.xml
│ │ ├── layout_info.xml
│ │ ├── layout_info_merge.xml
│ │ ├── layout_info_view_stub.xml
│ │ ├── layout_item_text.xml
│ │ └── layout_view.xml
│ │ ├── menu
│ │ └── menu_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── navigation
│ │ └── nav_graph.xml
│ │ ├── values-land
│ │ └── dimens.xml
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values-w1240dp
│ │ └── dimens.xml
│ │ ├── values-w600dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── jay
│ └── vbhelper
│ └── ExampleUnitTest.kt
└── vbhelper
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── jay
│ └── vbhelper
│ └── ExampleInstrumentedTest.kt
├── main
├── AndroidManifest.xml
└── java
│ └── com
│ └── jay
│ └── vbhelper
│ └── delegate
│ ├── ActivityVBDelegate.kt
│ ├── FragmentVBDelegate.kt
│ ├── ViewHolderVBDelegate.kt
│ └── ViewVBDelegate.kt
└── test
└── java
└── com
└── jay
└── vbhelper
└── ExampleUnitTest.kt
/.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 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
22 |
23 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VBHelper
2 | ViewBinding 超详细原理解析,利用 Kotlin 属性委托实现 VBHelper 方便 VB 在多场景下的生成
3 |
4 | **ViewBinding 原理解析点[这里](https://juejin.cn/post/7005504217935052837),主要介绍了以下几点:**
5 |
6 | - VB 集成与一般使用方式,包括:Activity 、Fragment、Adapter、include、merge、ViewStub
7 | - KT 属性代理与泛型实化类型参数 `reified` 的介绍
8 | - 通过 KT 属性代理创建 VB
9 | - LayoutInflater 原理与参数解析
10 | - XXXBinding 类的绑定过程
11 | - XXXBinding 类的生成过程
12 |
13 | **属性委托详解点[这里](https://juejin.cn/post/7009329913534611486), 要介绍了以下几点:**
14 |
15 | - 辨析委托模式与代理模式
16 | - 接口委托(Delegated interface)
17 | - 属性委托(Delegated properties)
18 | - 映射委托(Map delegation)
19 | - 延迟属性(lazy properties)
20 | - 非空属性(Delegates.notNull)
21 | - 变量值更新后的监听(Delegates.observable)
22 | - 变量值更新前的拦截(Delegates.vetoable)
23 | - ViewBinding+属性委托
24 | - ViewModel+属性委托
25 | - SP+属性委托
26 |
27 | **借助 lazy 属性委托的优势:**
28 |
29 | - Kotlin 1.4 做的优化,当某些委托属性不会使用 KProperty。对于他们来说,在 $$delegatedProperties 中生成 KProperty 对象是多余的。Kotlin 1.4 版本将优化此类情况。如果委托的属性运算符是内联的,并且没有使用 KProperty 参数,则不会生成相应的反射对象。
30 | - 参考博客:[What to Expect in Kotlin 1.4 and Beyond | Optimized delegated properties](https://blog.jetbrains.com/kotlin/2019/12/what-to-expect-in-kotlin-1-4-and-beyond/)
31 |
32 |
33 |
34 |
35 | ## VBHelper 功能与使用
36 |
37 | ### 1. 在 Activity 中创建 ViewBinding 绑定类
38 |
39 | **反射和无反射两种使用方式如下:**
40 |
41 | 1. 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
42 |
43 | ```kotlin
44 | private val binding: ActivityMainBinding by vb()
45 | ```
46 |
47 | 2. 借助 lazy 属性委托 + 传递 inflate 方法引用
48 |
49 | ```kotlin
50 | private val binding: ActivityMainBinding by vb(ActivityMainBinding::inflate)
51 | ```
52 |
53 | **核心实现代码**
54 |
55 | ```kotlin
56 | class ActivityVBLazy(
57 | private val activity: ComponentActivity,
58 | private val kClass: KClass<*>,
59 | private val inflateMethodRef: ((LayoutInflater) -> T)?
60 | ) : Lazy {
61 | private var cachedBinding: T? = null
62 | override val value: T
63 | get() {
64 | var viewBinding = cachedBinding
65 | if (viewBinding == null) {
66 | viewBinding = if (inflateMethodRef != null) {
67 | //借助 lazy 属性委托 + 传递 inflate 方法引用
68 | inflateMethodRef.invoke(activity.layoutInflater)
69 | } else {
70 | //借助 lazy 属性委托 + 反射绑定类的 inflate 方法
71 | @Suppress("UNCHECKED_CAST")
72 | kClass.java.getMethod(METHOD_INFLATE, LayoutInflater::class.java)
73 | .invoke(null, activity.layoutInflater) as T
74 | }
75 | activity.setContentView(viewBinding.root)
76 | cachedBinding = viewBinding
77 | }
78 | return viewBinding
79 | }
80 | override fun isInitialized() = cachedBinding != null
81 | }
82 | ```
83 |
84 | ### 2. 在 Fragment 中创建 ViewBinding 绑定类
85 |
86 | **反射和无反射两种使用方式如下:**
87 |
88 | 1. 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
89 |
90 | ```kotlin
91 | private val binding: FragmentMainBinding by vb()
92 | ```
93 |
94 | 2. 借助 lazy 属性委托 + 传递 inflate 方法引用
95 |
96 | ```kotlin
97 | private val binding: FragmentMainBinding by vb(FragmentMainBinding::inflate)
98 | ```
99 |
100 | **核心实现代码**
101 |
102 | ```kotlin
103 | class FragmentVNLazy(
104 | private val fragment: Fragment,
105 | private val kClass: KClass<*>,
106 | private val inflateMethodRef: ((LayoutInflater) -> T)?
107 | ) : Lazy {
108 | private var cachedBinding: T? = null
109 | private val clearBindingHandler by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }
110 |
111 | init {
112 | observeFragmentDestroy(fragment) { clearBindingHandler.post { cachedBinding = null } }
113 | }
114 |
115 | override val value: T
116 | get() {
117 | var viewBinding = cachedBinding
118 | if (viewBinding == null) {
119 | checkBindingFirstInvoke(fragment)
120 | viewBinding = if (inflateMethodRef != null) {
121 | //借助 lazy 属性委托 + 传递 inflate 方法引用
122 | inflateMethodRef.invoke(fragment.layoutInflater)
123 | } else {
124 | //借助 lazy 属性委托 + 反射绑定类的 inflate 方法
125 | @Suppress("UNCHECKED_CAST")
126 | kClass.java.getMethod(METHOD_INFLATE, LayoutInflater::class.java)
127 | .invoke(null, fragment.layoutInflater) as T
128 | }
129 | cachedBinding = viewBinding
130 | }
131 | return viewBinding!!
132 | }
133 |
134 | override fun isInitialized() = cachedBinding != null
135 |
136 | }
137 | ```
138 |
139 | ### 3. 在 View 中创建 ViewBinding 绑定类
140 |
141 | **反射和无反射两种使用方式如下:**
142 |
143 | 1. 通过自定义属性代理 + 反射绑定类的 inflate 三参数方法
144 |
145 | ```kotlin
146 | private val binding: MyViewBinding by vb()
147 | ```
148 |
149 | 2. 通过自定义属性代理 + 传递 inflate 三参数方法引用
150 |
151 | ```kotlin
152 | private val binding: MyViewBinding by vb(MyViewBinding::inflate)
153 | ```
154 |
155 | ### 4. 在 Adapter 中创建包含了绑定类的 BindingViewHolder
156 |
157 | **反射和无反射两种使用方式如下:**
158 |
159 | 1. 通过自定义属性代理 + 反射绑定类的 inflate 三参数方法
160 |
161 | ```kotlin
162 | val holder: BindingViewHolder by vh(parent)
163 | ```
164 |
165 | 2. 通过自定义属性代理 + 传递绑定类的 inflate 三参数方法引用
166 |
167 | ```kotlin
168 | val holder: BindingViewHolder by vh(parent, LayoutItemTextBinding::inflate)
169 | ```
170 |
171 |
172 |
173 | ## VBHelper 集成
174 |
175 | [](https://jitpack.io/#jaydroid1024/VBHelper)
176 |
177 | ```groovy
178 | //Step 1. Add the JitPack repository to your build file
179 | dependencyResolutionManagement {
180 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
181 | repositories {
182 | maven { url 'https://jitpack.io' }
183 | ...
184 | }
185 | }
186 |
187 | //Step 2. Add the dependency
188 | dependencies {
189 | implementation 'com.github.jaydroid1024.VBHelper:vbhelper:0.0.7'
190 | //anyby 包含 vbhelper 之后会陆续将更多Kotlin 属性委托的最佳实践放在这里
191 | implementation 'com.github.jaydroid1024.VBHelper:anyby:0.0.7'
192 | }
193 |
194 | //Step 3. Add the ProGuard
195 |
196 | # proguard 官网
197 | #https://www.guardsquare.com/manual/configuration/examples
198 | #https://www.guardsquare.com/proguard
199 | #-keepclassmembers 作用只是保证类成员 ( 成员变量 , 成员方法 ) 不被混淆 , 类名还是会被混淆的
200 | -keepclassmembers class * implements androidx.viewbinding.ViewBinding {
201 | #只保留指定的几个方法,挺高代码混淆/压缩率
202 | public static * bind(android.view.View); #目前反射未采用bind的方式,这个可以不保留
203 | public static * inflate(android.view.LayoutInflater);
204 | public static * inflate(android.view.LayoutInflater, android.view.ViewGroup);
205 | public static * inflate(android.view.LayoutInflater, android.view.ViewGroup,boolean);
206 | }
207 |
208 | //推荐上面的混淆方式
209 | #keep 所有类成员,类名会被混淆
210 | #-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
211 | # *;
212 | #}
213 |
214 | #keep 类名和所有类成员不会被移除和混淆
215 | #-keepclasseswithmembers class * implements androidx.viewbinding.ViewBinding {
216 | # *;
217 | #}
218 |
219 | #keep 类名和类成员都不会被移除和混淆
220 | #-keep class * implements androidx.viewbinding.ViewBinding {
221 | # *;
222 | #}
223 |
224 | #keep 类名不会被移除和混淆,类成员都被混淆
225 | #-keep class * implements androidx.viewbinding.ViewBinding
226 |
227 |
228 |
229 |
230 | //Build artifacts:
231 | com.github.jaydroid1024.VBHelper:vbhelper:0.0.7
232 | com.github.jaydroid1024.VBHelper:anyby:0.0.7
233 |
234 | //Files:
235 | com/github/jaydroid1024/VBHelper/vbhelper/0.0.7
236 | com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7-sources.jar
237 | com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.aar
238 | com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.module
239 | com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.pom
240 | com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.pom.md5
241 | com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.pom.sha1
242 |
243 | com/github/jaydroid1024/VBHelper/anyby/0.0.7
244 | com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7-sources.jar
245 | com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.aar
246 | com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.module
247 | com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.pom
248 | com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.pom.md5
249 | com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.pom.sha1
250 |
251 | ```
252 |
253 |
254 |
255 | 更新记录
256 | ----------
257 |
258 |
259 | | 标签 | `New:` | `Upgrade: ` | `Fix:` |
260 | | :--: | :----------: | :---------: | :--------: |
261 | | 描述 | 新添加的功能 | 更新的功能 | 修复的功能 |
262 |
263 | ### V1.0.2
264 |
265 | ***2021-9-17***
266 |
267 | - `Upgrade: ` Activity 中获取VB的方式更新为:借助 Lazy 接口实现委托的方式
268 | - `Upgrade: ` Fragment 中获取VB的方式更新为:借助 Lazy 接口实现委托的方式
269 | - 借助 Lazy 接口实现委托的方式的优势可以避免生成多余的 KProperty 反射类
270 |
271 | ### V1.0.1
272 |
273 | ***2021-9-9***
274 |
275 | - `New:` 支持在 Activity 中创建 VB 绑定类
276 | - `New:` 支持在 Fragment 中创建 VB 绑定类
277 | - `New:` 支持在 View 中创建 VB 绑定类
278 | - `New:` 支持在 Adapter 中创建包含了绑定类的 BindingViewHolder
279 |
--------------------------------------------------------------------------------
/anyby/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/anyby/build.gradle:
--------------------------------------------------------------------------------
1 | group = "anyby"
2 | apply from: "../common.gradle"
3 |
4 | dependencies {
5 | api(project(":vbhelper"))
6 | // api 'com.github.jaydroid1024.anyby:vbhelper:0.0.7'
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/anyby/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/anyby/consumer-rules.pro
--------------------------------------------------------------------------------
/anyby/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
--------------------------------------------------------------------------------
/anyby/src/androidTest/java/com/jay/anyby/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.jay.anyby
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.jay.anyby.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/anyby/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/anyby/src/main/java/com/jay/anyby/api/InterfaceApiDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * 委托所有接口的实现类,通过动态代理实现
3 | * 使用方式如下:
4 | * private val lifecycleCallbacksWithDelegate = object : Application.ActivityLifecycleCallbacks by api() {}
5 | * 注意:目前只支持接口中无返回值的代理,返回值的方法还需要自己实现
6 | * @author jaydroid
7 | * @version 1.0
8 | * @date 2021/10/6
9 | */
10 |
11 | package com.jay.anyby.api
12 |
13 | import java.lang.reflect.InvocationHandler
14 | import java.lang.reflect.Proxy
15 |
16 | inline fun api(): T {
17 | val javaClass = T::class.java
18 | val noOpHandler = InvocationHandler { _, _, _ ->
19 | // no op
20 | }
21 | return Proxy.newProxyInstance(javaClass.classLoader, arrayOf(javaClass), noOpHandler) as T
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/anyby/src/main/java/com/jay/anyby/sp/PreferencesExt.kt:
--------------------------------------------------------------------------------
1 | package com.jay.anyby.sp
2 |
3 | import android.content.Context
4 | import kotlin.properties.ReadWriteProperty
5 | import kotlin.reflect.KProperty
6 |
7 | class Preference(
8 | val context: Context,
9 | val name: String,
10 | private val default: T,
11 | private val prefName: String = "default"
12 | ) : ReadWriteProperty {
13 |
14 | private val prefs by lazy {
15 | context.getSharedPreferences(prefName, Context.MODE_PRIVATE)
16 | }
17 |
18 | override fun getValue(thisRef: Any?, property: KProperty<*>): T {
19 | return findPreference(findProperName(property))
20 | }
21 |
22 | private fun findProperName(property: KProperty<*>) = if (name.isEmpty()) property.name else name
23 |
24 | private fun findPreference(key: String): T {
25 | @Suppress("UNCHECKED_CAST")
26 | return when (default) {
27 | is Long -> prefs.getLong(key, default)
28 | is Int -> prefs.getInt(key, default)
29 | is Boolean -> prefs.getBoolean(key, default)
30 | is String -> prefs.getString(key, default)
31 | else -> throw IllegalArgumentException("Unsupported type.")
32 | } as T
33 | }
34 |
35 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
36 | putPreference(findProperName(property), value)
37 | }
38 |
39 | private fun putPreference(key: String, value: T) {
40 | with(prefs.edit()) {
41 | when (value) {
42 | is Long -> putLong(key, value)
43 | is Int -> putInt(key, value)
44 | is Boolean -> putBoolean(key, value)
45 | is String -> putString(key, value)
46 | else -> throw IllegalArgumentException("Unsupported type.")
47 | }
48 | }.apply()
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/anyby/src/main/java/com/jay/anyby/sp/SPDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.jay.anyby.sp
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.SharedPreferences
5 | import kotlin.properties.ReadWriteProperty
6 | import kotlin.reflect.KProperty
7 |
8 | /**
9 | * @author jaydroid
10 | * @version 1.0
11 | * @date 2021/9/18
12 | */
13 |
14 | fun SharedPreferences.int(def: Int = 0, key: String? = null) =
15 | delegate(def, key, SharedPreferences::getInt, SharedPreferences.Editor::putInt)
16 |
17 | fun SharedPreferences.long(def: Long = 0, key: String? = null) =
18 | delegate(def, key, SharedPreferences::getLong, SharedPreferences.Editor::putLong)
19 |
20 | fun SharedPreferences.string(def: String = "", key: String? = null) =
21 | delegate(def, key, SharedPreferences::getString, SharedPreferences.Editor::putString)
22 |
23 |
24 | private inline fun SharedPreferences.delegate(
25 | defaultValue: T,
26 | key: String?,
27 | crossinline getter: SharedPreferences.(String, T) -> T,
28 | crossinline setter: SharedPreferences.Editor.(String, T) -> SharedPreferences.Editor
29 | ) = object : ReadWriteProperty {
30 | override fun getValue(thisRef: Any, property: KProperty<*>) =
31 | getter(key ?: property.name, defaultValue)
32 |
33 | @SuppressLint("CommitPrefEdits")
34 | override fun setValue(thisRef: Any, property: KProperty<*>, value: T) =
35 | edit().setter(key ?: property.name, value).apply()
36 | }
37 |
--------------------------------------------------------------------------------
/anyby/src/main/java/com/jay/anyby/vm/ActivityVMDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ActivityVMDelegate
3 | * @author jaydroid
4 | * @version 1.0
5 | * @date 2021/9/17
6 | */
7 | @file:kotlin.jvm.JvmName("ActivityVMDelegateKt")
8 |
9 | package com.jay.anyby.vm
10 |
11 | import androidx.activity.ComponentActivity
12 | import androidx.annotation.MainThread
13 | import androidx.lifecycle.ViewModel
14 | import androidx.lifecycle.ViewModelProvider
15 | import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
16 | import kotlin.reflect.KClass
17 |
18 | /**
19 | * 返回一个 Lazy 委托来访问 ComponentActivity 的 ViewModel,如果指定了工厂,它将用于第一次创建ViewModel。
20 | * class MyComponentActivity : ComponentActivity() {
21 | * val viewmodel: MyViewModel by vm()
22 | * }
23 | * 只有在 Activity attached 到应用程序后才能访问此属性,在此之前访问将导致 IllegalArgumentException
24 | *
25 | * @param VM NyViewModel
26 | * @param factory AndroidViewModelFactory
27 | * @return viewModelInstance
28 | */
29 |
30 | @MainThread
31 | inline fun ComponentActivity.vm(
32 | factory: ViewModelProvider.Factory? = null
33 | ): Lazy = ActivityVMLazy(this, VM::class, factory)
34 |
35 | /**
36 | * ComponentActivity VM 使用实现 Lazy 接口的方式实现委托并与给定的 activity、 viewModelClass 、factory 相关联
37 | * @param VM NyViewModel
38 | * @property activity MyFragment
39 | * @property viewModelClass viewModelClass
40 | * @property factory AndroidViewModelFactory
41 | */
42 | class ActivityVMLazy(
43 | private val activity: ComponentActivity,
44 | private val viewModelClass: KClass,
45 | private val factory: ViewModelProvider.Factory?
46 | ) : Lazy {
47 | private var cached: VM? = null
48 | override val value: VM
49 | get() {
50 | var viewModel = cached
51 | if (viewModel == null) {
52 | val application = activity.application
53 | ?: throw IllegalArgumentException(
54 | "ViewModel can be accessed only when Activity is attached"
55 | )
56 | val resolvedFactory = factory ?: AndroidViewModelFactory.getInstance(application)
57 | viewModel = ViewModelProvider(activity, resolvedFactory).get(viewModelClass.java)
58 | cached = viewModel
59 | }
60 | return viewModel
61 | }
62 |
63 | override fun isInitialized() = cached != null
64 | }
65 |
66 |
67 | /**
68 | * Returns a [Lazy] delegate to access the ComponentActivity's ViewModel, if [factoryProducer]
69 | * is specified then [ViewModelProvider.Factory] returned by it will be used
70 | * to create [ViewModel] first time.
71 | *
72 | * ```
73 | * class MyComponentActivity : ComponentActivity() {
74 | * val viewmodel: MyViewModel by viewmodels()
75 | * }
76 | * ```
77 | *
78 | * This property can be accessed only after the Activity is attached to the Application,
79 | * and access prior to that will result in IllegalArgumentException.
80 | */
81 | @MainThread
82 | inline fun ComponentActivity.vm2(
83 | noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
84 | ): Lazy {
85 |
86 | val factoryPromise = factoryProducer ?: {
87 | defaultViewModelProviderFactory
88 | }
89 | return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
90 | }
--------------------------------------------------------------------------------
/anyby/src/main/java/com/jay/anyby/vm/FragmentVMDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * FragmentVMDelegateKt
3 | * @author jaydroid
4 | * @version 1.0
5 | * @date 2021/9/17
6 | */
7 | @file:kotlin.jvm.JvmName("FragmentVMDelegateKt")
8 |
9 | package com.jay.anyby.vm
10 |
11 | import androidx.annotation.MainThread
12 | import androidx.fragment.app.Fragment
13 | import androidx.lifecycle.ViewModel
14 | import androidx.lifecycle.ViewModelProvider
15 | import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
16 | import androidx.lifecycle.ViewModelProvider.Factory
17 | import androidx.lifecycle.ViewModelStore
18 | import androidx.lifecycle.ViewModelStoreOwner
19 | import kotlin.reflect.KClass
20 |
21 |
22 | /**
23 | * 返回一个属性委托来访问 Activity 的ViewModel ,如果指定了工厂,它将第一次用于创建ViewModel 。
24 | * class MyFragment : Fragment() {
25 | * val viewmodel: NyViewModel by vm()
26 | * }
27 | * 只有在 Fragment attached 之后才能访问此属性,即在 Fragment.onAttach() 之后,在此之前访问将导致 IllegalArgumentException。
28 | *
29 | * @param VM NyViewModel
30 | * @param factory AndroidViewModelFactory
31 | * @return viewModelInstance
32 | */
33 | @MainThread
34 | inline fun Fragment.vm(factory: Factory? = null): Lazy =
35 | FragmentVMLazy(this, VM::class, factory)
36 |
37 |
38 | /**
39 | * Fragment VM 使用实现 Lazy 接口的方式实现委托并与给定的 fragment、 viewModelClass 、factory 相关联
40 | * @param VM NyViewModel
41 | * @property fragment MyFragment
42 | * @property viewModelClass viewModelClass
43 | * @property factory AndroidViewModelFactory
44 | */
45 | class FragmentVMLazy(
46 | private val fragment: Fragment,
47 | private val viewModelClass: KClass,
48 | private val factory: Factory?
49 | ) : Lazy {
50 | private var cached: VM? = null
51 | override val value: VM
52 | get() {
53 | var viewModel = cached
54 | if (viewModel == null) {
55 | val application = fragment.activity?.application
56 | ?: throw IllegalArgumentException(
57 | "ViewModel can be accessed only when Fragment is attached"
58 | )
59 | val resolvedFactory = factory ?: AndroidViewModelFactory.getInstance(application)
60 | viewModel = ViewModelProvider(fragment, resolvedFactory).get(viewModelClass.java)
61 | cached = viewModel
62 | }
63 | return viewModel
64 | }
65 |
66 | override fun isInitialized() = cached != null
67 | }
68 |
69 |
70 | /**
71 | * Returns a property delegate to access [ViewModel] by **default** scoped to this [Fragment]:
72 | * ```
73 | * class MyFragment : Fragment() {
74 | * val viewmodel: MYViewModel by viewmodels()
75 | * }
76 | * ```
77 | *
78 | * Custom [ViewModelProvider.Factory] can be defined via [factoryProducer] parameter,
79 | * factory returned by it will be used to create [ViewModel]:
80 | * ```
81 | * class MyFragment : Fragment() {
82 | * val viewmodel: MYViewModel by viewmodels { myFactory }
83 | * }
84 | * ```
85 | *
86 | * Default scope may be overridden with parameter [ownerProducer]:
87 | * ```
88 | * class MyFragment : Fragment() {
89 | * val viewmodel: MYViewModel by viewmodels ({requireParentFragment()})
90 | * }
91 | * ```
92 | *
93 | * This property can be accessed only after this Fragment is attached i.e., after
94 | * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
95 | */
96 | @MainThread
97 | inline fun Fragment.vm2(
98 | noinline ownerProducer: () -> ViewModelStoreOwner = { this },
99 | noinline factoryProducer: (() -> Factory)? = null
100 | ) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
101 |
102 | /**
103 | * Returns a property delegate to access parent activity's [ViewModel],
104 | * if [factoryProducer] is specified then [ViewModelProvider.Factory]
105 | * returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
106 | * [androidx.activity.ComponentActivity.getDefaultViewModelProviderFactory](default factory)
107 | * will be used.
108 | *
109 | * ```
110 | * class MyFragment : Fragment() {
111 | * val viewmodel: MyViewModel by activityViewModels()
112 | * }
113 | * ```
114 | *
115 | * This property can be accessed only after this Fragment is attached i.e., after
116 | * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
117 | */
118 | @MainThread
119 | inline fun Fragment.activityViewModels(
120 | noinline factoryProducer: (() -> Factory)? = null
121 | ) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
122 | factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
123 |
124 | /**
125 | * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
126 | * to default factory.
127 | */
128 | @MainThread
129 | fun Fragment.createViewModelLazy(
130 | viewModelClass: KClass,
131 | storeProducer: () -> ViewModelStore,
132 | factoryProducer: (() -> Factory)? = null
133 | ): Lazy {
134 | val factoryPromise = factoryProducer ?: {
135 | defaultViewModelProviderFactory
136 | }
137 | return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
138 | }
--------------------------------------------------------------------------------
/anyby/src/main/java/com/jay/anyby/vm/VMDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.jay.anyby.vm
2 |
3 | /**
4 | * @author jaydroid
5 | * @version 1.0
6 | * @date 2021/9/17
7 | */
8 | import androidx.annotation.MainThread
9 | import androidx.lifecycle.ViewModel
10 | import androidx.lifecycle.ViewModelProvider
11 | import androidx.lifecycle.ViewModelStore
12 | import kotlin.reflect.KClass
13 |
14 | /**
15 | * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
16 | * an activity), associated with this `ViewModelProvider`.
17 | *
18 | * @see ViewModelProvider.get(Class)
19 | */
20 | @MainThread
21 | public inline fun ViewModelProvider.get(): VM = get(VM::class.java)
22 |
23 | /**
24 | * An implementation of [Lazy] used by [androidx.fragment.app.Fragment.viewModels] and
25 | * [androidx.activity.ComponentActivity.viewmodels].
26 | *
27 | * [storeProducer] is a lambda that will be called during initialization, [VM] will be created
28 | * in the scope of returned [ViewModelStore].
29 | *
30 | * [factoryProducer] is a lambda that will be called during initialization,
31 | * returned [ViewModelProvider.Factory] will be used for creation of [VM]
32 | */
33 | public class ViewModelLazy(
34 | private val viewModelClass: KClass,
35 | private val storeProducer: () -> ViewModelStore,
36 | private val factoryProducer: () -> ViewModelProvider.Factory
37 | ) : Lazy {
38 | private var cached: VM? = null
39 |
40 | override val value: VM
41 | get() {
42 | val viewModel = cached
43 | return if (viewModel == null) {
44 | val factory = factoryProducer()
45 | val store = storeProducer()
46 | ViewModelProvider(store, factory).get(viewModelClass.java).also {
47 | cached = it
48 | }
49 | } else {
50 | viewModel
51 | }
52 | }
53 |
54 | override fun isInitialized(): Boolean = cached != null
55 | }
--------------------------------------------------------------------------------
/anyby/src/test/java/com/jay/anyby/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.jay.anyby
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun 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 | buildscript {
3 | //使用config.gradle统一管理项目的依赖库
4 | apply from: "config.gradle"
5 | repositories {
6 | google()
7 | mavenCentral()
8 | maven { url 'https://jitpack.io' }
9 |
10 | }
11 | dependencies {
12 | classpath "com.android.tools.build:gradle:7.0.2"
13 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30'
14 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
15 | }
16 | }
17 |
18 | task clean(type: Delete) {
19 | delete rootProject.buildDir
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/common.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'maven-publish'
4 |
5 | android {
6 | buildFeatures {
7 | viewBinding true
8 | }
9 | compileSdk rootProject.ext.compileSdk
10 | defaultConfig {
11 | minSdkVersion rootProject.ext.minSdk
12 | targetSdkVersion rootProject.ext.targetSdk
13 | versionCode rootProject.ext.versionCode
14 | versionName rootProject.ext.versionName
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles "consumer-rules.pro"
17 | }
18 | compileOptions {
19 | sourceCompatibility rootProject.ext.javaSourceCompatibility
20 | targetCompatibility rootProject.ext.javaSourceCompatibility
21 | }
22 |
23 | kotlinOptions {
24 | jvmTarget = rootProject.ext.kotlinOptionsJvmTarget
25 | }
26 |
27 | buildTypes {
28 | release {
29 | minifyEnabled false
30 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: 'libs', include: ['*.jar'])
37 | implementation rootProject.ext.kotlinStdlibJdk8
38 | implementation rootProject.ext.appcompat
39 | implementation rootProject.ext.material
40 | implementation rootProject.ext.coreKtx
41 | testImplementation rootProject.ext.junit
42 | androidTestImplementation rootProject.ext.junitX
43 | androidTestImplementation rootProject.ext.espressoCore
44 | }
45 |
46 | // 唯一标识 每个组件都要指定
47 | def ARTIFACT_ID = group.toString()
48 |
49 | afterEvaluate {
50 | publishing {
51 | publications {
52 | // Creates a Maven publication called "release".
53 | release(MavenPublication) {
54 | println ARTIFACT_ID
55 | from components.release
56 | groupId = 'com.github.jaydroid1024'
57 | artifactId = ARTIFACT_ID
58 | }
59 | }
60 | }
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/config.gradle:
--------------------------------------------------------------------------------
1 | // ext属性是ExtensionAware类型的一个特殊的属性,本质是一个Map类型的变量,
2 | // 而 ExtentionAware接口的实现类为Project, Settings, Task, SourceSet等,
3 | // ExtentionAware 可以在运行时扩充属性,而这里的ext,就是里面的一个特殊的属性而已。
4 | ext {
5 |
6 |
7 | android = [
8 | compileSdk= 30,
9 | buildToolsVersion = '30.0.3',
10 | minSdk = 21,
11 | targetSdk = 30,
12 | versionCode = 1,
13 | versionName = "1.0.0",
14 |
15 | // Application ID
16 | prodApplicationId = 'id.tix.android',
17 | apiaryApplicationId = 'id.tix.android.apiary',
18 | devApplicationId = 'id.tix.android.dev',
19 | ]
20 |
21 |
22 | java = [
23 | javaSourceCompatibility = JavaVersion.VERSION_1_8,
24 | javaTargetCompatibility = JavaVersion.VERSION_1_8
25 | ]
26 |
27 | kotlin = [
28 | kotlinVersion = '1.5.30',
29 | kotlinOptionsJvmTarget = JavaVersion.VERSION_1_8.toString()
30 | ]
31 |
32 |
33 | //三方依赖库多个依赖路径引入同一个版本号时需要提取版本号
34 | version = [
35 | kotlinVersion = '1.5.10',
36 | stethoVersion = '1.5.1',
37 | retrofitVersion = '2.8.1',
38 | okhttpVersion = '4.5.0',
39 | glideVersion = '4.11.0',
40 | chuckVersion = '1.1.0',
41 | chuckerVersion = '3.2.0',
42 | butterknifeVersion = '10.2.1',
43 | immersionbarVersion = '3.0.0',
44 | tinkerVersion = '1.9.14',
45 | jDispatcherVersion = '0.0.7',
46 | epoxyVersion = '4.6.2'
47 |
48 | ]
49 |
50 | //引入三方依赖库时考虑该库到稳定性,可维护性,需要标明引入到github地址并归类放置
51 | dependencies = [
52 |
53 | /** ========================================================= */
54 | /** =================== android/androidX ==================== */
55 | /** ========================================================= */
56 | kotlinStdlibJdk7 = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion",
57 | appcompat = 'androidx.appcompat:appcompat:1.3.1',
58 | coreKtx = 'androidx.core:core-ktx:1.6.0',
59 | material = 'com.google.android.material:material:1.4.0',
60 | junit = 'junit:junit:4.12',
61 | junitX = 'androidx.test.ext:junit:1.1.1',
62 | espressoCore = 'androidx.test.espresso:espresso-core:3.2.0',
63 | androidJUnitRunner = "android.support.test.runner.AndroidJUnitRunner",
64 | constraintLayout = "androidx.constraintlayout:constraintlayout:1.1.3",
65 |
66 | /** ========================================================= */
67 | /** ========================= kotlin ======================== */
68 | /** ========================================================= */
69 | //kotlin-stdlib-jdk7
70 | kotlinStdlibJdk7 = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion",
71 | //kotlin-stdlib-jdk8
72 | kotlinStdlibJdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion",
73 | //anko-sqlite
74 | ankoSqlite = "org.jetbrains.anko:anko-sqlite:0.10.5",
75 | //kotlin-reflect
76 | kotlinReflect = "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion",
77 |
78 |
79 | /** ========================================================= */
80 | /** ========================== tools ======================== */
81 | /** ========================================================= */
82 | //stetho 调试工具 https://github.com/facebook/stetho
83 | stetho = "com.facebook.stetho:stetho:$stethoVersion",
84 | //stetho 网络调试 https://github.com/facebook/stetho
85 | stethoOkhttp3 = "com.facebook.stetho:stetho-okhttp3:$stethoVersion",
86 | //stetho 网络调试 https://github.com/facebook/stetho
87 | stethoUrlconnection = "com.facebook.stetho:stetho-urlconnection:$stethoVersion",
88 |
89 | //Chuck Okhttp日志打印工具 https://github.com/jgilfelt/chuck
90 | chuckDebug = "com.readystatesoftware.chuck:library:$chuckVersion",
91 | //Chuck Okhttp日志打印工具 https://github.com/jgilfelt/chuck
92 | chuckRelease = "com.readystatesoftware.chuck:library-no-op:$chuckVersion",
93 | //Chucker Okhttp日志和Android异常信息打印工具 https://github.com/ChuckerTeam/chucker
94 | ChuckerDebug = "com.github.ChuckerTeam.Chucker:library:$chuckerVersion",
95 | //Chucker Okhttp日志和Android异常信息打印工具 https://github.com/ChuckerTeam/chucker
96 | ChuckerRelease = "com.github.ChuckerTeam.Chucker:library-no-op:$chuckerVersion",
97 |
98 | //logger https://github.com/orhanobut/logger
99 | logger = "com.orhanobut:logger:2.2.0",
100 |
101 | //arouter api https://github.com/alibaba/ARouter
102 | arouterApi = "com.alibaba:arouter-api:1.5.2",
103 | //arouter compiler https://github.com/alibaba/ARouter
104 | arouterCompiler = "com.alibaba:arouter-compiler:1.2.2",
105 |
106 | //butterknife https://github.com/JakeWharton/butterknife
107 | butterknife = "com.jakewharton:butterknife:$butterknifeVersion",
108 | //butterknifeCompiler https://github.com/JakeWharton/butterknife
109 | butterknifeCompiler = "com.jakewharton:butterknife-compiler:$butterknifeVersion",
110 |
111 | //eventbus https://github.com/greenrobot/EventBus
112 | eventbus = 'org.greenrobot:eventbus:3.2.0',
113 | //greendao https://github.com/greenrobot/greenDAO
114 | greendao = 'org.greenrobot:greendao:3.2.2',
115 |
116 | //gson https://github.com/google/gson
117 | gson = 'com.google.code.gson:gson:2.8.6',
118 |
119 |
120 | jdispatcherApi = "com.github.jaydroid1024.JDispatcher:jdispatcher-api:$jDispatcherVersion",
121 | jdispatcherCompiler = "com.github.jaydroid1024.JDispatcher:jdispatcher-compiler:$jDispatcherVersion",
122 | jdispatcherPlugin = "com.github.jaydroid1024.JDispatcher:jdispatcher-plugin:$jDispatcherVersion",
123 |
124 |
125 | /** ========================================================= */
126 | /** ========================= network ======================= */
127 | /** ========================================================= */
128 | //retrofit https://github.com/square/retrofit
129 | retrofit = "com.squareup.retrofit2:retrofit:$retrofitVersion",
130 | //retrofit converter-gson https://github.com/square/retrofit/blob/master/retrofit-converters/gson/README.md
131 | retrofitConverterGson = "com.squareup.retrofit2:converter-gson:$retrofitVersion",
132 | //retrofit adapter-rxjava https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2
133 | retrofitAdapterRxjava = "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion",
134 | // Retrofit 同时支持多个 BaseUrl 以及动态改变 BaseUrl(已知问题:不同url 的https证书问题) https://github.com/JessYanCoding/RetrofitUrlManager
135 | retrofitUrlManager = "me.jessyan:retrofit-url-manager:1.4.0",
136 |
137 | //okhttp https://github.com/square/okhttp
138 | okhttp = "com.squareup.okhttp3:okhttp:$okhttpVersion",
139 | //okhttp logging-interceptor https://github.com/square/okhttp/blob/master/okhttp-logging-interceptor/README.md
140 | okhttpLoggingInterceptor = "com.squareup.okhttp3:logging-interceptor:$okhttpVersion",
141 | // PersistentCookieJar https://github.com/franmontiel/PersistentCookieJar
142 | persistentcookiejar = "com.github.franmontiel:PersistentCookieJar:v1.0.1",
143 |
144 | //rxjava https://github.com/ReactiveX/RxJava todo rxjava:3.x.x
145 | rxjava = 'io.reactivex.rxjava2:rxjava:2.2.10',
146 | //rxandroid https://github.com/ReactiveX/RxAndroid todo rxandroid:3.x.x
147 | rxandroid = 'io.reactivex.rxjava2:rxandroid:2.1.1',
148 |
149 | //glide 图片加载 https://github.com/bumptech/glide
150 | glide = "com.github.bumptech.glide:glide:$glideVersion",
151 | //glide图片加载注解 https://github.com/bumptech/glide
152 | glideAnnotationProcessor = "com.github.bumptech.glide:compiler:$glideVersion",
153 |
154 |
155 | /** ========================================================= */
156 | /** ========================== UI相关 ======================== */
157 | /** ========================================================= */
158 | // statusbarUtil https://github.com/laobie/StatusBarUtil
159 | statusbarUtil = "com.jaeger.statusbarutil:library:1.5.1",
160 |
161 | // 基础依赖包,必须要依赖 https://github.com/gyf-dev/ImmersionBar
162 | immersionbar = "com.gyf.immersionbar:immersionbar:$immersionbarVersion",
163 | // fragment快速实现(可选) https://github.com/gyf-dev/ImmersionBar
164 | immersionbarComponents = "com.gyf.immersionbar:immersionbar-components:$immersionbarVersion",
165 | // kotlin扩展(可选) https://github.com/gyf-dev/ImmersionBar
166 | immersionbarKtx = "com.gyf.immersionbar:immersionbar-ktx:$immersionbarVersion",
167 | //
168 | recyclerAdapterHelper = "com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.45-androidx",
169 | //
170 | smartRefreshLayout = "com.scwang.smartrefresh:SmartRefreshLayout:1.1.0",
171 | //
172 | banner = "com.youth.banner:banner:1.4.10",
173 | //
174 | lottie = "com.airbnb.android:lottie:2.7.0",
175 |
176 | //https://github.com/airbnb/epoxy
177 | epoxy = "com.airbnb.android:epoxy:$epoxyVersion",
178 | // Add the annotation processor if you are using Epoxy's annotations (recommended)
179 | epoxyProcessor = "com.airbnb.android:epoxy-processor:$epoxyVersion",
180 |
181 |
182 | /** ========================================================= */
183 | /** ========================== 三方SDK ======================= */
184 | /** ========================================================= */
185 | //tinker热修复:tinker的核心库
186 | tinkerLib = "com.tencent.tinker:tinker-android-lib:$tinkerVersion",
187 | tinkerLoader = "com.tencent.tinker:tinker-android-loader:$tinkerVersion",
188 | //tinker热修复:可选,用于生成application类
189 | tinkerAnno = "com.tencent.tinker:tinker-android-anno:$tinkerVersion",
190 | //
191 | wechat = "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+",
192 |
193 |
194 | /** ========================================================= */
195 | /** ======================== components ===================== */
196 | /** ========================================================= */
197 | //maven components
198 | base_lib = 'com.jaydroid.base_lib:base_lib:1.0.0',
199 | base_component = 'com.jaydroid.base_component:base_component:2.0.0',
200 | base_component_wan = 'com.jaydroid.base_component_wan:base_component_wan:2.0.0',
201 | main = "com.jaydroid.main:main:1.0.0",
202 | login = "com.jaydroid.login:login:1.0.0",
203 |
204 | ]
205 |
206 |
207 | }
208 |
209 |
210 | /**
211 | * 获取属性总配置文件
212 | * @return Properties实例 config
213 | */
214 | Properties getConfigProperties() {
215 | def CONFIG_PATH = "./buildsystem/config.properties"
216 | return getProperties(CONFIG_PATH)
217 | }
218 |
219 | /**
220 | * 获取属性配置文件公共方法
221 | * @param propPath Properties配置文件路径
222 | * @return Properties实例
223 | */
224 | Properties getProperties(String propPath) {
225 | Properties props = new Properties()
226 | props.load(new FileInputStream(file(propPath)))
227 | return props
228 | }
229 |
230 | /**
231 | * 获取签名配置文件
232 | * @return Properties实例 签名
233 | */
234 | Properties getKeyProperties() {
235 | Properties configProperties = getConfigProperties()
236 | // 使用assert确保存在该属性否则报错,避免错误打包
237 | assert configProperties['keystore.props.file']
238 | return getProperties(configProperties.getProperty("keystore.props.file"))
239 | }
240 |
241 | /**
242 | * 获取Maven仓库配置文件
243 | * @return Properties实例 Maven
244 | */
245 | Properties getMavenProperties() {
246 | Properties configProperties = getConfigProperties()
247 | // 使用assert确保存在该属性否则报错,避免错误打包
248 | assert configProperties['maven.props.file']
249 | return getProperties(configProperties.getProperty("maven.props.file"))
250 | }
251 |
252 | /**
253 | * 获取当前指定的环境类型,缺省dev
254 | * @return
255 | */
256 | String getEnvironmentPropertyType() {
257 | Properties localProperties = getConfigProperties()
258 | return localProperties.getProperty("propertyType", "dev")
259 | }
260 |
261 | //根据环境类型获取环境配置文件
262 | Properties getEnvironmentProperties(String propertyType) {
263 | Properties localProperties = getConfigProperties()
264 | String environmentPath = "${propertyType}.props.file"
265 | // 使用assert确保存在该属性否则报错,避免错误打包
266 | assert localProperties[environmentPath]
267 | return getProperties(localProperties.getProperty(environmentPath))
268 | }
269 |
270 |
271 |
--------------------------------------------------------------------------------
/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=-Xmx2048m -Dfile.encoding=UTF-8
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 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Sep 03 14:21:49 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk: openjdk11
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | maven { url 'https://jitpack.io' }
7 | jcenter() // Warning: this repository is going to shut down soon
8 | }
9 | }
10 | rootProject.name = "anyby"
11 |
12 | include ':simple'
13 | include ':vbhelper'
14 | include ':anyby'
15 |
--------------------------------------------------------------------------------
/simple/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/simple/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | //启用视图绑定的标志,默认值为false。Android Studio 3.6 Canary 11 及更高版本中可用。
8 | viewBinding { enabled = true }
9 | //启用数据绑定的标志。
10 | //If you plan to use data binding in a Kotlin project, you should apply the kotlin-kapt plugin.
11 | // dataBinding {
12 | // enabled = true
13 | // }
14 |
15 | //buildFeatures: 可以在 Android 项目中禁用或启用的构建功能列表。此列表适用于所有插件类型。
16 | //其它功能开关:https://developer.android.com/reference/tools/gradle-api/7.0/com/android/build/api/dsl/BuildFeatures
17 | buildFeatures {
18 | //启用视图绑定的标志,默认值为false。// Android Studio 4.0 及更高版本中可用。
19 | //[viewBinding = true 是 kotlin 语法] 会有这个警告 Access to 'viewBinding' exceeds its access rights
20 | viewBinding true
21 | ////启用数据绑定的标志,默认值为false。
22 | // dataBinding = true
23 | //标记以启用撰写功能,默认值为false。
24 | // compose = true
25 | }
26 |
27 |
28 | compileSdk rootProject.ext.compileSdk
29 |
30 | defaultConfig {
31 | applicationId "com.jay.vbhelper.simple"
32 | minSdkVersion rootProject.ext.minSdk
33 | targetSdkVersion rootProject.ext.targetSdk
34 | versionCode rootProject.ext.versionCode
35 | versionName rootProject.ext.versionName
36 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
37 | }
38 | compileOptions {
39 | sourceCompatibility rootProject.ext.javaSourceCompatibility
40 | targetCompatibility rootProject.ext.javaSourceCompatibility
41 | }
42 |
43 | kotlinOptions {
44 | jvmTarget = rootProject.ext.kotlinOptionsJvmTarget
45 | }
46 |
47 | buildTypes {
48 | debug {
49 | minifyEnabled true
50 | zipAlignEnabled true
51 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
52 | }
53 |
54 | release {
55 | minifyEnabled true
56 | zipAlignEnabled true
57 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
58 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
59 | }
60 | }
61 |
62 | lintOptions {
63 | abortOnError false
64 | }
65 |
66 | }
67 |
68 | dependencies {
69 |
70 | implementation 'androidx.core:core-ktx:1.6.0'
71 | implementation 'androidx.appcompat:appcompat:1.3.1'
72 | implementation 'com.google.android.material:material:1.4.0'
73 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
74 |
75 | implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
76 | implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
77 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
78 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
79 |
80 | testImplementation 'junit:junit:4.13.2'
81 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
82 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
83 |
84 |
85 | implementation(project(":anyby"))
86 | // implementation 'com.github.jaydroid1024.anyby:anyby:0.0.7'
87 |
88 | // implementation 'com.github.jaydroid1024:VBHelper:0.0.1'
89 |
90 |
91 | //todo 依赖 databinding-compiler 方便查看 ViewBinding 类的生成过程
92 | // https://mvnrepository.com/artifact/androidx.databinding/databinding-compiler-common
93 | // implementation group: 'androidx.databinding', name: 'databinding-compiler-common', version: '7.0.1'
94 | // // https://mvnrepository.com/artifact/com.android.tools.build/gradle
95 | // implementation group: 'com.android.tools.build', name: 'gradle', version: '7.0.1'
96 | //git push origin :refs/tags/0.0.3
97 |
98 |
99 | }
--------------------------------------------------------------------------------
/simple/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 |
23 |
24 | # proguard 官网
25 | #https://www.guardsquare.com/manual/configuration/examples
26 | #https://www.guardsquare.com/proguard
27 | #-keepclassmembers 作用只是保证类成员 ( 成员变量 , 成员方法 ) 不被混淆 , 类名还是会被混淆的
28 | -keepclassmembers class * implements androidx.viewbinding.ViewBinding {
29 | #只保留指定的几个方法,挺高代码混淆/压缩率
30 | public static * bind(android.view.View); #目前反射未采用bind的方式,这个可以不保留
31 | public static * inflate(android.view.LayoutInflater);
32 | public static * inflate(android.view.LayoutInflater, android.view.ViewGroup);
33 | public static * inflate(android.view.LayoutInflater, android.view.ViewGroup,boolean);
34 | }
35 |
36 | #keep 所有类成员,类名会被混淆
37 | #-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
38 | # *;
39 | #}
40 |
41 | #keep 类名和所有类成员不会被移除和混淆
42 | #-keepclasseswithmembers class * implements androidx.viewbinding.ViewBinding {
43 | # *;
44 | #}
45 |
46 | #keep 类名和类成员都不会被移除和混淆
47 | #-keep class * implements androidx.viewbinding.ViewBinding {
48 | # *;
49 | #}
50 |
51 | #keep 类名不会被移除和混淆,类成员都被混淆
52 | #-keep class * implements androidx.viewbinding.ViewBinding
53 |
54 |
55 |
--------------------------------------------------------------------------------
/simple/src/androidTest/java/com/jay/vbhelper/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.jay.vbhelper", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/simple/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
19 |
23 |
27 |
31 |
35 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/SimpleActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.jay.vbhelper.delegate.vb
7 | import com.jay.vbhelper.simple.databinding.ActivitySimpleBinding
8 | import com.jay.vbhelper.simple.delagate_vb.MainActivityWithDelegate
9 | import com.jay.vbhelper.simple.delegate_api.ApiTestActivity
10 | import com.jay.vbhelper.simple.delegate_sp.SPTestActivity
11 | import com.jay.vbhelper.simple.delegate_vm.ViewModelTestActivity
12 | import com.jay.vbhelper.simple.inflate_test.InflateTestActivity
13 | import com.jay.vbhelper.simple.normal_use_vb.MainActivity
14 |
15 | /**
16 | * @author jaydroid
17 | * @version 1.0
18 | * @date 2021/9/3
19 | */
20 | class SimpleActivity : AppCompatActivity() {
21 |
22 | private val binding: ActivitySimpleBinding by vb()
23 |
24 | override fun onCreate(savedInstanceState: Bundle?) {
25 | super.onCreate(savedInstanceState)
26 | setSupportActionBar(binding.toolbar)
27 | }
28 |
29 |
30 | fun normalUseVb(view: android.view.View) {
31 | startActivity(Intent(this, MainActivity::class.java))
32 | }
33 |
34 | fun delegateVB(view: android.view.View) {
35 | startActivity(Intent(this, MainActivityWithDelegate::class.java))
36 | }
37 |
38 | fun inflateTest(view: android.view.View) {
39 | startActivity(Intent(this, InflateTestActivity::class.java))
40 | }
41 |
42 | fun delegateSP(view: android.view.View) {
43 | startActivity(Intent(this, SPTestActivity::class.java))
44 | }
45 |
46 | fun delegateVM(view: android.view.View) {
47 | startActivity(Intent(this, ViewModelTestActivity::class.java))
48 | }
49 |
50 | fun delegateApi(view: android.view.View) {
51 | startActivity(Intent(this, ApiTestActivity::class.java))
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delagate_vb/CustomViewWithDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delagate_vb
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.util.AttributeSet
6 | import android.widget.LinearLayout
7 | import com.jay.vbhelper.delegate.vb
8 | import com.jay.vbhelper.simple.databinding.LayoutViewBinding
9 |
10 |
11 | @SuppressLint("SetTextI18n")
12 | class CustomViewWithDelegate @JvmOverloads constructor(
13 | context: Context,
14 | attrs: AttributeSet? = null,
15 | defStyleAttr: Int = 0
16 | ) : LinearLayout(context, attrs, defStyleAttr) {
17 |
18 | private val binding: LayoutViewBinding by vb()
19 | // private val binding: LayoutViewBinding by vb(LayoutViewBinding::inflate)
20 |
21 | fun setName(name: String) {
22 | binding.firstName.text = name
23 | }
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delagate_vb/MainActivityWithDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delagate_vb
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.navigation.findNavController
6 | import androidx.navigation.ui.AppBarConfiguration
7 | import androidx.navigation.ui.navigateUp
8 | import androidx.navigation.ui.setupActionBarWithNavController
9 | import com.jay.vbhelper.delegate.vb
10 | import com.jay.vbhelper.simple.R
11 | import com.jay.vbhelper.simple.databinding.ActivityMainBinding
12 |
13 | /**
14 | * @author jaydroid
15 | * @version 1.0
16 | * @date 2021/9/3
17 | */
18 | class MainActivityWithDelegate : AppCompatActivity() {
19 |
20 | private lateinit var appBarConfiguration: AppBarConfiguration
21 |
22 | private val binding: ActivityMainBinding by vb()
23 |
24 | private val binding2: ActivityMainBinding by vb(ActivityMainBinding::inflate)
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setSupportActionBar(binding.toolbar)
29 | val navController = findNavController(R.id.nav_host_fragment_content_main)
30 | appBarConfiguration = AppBarConfiguration(navController.graph)
31 | setupActionBarWithNavController(navController, appBarConfiguration)
32 | }
33 |
34 |
35 | override fun onSupportNavigateUp(): Boolean {
36 | val navController = findNavController(R.id.nav_host_fragment_content_main)
37 | return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
38 | }
39 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delagate_vb/SecondFragmentWithDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delagate_vb
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.constraintlayout.widget.ConstraintLayout
9 | import androidx.fragment.app.Fragment
10 | import androidx.navigation.fragment.findNavController
11 | import com.jay.vbhelper.delegate.vb
12 | import com.jay.vbhelper.simple.R
13 | import com.jay.vbhelper.simple.databinding.FragmentSecondBinding
14 | import com.jay.vbhelper.simple.databinding.LayoutInfoBinding
15 | import com.jay.vbhelper.simple.databinding.LayoutInfoMergeBinding
16 | import com.jay.vbhelper.simple.databinding.LayoutInfoViewStubBinding
17 |
18 | /**
19 | * @author jaydroid
20 | * @version 1.0
21 | * @date 2021/9/3
22 | */
23 | class SecondFragmentWithDelegate : Fragment() {
24 |
25 | //通过自定义属性代理 + 反射 bind 方法,这种方式需要提前加载好 view
26 | private val binding: FragmentSecondBinding by vb()
27 |
28 | // private val binding: FragmentSecondBinding by vb(FragmentSecondBinding::inflate)
29 |
30 | // private val binding: FragmentSecondBinding by vb(R.layout.fragment_second)
31 |
32 | override fun onCreateView(
33 | inflater: LayoutInflater,
34 | container: ViewGroup?,
35 | savedInstanceState: Bundle?
36 | ): View {
37 | return binding.root
38 | }
39 |
40 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
41 | super.onViewCreated(view, savedInstanceState)
42 |
43 | Log.d("Jay", "00layoutIdRes")
44 |
45 | binding.buttonSecond.setOnClickListener {
46 | findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
47 | }
48 | val item = arguments?.get("item")?.toString()
49 |
50 |
51 | binding.textviewSecond.text = item
52 |
53 | //include
54 | // binding.includeLayout.tvInfoInclude.text = "tvInfoInclude:$item"
55 | // todo include 方式有时候无法识别到真实的绑定类类型只能识别它是个View类型但是编译不会报错, 这种情况清理缓存可能会好 ,或者也可以强制类型转换或者自己bind
56 | // val tvInfoInclude: LayoutInfoBinding = binding.includeLayout as LayoutInfoBinding
57 | // 找到include的根布局,手动绑定
58 | val includeLayout = view.findViewById(R.id.include_layout)
59 | val tvInfoInclude = LayoutInfoBinding.bind(includeLayout)
60 | tvInfoInclude.tvInfoInclude.text = "tvInfoInclude:$item"
61 |
62 |
63 | //include+merge 只能手动调用绑定类的bind方法
64 | val layoutInfoMergeBinding = LayoutInfoMergeBinding.bind(binding.root)
65 | val tvInfoMerge = layoutInfoMergeBinding.tvInfoMerge
66 | tvInfoMerge.text = "tvInfoMerge:$item"
67 |
68 | //ViewStub 只能手动调用绑定类的bind方法
69 | binding.layoutViewStub.setOnInflateListener { _, inflateId ->
70 | val layoutInfoViewStubBinding = LayoutInfoViewStubBinding.bind(inflateId)
71 | val tvInfoViewStub = layoutInfoViewStubBinding.tvInfoViewStub
72 | tvInfoViewStub.text = "tvInfoViewStub:$item"
73 | }
74 | binding.layoutViewStub.inflate()
75 |
76 | //CustomView
77 | binding.name1.setName("CustomView")
78 | binding.name.setName("CustomViewWithDelegate")
79 |
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delagate_vb/TextAdapterWithDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delagate_vb
2 |
3 | import android.view.ViewGroup
4 | import androidx.recyclerview.widget.DiffUtil
5 | import androidx.recyclerview.widget.ListAdapter
6 | import com.jay.vbhelper.delegate.BindingViewHolder
7 | import com.jay.vbhelper.delegate.vh
8 | import com.jay.vbhelper.simple.databinding.LayoutItemTextBinding
9 |
10 | /**
11 | * @author jaydroid
12 | * @version 1.0
13 | * @date 2021/9/3
14 | */
15 | class TextAdapterWithDelegate(
16 | diffCallback: DiffUtil.ItemCallback,
17 | private val list: List
18 | ) : ListAdapter>(diffCallback) {
19 |
20 | private var function: ((item: String) -> Unit)? = null
21 |
22 | override fun onCreateViewHolder(
23 | parent: ViewGroup,
24 | viewType: Int
25 | ): BindingViewHolder {
26 |
27 | val holder: BindingViewHolder by vh(parent)
28 | val holder2: BindingViewHolder by vh(
29 | parent,
30 | LayoutItemTextBinding::inflate
31 | )
32 | return holder2
33 | }
34 |
35 | override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {
36 | val item: String = list[position]
37 | holder.binding.tvName.text = item
38 | holder.binding.root.setOnClickListener {
39 | function?.invoke(item)
40 | }
41 | }
42 |
43 | override fun getItemCount(): Int = list.size
44 |
45 | fun setOnItemClickListener(function: (item: String) -> Unit) {
46 | this.function = function
47 | }
48 |
49 |
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_api/ApiTestActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_api
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import android.os.Bundle
6 | import android.util.Log
7 | import android.view.GestureDetector
8 | import android.view.MotionEvent
9 | import android.view.View
10 | import androidx.appcompat.app.AppCompatActivity
11 | import com.jay.anyby.api.api
12 | import com.jay.vbhelper.delegate.vb
13 | import com.jay.vbhelper.simple.databinding.ActivityApiTestBinding
14 |
15 | class ApiTestActivity : AppCompatActivity(), View.OnTouchListener {
16 |
17 | private val binding: ActivityApiTestBinding by vb()
18 |
19 | private var gestureDetector: GestureDetector? = null
20 |
21 |
22 | //接口委托
23 | private val lifecycleCallbacksWithDelegate =
24 | object : Application.ActivityLifecycleCallbacks by api() {
25 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
26 | Log.d("Jay", "onActivityCreated, $activity")
27 | }
28 |
29 | override fun onActivityDestroyed(activity: Activity) {
30 | Log.d("Jay", "onActivityDestroyed, $activity")
31 | }
32 | }
33 |
34 | //接口委托
35 | private val gestureListenerWithDelegate = object : GestureDetector.OnGestureListener by api() {
36 |
37 | override fun onDown(e: MotionEvent?): Boolean {
38 | Log.d("Jay", "onDown")
39 | return true
40 | }
41 |
42 | //todo 带返回值的方法不能通过动态代理正常返回
43 | override fun onSingleTapUp(e: MotionEvent?): Boolean {
44 | return true
45 | }
46 |
47 | override fun onScroll(
48 | e1: MotionEvent?,
49 | e2: MotionEvent?,
50 | distanceX: Float,
51 | distanceY: Float
52 | ): Boolean {
53 | return true
54 | }
55 |
56 | override fun onFling(
57 | e1: MotionEvent?,
58 | e2: MotionEvent?,
59 | velocityX: Float,
60 | velocityY: Float
61 | ): Boolean {
62 | return true
63 | }
64 |
65 |
66 | }
67 |
68 |
69 | override fun onCreate(savedInstanceState: Bundle?) {
70 | super.onCreate(savedInstanceState)
71 |
72 | //GestureDetector
73 | // mGestureDetector = GestureDetector(this, gestureListener)
74 | gestureDetector = GestureDetector(this, gestureListenerWithDelegate)
75 | binding.tvTouch.setOnTouchListener(this)
76 | binding.tvTouch.isFocusable = true
77 | binding.tvTouch.isClickable = true
78 | binding.tvTouch.isLongClickable = true
79 |
80 | //ActivityLifecycleCallbacks
81 | // application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
82 | application.registerActivityLifecycleCallbacks(lifecycleCallbacksWithDelegate)
83 | }
84 |
85 |
86 | override fun onTouch(v: View?, event: MotionEvent?): Boolean {
87 | return gestureDetector?.onTouchEvent(event) ?: false
88 | }
89 |
90 | //非接口委托
91 | private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
92 | /**
93 | * Called when the Activity calls [super.onCreate()][Activity.onCreate].
94 | */
95 | override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
96 |
97 | }
98 |
99 | /**
100 | * Called when the Activity calls [super.onStart()][Activity.onStart].
101 | */
102 | override fun onActivityStarted(activity: Activity?) {
103 |
104 | }
105 |
106 | /**
107 | * Called when the Activity calls [super.onResume()][Activity.onResume].
108 | */
109 | override fun onActivityResumed(activity: Activity?) {
110 |
111 | }
112 |
113 | /**
114 | * Called when the Activity calls [super.onPause()][Activity.onPause].
115 | */
116 | override fun onActivityPaused(activity: Activity?) {
117 |
118 | }
119 |
120 | /**
121 | * Called when the Activity calls [super.onStop()][Activity.onStop].
122 | */
123 | override fun onActivityStopped(activity: Activity?) {
124 |
125 | }
126 |
127 | /**
128 | * Called when the Activity calls
129 | * [super.onSaveInstanceState()][Activity.onSaveInstanceState].
130 | */
131 | override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
132 |
133 | }
134 |
135 | /**
136 | * Called when the Activity calls [super.onDestroy()][Activity.onDestroy].
137 | */
138 | override fun onActivityDestroyed(activity: Activity?) {
139 |
140 | }
141 | }
142 |
143 | //非接口委托
144 | private val gestureListener: GestureDetector.OnGestureListener =
145 | object : GestureDetector.OnGestureListener {
146 |
147 | override fun onDown(e: MotionEvent?): Boolean {
148 | Log.d("Jay", "onDown")
149 | return true
150 | }
151 |
152 |
153 | override fun onShowPress(e: MotionEvent?) {
154 |
155 | }
156 |
157 |
158 | override fun onSingleTapUp(e: MotionEvent?): Boolean {
159 | return true
160 | }
161 |
162 | override fun onScroll(
163 | e1: MotionEvent?,
164 | e2: MotionEvent?,
165 | distanceX: Float,
166 | distanceY: Float
167 | ): Boolean {
168 | return true
169 | }
170 |
171 |
172 | override fun onLongPress(e: MotionEvent?) {
173 |
174 | }
175 |
176 | override fun onFling(
177 | e1: MotionEvent?,
178 | e2: MotionEvent?,
179 | velocityX: Float,
180 | velocityY: Float
181 | ): Boolean {
182 | return true
183 | }
184 |
185 | }
186 |
187 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_api/ProxyTest.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_api
2 |
3 | import java.lang.reflect.InvocationHandler
4 | import java.lang.reflect.Method
5 | import java.lang.reflect.Proxy
6 |
7 | /**
8 | * java 动态代理测试类
9 | * 利用反射机制在运行时创建代理类。
10 | * @author jaydroid
11 | * @version 1.0
12 | * @date 2021/10/6
13 | */
14 | interface HelloInterface {
15 | fun sayHello()
16 | }
17 |
18 | class Hello : HelloInterface {
19 | override fun sayHello() {
20 | println("Hello HelloInterface!")
21 | }
22 | }
23 |
24 | interface ByeInterface {
25 | fun sayBye()
26 | }
27 |
28 | class Bye : ByeInterface {
29 | override fun sayBye() {
30 | println("Bye ByeInterface!")
31 | }
32 | }
33 |
34 | //由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐
35 | class HelloProxy : HelloInterface {
36 | private val helloInterface: HelloInterface = Hello()
37 | override fun sayHello() {
38 | println("Before invoke sayHello")
39 | helloInterface.sayHello()
40 | println("After invoke sayHello")
41 | }
42 | }
43 |
44 |
45 | class ProxyHandler(private val o: Any) : InvocationHandler {
46 |
47 | override fun invoke(proxy: Any?, method: Method, args: Array?): Any? {
48 | System.out.println("Before invoke " + method.name)
49 | val result = method.invoke(o, *args.orEmpty())
50 | System.out.println("After invoke " + method.name)
51 | return result
52 | }
53 | }
54 |
55 | fun testProxy() {
56 | // System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")
57 | val bye: ByeInterface = Bye()
58 | val proxyBye = Proxy.newProxyInstance(
59 | Bye::class.java.classLoader,
60 | arrayOf(ByeInterface::class.java),
61 | ProxyHandler(bye)
62 | ) as ByeInterface
63 | proxyBye.sayBye()
64 |
65 | val hello: HelloInterface = Hello()
66 | val proxyHello: HelloInterface = Proxy.newProxyInstance(
67 | Hello::class.java.classLoader,
68 | arrayOf(HelloInterface::class.java),
69 | ProxyHandler(hello)
70 | ) as HelloInterface
71 | proxyHello.sayHello()
72 | }
73 |
74 |
75 | interface IPlay {
76 |
77 | fun playFilm()
78 |
79 | fun isPlay(): Boolean
80 |
81 | }
82 |
83 |
84 | class Actor : IPlay {
85 | private var name: String
86 |
87 | constructor(name: String) {
88 | this.name = name
89 | }
90 |
91 | override fun playFilm() {
92 | println("The actor $name is playing film")
93 | }
94 |
95 | override fun isPlay(): Boolean {
96 | return true
97 | }
98 | }
99 |
100 | fun dynamicProxy1() {
101 | val actor = Actor("hha")
102 | val proxy = Proxy.newProxyInstance(
103 | Actor::class.java.classLoader,
104 | arrayOf(IPlay::class.java),
105 | object : InvocationHandler {
106 | override fun invoke(proxy: Any?, method: Method?, args: Array?): Any? {
107 | val result = method?.invoke(actor, *args.orEmpty())
108 | return result
109 | }
110 | }) as IPlay
111 |
112 | proxy.playFilm()
113 | val play = proxy.isPlay()
114 | println("isplay:$play")
115 | }
116 |
117 | fun dynamicProxy2() {
118 |
119 | val noOpHandler = InvocationHandler { _, _, _ ->
120 | // no op
121 | }
122 |
123 | val actor = Actor("haha")
124 | val proxy = Proxy.newProxyInstance(
125 | Actor::class.java.classLoader,
126 | arrayOf(IPlay::class.java),
127 | noOpHandler
128 | ) as IPlay
129 |
130 | proxy.playFilm()
131 |
132 | }
133 |
134 | inline fun noOpDelegate(): T {
135 | val javaClass = T::class.java
136 | val noOpHandler = InvocationHandler { _, _, _ ->
137 | // no op
138 | }
139 | return Proxy.newProxyInstance(
140 | javaClass.classLoader, arrayOf(javaClass), noOpHandler
141 | ) as T
142 | }
143 |
144 | class DynamicProxyHandler : InvocationHandler {
145 |
146 | private var traget: Any
147 |
148 | constructor(target: Any) {
149 | this.traget = target
150 | }
151 |
152 | val noOpHandler = InvocationHandler { _, _, _ ->
153 | // no op
154 | }
155 |
156 | override fun invoke(proxy: Any?, method: Method?, args: Array?): Any? {
157 | val proxy =
158 | Proxy.newProxyInstance(javaClass.classLoader, arrayOf(javaClass), noOpHandler)
159 | println("执行之前....")
160 | //由于传来的参数是不确定的,这里用*args.orEmpty()传参
161 | val result = method?.invoke(proxy, *args.orEmpty())
162 | println("执行之后....")
163 | return result
164 | }
165 | }
166 |
167 | fun main() {
168 | println("静态代理")
169 | // val helloProxy = HelloProxy()
170 | // helloProxy.sayHello()
171 |
172 | println("动态代理")
173 | // dynamicProxy1()
174 | // dynamicProxy2()
175 |
176 | // testProxy()
177 |
178 | val p: IPlay = object : IPlay by noOpDelegate() {
179 | // override fun isPlay(): Boolean {
180 | // println("isPlay")
181 | // return true
182 | // }
183 |
184 | override fun playFilm() {
185 | println("playFilm")
186 | }
187 | }
188 |
189 | p.isPlay()
190 | p.playFilm()
191 |
192 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_sp/SPTestActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_sp
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.util.Log
6 | import androidx.appcompat.app.AppCompatActivity
7 |
8 | /**
9 | * @author jaydroid
10 | * @version 1.0
11 | * @date 2021/9/3
12 | */
13 | class SPTestActivity : AppCompatActivity() {
14 |
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 |
18 | val prefs = getSharedPreferences("sp_app_jay", Context.MODE_PRIVATE)
19 |
20 | //缓存Token的场景
21 | // val tokenHolder = TokenHolder(prefs)
22 | // Log.d("Jay", "tokenHolder:$tokenHolder")
23 | // tokenHolder.saveToken("token_one")
24 | // tokenHolder.saveToken("token_second")
25 | //
26 | // //缓存登录信息的场景
27 | // val userHolder = UserHolder(prefs)
28 | // Log.d("Jay", "userHolder:$userHolder")
29 | // userHolder.saveUserAccount("jay", "123456")
30 |
31 |
32 | }
33 |
34 |
35 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_sp/TokenHolder.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_sp
2 |
3 | import android.content.SharedPreferences
4 |
5 |
6 | /**
7 | * @author jaydroid
8 | * @version 1.0
9 | * @date 2021/9/18
10 | */
11 | class TokenHolder(prefs: SharedPreferences) {
12 |
13 | // var token: String by prefs.string()
14 | // private set
15 | //
16 | // var count by prefs.int()
17 | // private set
18 | //
19 | // fun saveToken(newToken: String) {
20 | // token = newToken
21 | // count++
22 | // }
23 | //
24 | // override fun toString(): String {
25 | // return "TokenHolder(token='$token', count=$count)"
26 | // }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_sp/UserHolder.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_sp
2 |
3 | import android.content.SharedPreferences
4 |
5 | /**
6 | * @author jaydroid
7 | * @version 1.0
8 | * @date 2021/9/18
9 | */
10 | class UserHolder(prefs: SharedPreferences) {
11 |
12 | // var name: String by prefs.string()
13 | // private set
14 | //
15 | // var pwd: String by prefs.string()
16 | // private set
17 | //
18 | // fun saveUserAccount(name: String, pwd: String) {
19 | // this.name = name
20 | // this.pwd = pwd
21 | // }
22 | //
23 | // override fun toString(): String {
24 | // return "UserHolder(name='$name', pwd='$pwd')"
25 | // }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_vm/MyViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_vm
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | /**
8 | * @author jaydroid
9 | * @version 1.0
10 | * @date 2021/9/17
11 | */
12 | class MyViewModel : ViewModel() {
13 |
14 |
15 |
16 |
17 | private val users: MutableLiveData> by lazy {
18 | MutableLiveData>().also { loadUsers() }
19 | }
20 |
21 | fun getUsers(): LiveData> {
22 | return users
23 | }
24 |
25 | private fun loadUsers() {
26 | // Do an asynchronous operation to fetch users.
27 | }
28 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_vm/User.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_vm
2 |
3 | /**
4 | * @author jaydroid
5 | * @version 1.0
6 | * @date 2021/9/17
7 | */
8 | data class User(val name: String, val age: Int)
9 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/delegate_vm/ViewModelTestActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.delegate_vm
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.jay.anyby.vm.vm
6 |
7 | /**
8 | * @author jaydroid
9 | * @version 1.0
10 | * @date 2021/9/3
11 | */
12 | class ViewModelTestActivity : AppCompatActivity() {
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 |
17 | //在系统第一次调用 activity 的 onCreate() 方法时创建一个 ViewModel。
18 | // 重新创建的 activity 接收由第一个 activity 创建的相同 MyViewModel 实例。
19 | // 使用来自 activity-ktx 工件的“by viewModels()”Kotlin 属性委托
20 | val model: MyViewModel by vm()
21 |
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/inflate_test/InflateTestActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.inflate_test
2 |
3 | import android.content.Context
4 | import android.content.res.XmlResourceParser
5 | import android.os.Bundle
6 | import android.util.Log
7 | import android.view.LayoutInflater
8 | import androidx.appcompat.app.AppCompatActivity
9 | import com.jay.vbhelper.delegate.vb
10 | import com.jay.vbhelper.simple.R
11 | import com.jay.vbhelper.simple.databinding.ActivityInflateTestBinding
12 |
13 | /**
14 | * @author jaydroid
15 | * @version 1.0
16 | * @date 2021/9/3
17 | */
18 | class InflateTestActivity : AppCompatActivity() {
19 |
20 | private val binding: ActivityInflateTestBinding by vb()
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 |
25 |
26 | //获取 LayoutInflater
27 | //1、通过 LayoutInflater 的静态方法 from 获取,内部调用的是第二种
28 | val layoutInflater1: LayoutInflater = LayoutInflater.from(this)
29 | //2、通过系统服务 getSystemService 方法获取
30 | val layoutInflater2: LayoutInflater =
31 | getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
32 | //3、如果是在 Activity 或 Fragment 可直接获取到实例
33 | val layoutInflater3: LayoutInflater = layoutInflater //相当于调用 getLayoutInflater()
34 |
35 | //三种方式在 Activity 范围内是单例
36 | Log.d("Jay", "layoutInflater1:${layoutInflater1.hashCode()}")
37 | Log.d("Jay", "layoutInflater2:${layoutInflater2.hashCode()}")
38 | Log.d("Jay", "layoutInflater3:${layoutInflater3.hashCode()}")
39 | //2021-09-06 23:41:52.925 6353-6353/com.jay.vbhelper D/Jay: layoutInflater1:31503528
40 | //2021-09-06 23:41:52.925 6353-6353/com.jay.vbhelper D/Jay: layoutInflater2:31503528
41 | //2021-09-06 23:41:52.925 6353-6353/com.jay.vbhelper D/Jay: layoutInflater3:31503528
42 | //调用 LayoutInflater.inflate 的四个方法重载
43 | //如果传入的 root 为 null ,此时会将 Xml 布局生成的根 View 对象直接返回
44 | val view1_1 = layoutInflater3.inflate(R.layout.layout_view, null)
45 | //这种方式加载的布局不需要再次addView(),否则:Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
46 | //如果传入的 root 不为 null 且 attachToRoot 为 true,此时会将 Xml 布局生成的根 View 通过 addView 方法携带布局参数添加到 root 中
47 | //如果 root 参数不为空 和 view2_1 一样
48 | val view1_2 = layoutInflater3.inflate(R.layout.layout_view, binding.clContainer)
49 | val view2_1 = layoutInflater3.inflate(R.layout.layout_view, binding.clContainer, true)
50 | //如果传入的 root 不为 null 且 attachToRoot 为 false,此时会给 Xml 布局生成的根 View 设置布局参数
51 | val view2_2 = layoutInflater3.inflate(R.layout.layout_view, binding.clContainer, false)
52 | val parser: XmlResourceParser = resources.getLayout(R.layout.layout_view)
53 | //这两个重载方法不常用
54 | // val view3 = layoutInflater3.inflate(parser, binding.clContainer)
55 | val view4 = layoutInflater3.inflate(parser, binding.clContainer, false)
56 | binding.clContainer.addView(view1_1)
57 |
58 |
59 | }
60 |
61 |
62 | /*
63 |
64 |
65 | public View inflate(XmlPullParser parser, @Nullable ViewGroup root)
66 | public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)
67 | public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
68 | public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | public LayoutInflater cloneInContext(Context newContext) {
77 | return new PhoneLayoutInflater(this, newContext);
78 | }
79 |
80 |
81 |
82 | @Override
83 | public Object getSystemService(String name) {
84 | if (LAYOUT_INFLATER_SERVICE.equals(name)) {
85 | if (mInflater == null) {
86 | mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
87 | }
88 | return mInflater;
89 | }
90 | return getBaseContext().getSystemService(name);
91 | }
92 |
93 |
94 | public static LayoutInflater from(Context context) {
95 | LayoutInflater LayoutInflater =
96 | (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
97 | if (LayoutInflater == null) {
98 | throw new AssertionError("LayoutInflater not found.");
99 | }
100 | return LayoutInflater;
101 | }
102 |
103 |
104 |
105 |
106 | public LayoutInflater getLayoutInflater() {
107 | return getWindow().getLayoutInflater();
108 | }
109 |
110 | public abstract LayoutInflater getLayoutInflater();
111 |
112 | */
113 |
114 |
115 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/normal_use_vb/CustomView.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.normal_use_vb
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.util.AttributeSet
6 | import android.widget.LinearLayout
7 | import com.jay.vbhelper.delegate.vb
8 | import com.jay.vbhelper.simple.databinding.LayoutViewBinding
9 |
10 |
11 | @SuppressLint("SetTextI18n")
12 | class CustomView @JvmOverloads constructor(
13 | context: Context,
14 | attrs: AttributeSet? = null,
15 | defStyleAttr: Int = 0
16 | ) : LinearLayout(context, attrs, defStyleAttr) {
17 |
18 | private val binding: LayoutViewBinding by vb()
19 |
20 | fun setName(name: String) {
21 | binding.firstName.text = name
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/normal_use_vb/FirstFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.normal_use_vb
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.lifecycle.ViewModelProvider
9 | import androidx.navigation.fragment.findNavController
10 | import androidx.recyclerview.widget.DiffUtil
11 | import androidx.recyclerview.widget.LinearLayoutManager
12 | import com.jay.vbhelper.simple.R
13 | import com.jay.vbhelper.simple.databinding.FragmentFirstBinding
14 | import com.jay.vbhelper.simple.delagate_vb.TextAdapterWithDelegate
15 |
16 | /**
17 | * @author jaydroid
18 | * @version 1.0
19 | * @date 2021/9/3
20 | */
21 | class FirstFragment : Fragment() {
22 |
23 | private lateinit var firstViewModel: FirstViewModel
24 | private var _binding: FragmentFirstBinding? = null
25 |
26 | // This property is only valid between onCreateView and onDestroyView.
27 | private val binding get() = _binding!!
28 |
29 | override fun onCreateView(
30 | inflater: LayoutInflater,
31 | container: ViewGroup?,
32 | savedInstanceState: Bundle?
33 | ): View {
34 | firstViewModel = ViewModelProvider(this).get(FirstViewModel::class.java)
35 | _binding = FragmentFirstBinding.inflate(inflater, container, false)
36 | return binding.root
37 | }
38 |
39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
40 | super.onViewCreated(view, savedInstanceState)
41 | binding.rvList.layoutManager = LinearLayoutManager(requireContext())
42 | binding.rvList.adapter = adapter
43 | firstViewModel.dataList.observe(viewLifecycleOwner, {
44 | data.clear()
45 | data.addAll(it)
46 | adapter.submitList(data)
47 | // It will always be more efficient to use more specific change events if you can. Rely on notifyDataSetChanged as a last resort.
48 | // adapter.notifyDataSetChanged()
49 | })
50 | adapter.setOnItemClickListener {
51 | findNavController().navigate(
52 | R.id.action_FirstFragment_to_SecondFragment,
53 | Bundle().apply { putString("item", it) })
54 | }
55 | }
56 |
57 | private val data: ArrayList by lazy { arrayListOf() }
58 |
59 | private val diffCallback: DiffCallback by lazy { DiffCallback() }
60 |
61 | private val adapter: TextAdapterWithDelegate by lazy {
62 | TextAdapterWithDelegate(diffCallback, data)
63 | }
64 |
65 | class DiffCallback : DiffUtil.ItemCallback() {
66 | override fun areItemsTheSame(oldItem: String, newItem: String) =
67 | oldItem.hashCode() == newItem.hashCode()
68 |
69 | override fun areContentsTheSame(oldItem: String, newItem: String) =
70 | oldItem.hashCode() == newItem.hashCode()
71 | }
72 |
73 |
74 | override fun onDestroyView() {
75 | super.onDestroyView()
76 | _binding = null
77 | }
78 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/normal_use_vb/FirstViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.normal_use_vb
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class FirstViewModel : ViewModel() {
8 |
9 | private val data = MutableLiveData>().apply {
10 | value = arrayListOf("Java", "Kotlin", "Dart", "Android", "KMM", "Flutter", "Jetpack")
11 | }
12 | val dataList: LiveData> = data
13 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/normal_use_vb/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.normal_use_vb
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.navigation.findNavController
6 | import androidx.navigation.ui.AppBarConfiguration
7 | import androidx.navigation.ui.navigateUp
8 | import androidx.navigation.ui.setupActionBarWithNavController
9 | import com.jay.vbhelper.simple.R
10 | import com.jay.vbhelper.simple.databinding.ActivityMainBinding
11 |
12 | /**
13 | * @author jaydroid
14 | * @version 1.0
15 | * @date 2021/9/3
16 | */
17 | class MainActivity : AppCompatActivity() {
18 |
19 | private lateinit var appBarConfiguration: AppBarConfiguration
20 |
21 | private lateinit var binding: ActivityMainBinding
22 |
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | super.onCreate(savedInstanceState)
25 | binding = ActivityMainBinding.inflate(layoutInflater)
26 | setContentView(binding.root)
27 | setSupportActionBar(binding.toolbar)
28 |
29 | val navController = findNavController(R.id.nav_host_fragment_content_main)
30 | appBarConfiguration = AppBarConfiguration(navController.graph)
31 | setupActionBarWithNavController(navController, appBarConfiguration)
32 |
33 | }
34 |
35 |
36 | override fun onSupportNavigateUp(): Boolean {
37 | val navController = findNavController(R.id.nav_host_fragment_content_main)
38 | return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
39 | }
40 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/normal_use_vb/SecondFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.normal_use_vb
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import androidx.fragment.app.Fragment
9 | import androidx.navigation.fragment.findNavController
10 | import com.jay.vbhelper.simple.R
11 | import com.jay.vbhelper.simple.databinding.*
12 |
13 | /**
14 | * @author jaydroid
15 | * @version 1.0
16 | * @date 2021/9/3
17 | */
18 | class SecondFragment : Fragment() {
19 |
20 | private var _binding: FragmentSecondBinding? = null
21 |
22 | // This property is only valid between onCreateView and
23 | // onDestroyView.
24 | private val binding get() = _binding!!
25 |
26 | override fun onCreateView(
27 | inflater: LayoutInflater,
28 | container: ViewGroup?,
29 | savedIS: Bundle?
30 | ): View {
31 | _binding = FragmentSecondBinding.inflate(inflater, container, false)
32 | return binding.root
33 | }
34 |
35 |
36 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
37 | super.onViewCreated(view, savedInstanceState)
38 | binding.buttonSecond.setOnClickListener {
39 | findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
40 | }
41 | val item = arguments?.get("item")?.toString()
42 |
43 | binding.textviewSecond.text = item
44 |
45 | //include
46 | // binding.includeLayout.tvInfoInclude.text = "tvInfoInclude:$item"
47 | // todo include 方式有时候无法识别到真实的绑定类类型只能识别它是个View类型但是编译不会报错, 这种情况清理缓存可能会好 ,或者也可以强制类型转换或者自己bind
48 | // val tvInfoInclude: LayoutInfoBinding = binding.includeLayout as LayoutInfoBinding
49 | // 找到include的根布局,手动绑定
50 | val includeLayout = view.findViewById(R.id.include_layout)
51 | val tvInfoInclude = LayoutInfoBinding.bind(includeLayout)
52 | tvInfoInclude.tvInfoInclude.text = "tvInfoInclude:$item"
53 |
54 |
55 | //include+merge 只能手动调用绑定类的bind方法
56 | val layoutInfoMergeBinding = LayoutInfoMergeBinding.bind(binding.root)
57 | val tvInfoMerge = layoutInfoMergeBinding.tvInfoMerge
58 | tvInfoMerge.text = "tvInfoMerge:$item"
59 |
60 | //ViewStub 只能手动调用绑定类的bind方法
61 | binding.layoutViewStub.setOnInflateListener { _, inflateId ->
62 | val layoutInfoViewStubBinding = LayoutInfoViewStubBinding.bind(inflateId)
63 | val tvInfoViewStub = layoutInfoViewStubBinding.tvInfoViewStub
64 | tvInfoViewStub.text = "tvInfoViewStub:$item"
65 | }
66 | binding.layoutViewStub.inflate()
67 |
68 | InfoLayoutInfoBinding.bind(binding.root)
69 |
70 | //CustomView
71 | binding.name.alpha = 0.5f
72 |
73 | }
74 |
75 | override fun onDestroyView() {
76 | super.onDestroyView()
77 | _binding = null
78 | }
79 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/jay/vbhelper/simple/normal_use_vb/TextAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper.simple.normal_use_vb
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.DiffUtil
6 | import androidx.recyclerview.widget.ListAdapter
7 | import androidx.recyclerview.widget.RecyclerView
8 | import com.jay.vbhelper.simple.databinding.LayoutItemTextBinding
9 |
10 | /**
11 | * @author jaydroid
12 | * @version 1.0
13 | * @date 2021/9/3
14 | */
15 | class TextAdapter(
16 | diffCallback: DiffUtil.ItemCallback,
17 | private val list: List
18 | ) : ListAdapter(diffCallback) {
19 |
20 | private var function: ((item: String) -> Unit)? = null
21 |
22 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextHolder {
23 | val itemBinding =
24 | LayoutItemTextBinding.inflate(LayoutInflater.from(parent.context), parent, false)
25 | //绑定类交给Holder
26 | return TextHolder(itemBinding)
27 | }
28 |
29 | override fun onBindViewHolder(holder: TextHolder, position: Int) {
30 | val item: String = list[position]
31 | //数据交给Holder
32 | holder.bind(item)
33 | holder.itemBinding.root.setOnClickListener {
34 | function?.invoke(item)
35 | }
36 | }
37 |
38 | class TextHolder(val itemBinding: LayoutItemTextBinding) :
39 | RecyclerView.ViewHolder(itemBinding.root) {
40 | fun bind(name: String) {
41 | itemBinding.tvName.text = name
42 | }
43 | }
44 |
45 | override fun getItemCount(): Int = list.size
46 |
47 | fun setOnItemClickListener(function: (item: String) -> Unit) {
48 | this.function = function
49 | }
50 |
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/simple/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/simple/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 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/activity_api_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/activity_inflate_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/activity_simple.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
16 |
17 |
23 |
24 |
25 |
26 |
32 |
33 |
39 |
40 |
46 |
47 |
48 |
54 |
55 |
56 |
62 |
63 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/activity_vm_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/fragment_first.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/fragment_second.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
32 |
38 |
39 |
40 |
47 |
48 |
49 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/info_layout_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/layout_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/layout_info_merge.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/layout_info_view_stub.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/layout_item_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/layout_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/simple/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/simple/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/simple/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
17 |
18 |
19 |
24 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/simple/src/main/res/values-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/simple/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/simple/src/main/res/values-w1240dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 200dp
3 |
--------------------------------------------------------------------------------
/simple/src/main/res/values-w600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/simple/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/simple/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
--------------------------------------------------------------------------------
/simple/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AnyBy
3 | Settings
4 |
5 | First Fragment
6 | Second Fragment
7 | Next
8 | Previous
9 |
10 | Hello first fragment
11 | Hello second fragment. Arg: %1$s
12 |
--------------------------------------------------------------------------------
/simple/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/simple/src/test/java/com/jay/vbhelper/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/vbhelper/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/vbhelper/build.gradle:
--------------------------------------------------------------------------------
1 | group = "vbhelper"
2 | apply from: "../common.gradle"
3 |
4 | dependencies {
5 |
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/vbhelper/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydroid1024/anyby/55d0c325ef81cf3feb4a18d179a15af29555fcea/vbhelper/consumer-rules.pro
--------------------------------------------------------------------------------
/vbhelper/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
--------------------------------------------------------------------------------
/vbhelper/src/androidTest/java/com/jay/vbhelper/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.jay.vbhelper.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/vbhelper/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/vbhelper/src/main/java/com/jay/vbhelper/delegate/ActivityVBDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * 在 Activity 中创建 ViewBinding 绑定类
3 | * 使用方式如下:
4 | * 1. 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
5 | * private val binding: ActivityMainBinding by vb()
6 | * 2. 借助 lazy 属性委托 + 传递 inflate 方法引用
7 | * private val binding: ActivityMainBinding by vb(ActivityMainBinding::inflate)
8 | *
9 | * 注意:需要在生命周期方法中访问 binding 属性才能执行 setContentView 布局填充流程,如果页面中没有交互的控件时需要注意这一点
10 | *
11 | * 借助 lazy 属性委托的优势:
12 | * Kotlin 1.4 做的优化,当某些委托属性不会使用 KProperty。
13 | * 对于他们来说,在 $$delegatedProperties 中生成 KProperty 对象是多余的。
14 | * Kotlin 1.4 版本将优化此类情况。如果委托的属性运算符是内联的,并且没有使用 KProperty 参数,则不会生成相应的反射对象。
15 | * 参考博客:What to Expect in Kotlin 1.4 and Beyond | Optimized delegated properties
16 | * https://blog.jetbrains.com/kotlin/2019/12/what-to-expect-in-kotlin-1-4-and-beyond/
17 | *
18 | * @author jaydroid
19 | * @version 1.0
20 | * @date 2021/9/17
21 | */
22 | package com.jay.vbhelper.delegate
23 |
24 | import android.view.LayoutInflater
25 | import androidx.activity.ComponentActivity
26 | import androidx.annotation.MainThread
27 | import androidx.viewbinding.ViewBinding
28 | import kotlin.reflect.KClass
29 |
30 |
31 | /**
32 | * 在 Activity 中创建 ViewBinding 绑定类,
33 | *
34 | * @param inflateMethodRef (LayoutInflater) -> T) VB 中 inflate 方法的函数引用
35 | * @param T ViewBinding 的子类
36 | */
37 | @MainThread
38 | inline fun ComponentActivity.vb(noinline inflateMethodRef: ((LayoutInflater) -> T)? = null): Lazy =
39 | ActivityVBLazy(this, T::class, inflateMethodRef)
40 |
41 |
42 | class ActivityVBLazy(
43 | private val activity: ComponentActivity,
44 | private val kClass: KClass<*>,
45 | private val inflateMethodRef: ((LayoutInflater) -> T)?
46 | ) : Lazy {
47 | private var cachedBinding: T? = null
48 | override val value: T
49 | get() {
50 | var viewBinding = cachedBinding
51 | if (viewBinding == null) {
52 | viewBinding = if (inflateMethodRef != null) {
53 | //借助 lazy 属性委托 + 传递 inflate 方法引用
54 | inflateMethodRef.invoke(activity.layoutInflater)
55 | } else {
56 | //借助 lazy 属性委托 + 反射绑定类的 inflate 方法
57 | @Suppress("UNCHECKED_CAST")
58 | kClass.java.getMethod(METHOD_INFLATE, LayoutInflater::class.java)
59 | .invoke(null, activity.layoutInflater) as T
60 | }
61 | activity.setContentView(viewBinding.root)
62 | cachedBinding = viewBinding
63 | }
64 | return viewBinding
65 | }
66 |
67 | override fun isInitialized() = cachedBinding != null
68 | }
69 |
70 |
71 | const val TAG = "VBHelper"
72 | const val METHOD_INFLATE = "inflate"
--------------------------------------------------------------------------------
/vbhelper/src/main/java/com/jay/vbhelper/delegate/FragmentVBDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * 在 Fragment 中创建 ViewBinding 绑定类
3 | * 使用方式如下:
4 | * 1. 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
5 | * private val binding: FragmentMainBinding by vb()
6 | * override fun onCreateView(l: LayoutInflater, c: ViewGroup?, s: Bundle?): View {
7 | * return binding.root //通过 inflate 方法获取 VB 的方式需要在 onCreateView 中返回根视图
8 | * }
9 | *
10 | * 2. 借助 lazy 属性委托 + 传递 VB 的 inflate 方法引用
11 | * private val binding: FragmentMainBinding by vb(FragmentMainBinding::inflate)
12 | * override fun onCreateView(l: LayoutInflater, c: ViewGroup?, s: Bundle?): View {
13 | * return binding.root //通过 inflate 方法获取 VB 的方式需要在 onCreateView 中返回根视图
14 | * }
15 | *
16 | * @author jaydroid
17 | * @version 1.0
18 | * @date 2021/9/17
19 | */
20 |
21 | package com.jay.vbhelper.delegate
22 |
23 | import android.os.Handler
24 | import android.os.Looper
25 | import android.util.Log
26 | import android.view.LayoutInflater
27 | import androidx.annotation.MainThread
28 | import androidx.fragment.app.Fragment
29 | import androidx.lifecycle.Lifecycle
30 | import androidx.lifecycle.LifecycleObserver
31 | import androidx.viewbinding.ViewBinding
32 | import kotlin.reflect.KClass
33 |
34 |
35 | /**
36 | * 在 Fragment 中创建 ViewBinding 绑定类
37 | *
38 | * @param T ViewBinding 的子类
39 | * @param inflateMethodRef (LayoutInflater) -> T) VB 中 inflate 方法的函数引用
40 | */
41 | @MainThread
42 | inline fun Fragment.vb(noinline inflateMethodRef: ((LayoutInflater) -> T)? = null): Lazy =
43 | FragmentVNLazy(this, T::class, inflateMethodRef)
44 |
45 |
46 | class FragmentVNLazy(
47 | private val fragment: Fragment,
48 | private val kClass: KClass<*>,
49 | private val inflateMethodRef: ((LayoutInflater) -> T)?
50 | ) : Lazy {
51 | private var cachedBinding: T? = null
52 | private val clearBindingHandler by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }
53 |
54 | init {
55 | observeFragmentDestroy(fragment) { clearBindingHandler.post { cachedBinding = null } }
56 | }
57 |
58 | override val value: T
59 | get() {
60 | var viewBinding = cachedBinding
61 | if (viewBinding == null) {
62 | checkBindingFirstInvoke(fragment)
63 | viewBinding = if (inflateMethodRef != null) {
64 | //借助 lazy 属性委托 + 传递 inflate 方法引用
65 | inflateMethodRef.invoke(fragment.layoutInflater)
66 | } else {
67 | //借助 lazy 属性委托 + 反射绑定类的 inflate 方法
68 | @Suppress("UNCHECKED_CAST")
69 | kClass.java.getMethod(METHOD_INFLATE, LayoutInflater::class.java)
70 | .invoke(null, fragment.layoutInflater) as T
71 | }
72 | cachedBinding = viewBinding
73 | }
74 | return viewBinding!!
75 | }
76 |
77 | override fun isInitialized() = cachedBinding != null
78 |
79 | }
80 |
81 |
82 | fun observeFragmentDestroy(
83 | fragment: Fragment,
84 | callback: () -> Unit
85 | ) {
86 | fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
87 | viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
88 | @androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
89 | fun onDestroy() {
90 | Log.i(TAG, "${fragment::class.java.simpleName} call onDestroy")
91 | callback.invoke()
92 | }
93 | })
94 | }
95 | }
96 |
97 | private fun checkBindingFirstInvoke(fragment: Fragment) {
98 | if (!fragment.viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
99 | error("Cannot access view bindings. View lifecycle is ${fragment.viewLifecycleOwner.lifecycle.currentState}!")
100 | }
101 | }
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/vbhelper/src/main/java/com/jay/vbhelper/delegate/ViewHolderVBDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * 在 Adapter 中创建包含了属性代理绑定类的 ViewHolder
3 | * 使用方式如下:
4 | * 1. 借助 lazy 属性委托 + 反射绑定类的 inflate 三参数方法
5 | * val holder: BindingViewHolder by vh(parent)
6 | * 2. 借助 lazy 属性委托 + 传递绑定类的 inflate 三参数方法引用
7 | * val holder: BindingViewHolder by vh(parent, LayoutItemTextBinding::inflate)
8 | *
9 | * @author jaydroid
10 | * @version 1.0
11 | * @date 2021/9/17
12 | */
13 |
14 | package com.jay.vbhelper.delegate
15 |
16 | import android.annotation.SuppressLint
17 | import android.view.LayoutInflater
18 | import android.view.ViewGroup
19 | import androidx.recyclerview.widget.RecyclerView
20 | import androidx.viewbinding.ViewBinding
21 | import kotlin.properties.ReadOnlyProperty
22 | import kotlin.reflect.KProperty
23 |
24 |
25 | /**
26 | * 借助 lazy 属性委托 + 反射绑定类的 inflate 三参数方法
27 | *
28 | * @param T ViewBinding 的子类
29 | * @param parent RecyclerView 父布局
30 | */
31 | inline fun Any.vh(parent: ViewGroup) =
32 | object : ReadOnlyProperty> {
33 | @SuppressLint("StaticFieldLeak")
34 | private var binding: BindingViewHolder? = null
35 | override fun getValue(thisRef: Nothing?, property: KProperty<*>): BindingViewHolder {
36 | binding?.let { return it }
37 | binding = BindingViewHolder(inflateBinding(parent))
38 | return binding!!
39 | }
40 | }
41 |
42 | /**
43 | * 借助 lazy 属性委托 + 传递绑定类的 inflate 三参数方法引用
44 | *
45 | * @param T ViewBinding 的子类
46 | * @param parent RecyclerView 父布局
47 | */
48 | inline fun vh(
49 | parent: ViewGroup,
50 | noinline inflate: (LayoutInflater, ViewGroup, Boolean) -> T
51 | ) = object : ReadOnlyProperty> {
52 | @SuppressLint("StaticFieldLeak")
53 | private var binding: BindingViewHolder? = null
54 | override fun getValue(thisRef: Nothing?, property: KProperty<*>): BindingViewHolder {
55 | binding?.let { return it }
56 | binding = BindingViewHolder(parent, inflate)
57 | return binding!!
58 | }
59 | }
60 |
61 | /**
62 | * 包含了属性代理绑定类的 ViewHolder
63 | *
64 | * @param T ViewBinding 的子类
65 | * @property binding 绑定类
66 | */
67 | class BindingViewHolder(val binding: T) : RecyclerView.ViewHolder(binding.root) {
68 | constructor(parent: ViewGroup, inflate: (LayoutInflater, ViewGroup, Boolean) -> T) : this(
69 | inflate(LayoutInflater.from(parent.context), parent, false)
70 | )
71 | }
72 |
73 | /**
74 | * 反射绑定类的 inflate 三参数方法
75 | *
76 | * @param T ViewBinding 的子类
77 | * @param parent RecyclerView 父布局
78 | */
79 | inline fun inflateBinding(parent: ViewGroup) = T::class.java.getMethod(
80 | METHOD_INFLATE,
81 | LayoutInflater::class.java,
82 | ViewGroup::class.java,
83 | Boolean::class.java
84 | ).invoke(null, LayoutInflater.from(parent.context), parent, false) as T
85 |
86 |
--------------------------------------------------------------------------------
/vbhelper/src/main/java/com/jay/vbhelper/delegate/ViewVBDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * 在 View 中创建 ViewBinding 绑定类
3 | * 使用方式如下:
4 | * 1. 借助 lazy 属性委托 + 反射绑定类的 inflate 三参数方法
5 | * private val binding: MyViewBinding by vb()
6 | * 2. 借助 lazy 属性委托 + 传递 inflate 三参数方法引用
7 | * private val binding: MyViewBinding by vb(MyViewBinding::inflate)
8 | *
9 | * @author jaydroid
10 | * @version 1.0
11 | * @date 2021/9/17
12 | */
13 |
14 | package com.jay.vbhelper.delegate
15 |
16 | import android.view.LayoutInflater
17 | import android.view.ViewGroup
18 | import androidx.viewbinding.ViewBinding
19 | import kotlin.properties.ReadOnlyProperty
20 | import kotlin.reflect.KProperty
21 |
22 |
23 | /**
24 | * 借助 lazy 属性委托 + 反射绑定类的 inflate 三参数方法
25 | *
26 | * @param T ViewBinding 的子类
27 | */
28 | inline fun ViewGroup.vb() = object : ReadOnlyProperty {
29 |
30 | private var binding: T? = null
31 |
32 | override fun getValue(thisRef: ViewGroup, property: KProperty<*>): T {
33 | binding?.let { return it }
34 | val bindingClass = T::class.java
35 | //反射绑定类中三参数的 inflate 方法
36 | @Suppress("UNCHECKED_CAST")
37 | val inflateMethod = bindingClass.getMethod(
38 | METHOD_INFLATE,
39 | LayoutInflater::class.java,
40 | ViewGroup::class.java,
41 | Boolean::class.java
42 | )
43 | binding =
44 | inflateMethod.invoke(null, LayoutInflater.from(thisRef.context), thisRef, true) as T
45 | return binding!!
46 | }
47 | }
48 |
49 |
50 | /**
51 | * 借助 lazy 属性委托 + 传递绑定类 inflate 三参数方法引用
52 | *
53 | * @param T ViewBinding 的子类
54 | * @param inflate LayoutInflater
55 | */
56 | fun ViewGroup.vb(inflate: (LayoutInflater, ViewGroup, Boolean) -> T) =
57 | object : ReadOnlyProperty {
58 | private var binding: T? = null
59 | override fun getValue(thisRef: ViewGroup, property: KProperty<*>): T {
60 | binding?.let { return it }
61 | val layoutInflater = LayoutInflater.from(thisRef.context)
62 | binding = inflate(layoutInflater, thisRef, true)
63 | return binding!!
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/vbhelper/src/test/java/com/jay/vbhelper/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.jay.vbhelper
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------