├── .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 | 36 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 26 | 27 | 28 | 29 | 30 | 31 | 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/v/jaydroid1024/VBHelper.svg)](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 |