├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hi │ │ └── dhl │ │ └── jdatabinding │ │ └── demo │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hi │ │ │ └── dhl │ │ │ └── jdatabinding │ │ │ └── demo │ │ │ ├── App.kt │ │ │ ├── AppDialog.kt │ │ │ ├── binding │ │ │ └── RecycleViewBinding.kt │ │ │ ├── di │ │ │ └── AppModule.kt │ │ │ └── ui │ │ │ ├── FragmentTest.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MainViewModel.kt │ │ │ └── adapter │ │ │ ├── HeaderViewHolder.kt │ │ │ ├── TestAdapter.kt │ │ │ └── TestViewHolder.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── dialog_app.xml │ │ ├── fragment_test.xml │ │ ├── recycie_item_footer.xml │ │ ├── recycie_item_header.xml │ │ └── recycie_item_test.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── hi │ └── dhl │ └── jdatabinding │ └── demo │ └── ExampleUnitTest.kt ├── build.gradle ├── buildSrc ├── build.gradle.kts └── src │ ├── main │ └── java │ │ └── com │ │ └── hi │ │ └── dhl │ │ └── Deps.kt │ └── test │ └── kotlin │ └── com │ └── hi │ └── dhl │ └── DepsTest.java ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jdatabinding ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro ├── publish.gradle └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hi │ │ └── dhl │ │ └── jdatabinding │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── hi │ │ └── dhl │ │ └── jdatabinding │ │ ├── ActivityDataBindingDelegate.kt │ │ ├── DataBindingActivity.kt │ │ ├── DataBindingDialog.kt │ │ ├── DataBindingFragment.kt │ │ ├── DataBindingListAdapter.kt │ │ ├── DataBindingViewHolder.kt │ │ └── ext │ │ ├── ContextExt.kt │ │ ├── LifecycleExt.kt │ │ └── ReflectExt.kt │ └── test │ └── java │ └── com │ └── hi │ └── dhl │ └── jdatabinding │ └── ExampleUnitTest.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows: 2 | Thumbs.db 3 | ehthumbs.db 4 | Desktop.ini 5 | 6 | # Python: 7 | *.py[cod] 8 | *.so 9 | *.egg 10 | *.egg-info 11 | dist 12 | build 13 | migrations 14 | 15 | #Django 16 | *.egg-info 17 | *.pot 18 | *.py[co] 19 | .tox/ 20 | __pycache__ 21 | MANIFEST 22 | dist/ 23 | docs/_build/ 24 | docs/locale/ 25 | node_modules/ 26 | tests/coverage_html/ 27 | tests/.coverage 28 | build/ 29 | tests/report/ 30 | 31 | #-------------------------------- 32 | __pycache__/ 33 | *.py[cod] 34 | *$py.class 35 | 36 | # C extensions 37 | *.so 38 | 39 | # Distribution / packaging 40 | .Python 41 | env/ 42 | build/ 43 | develop-eggs/ 44 | dist/ 45 | downloads/ 46 | eggs/ 47 | .eggs/ 48 | lib/ 49 | lib64/ 50 | parts/ 51 | sdist/ 52 | var/ 53 | *.egg-info/ 54 | .installed.cfg 55 | *.egg 56 | 57 | # PyInstaller 58 | # Usually these files are written by a python script from a template 59 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 60 | *.manifest 61 | *.spec 62 | 63 | # Installer logs 64 | pip-log.txt 65 | pip-delete-this-directory.txt 66 | 67 | # Unit test / coverage reports 68 | htmlcov/ 69 | .tox/ 70 | .coverage 71 | .coverage.* 72 | .cache 73 | nosetests.xml 74 | coverage.xml 75 | *,cover 76 | .hypothesis/ 77 | 78 | # Translations 79 | *.mo 80 | *.pot 81 | 82 | # Django stuff: 83 | *.log 84 | local_settings.py 85 | 86 | # Flask stuff: 87 | instance/ 88 | .webassets-cache 89 | 90 | # Scrapy stuff: 91 | .scrapy 92 | 93 | # Sphinx documentation 94 | docs/_build/ 95 | 96 | # PyBuilder 97 | target/ 98 | 99 | # IPython Notebook 100 | .ipynb_checkpoints 101 | 102 | # pyenv 103 | .python-version 104 | 105 | # celery beat schedule file 106 | celerybeat-schedule 107 | 108 | # dotenv 109 | .env 110 | 111 | # virtualenv 112 | venv/ 113 | ENV/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | database/ 122 | media/ 123 | whoosh_index/ 124 | .idea/ 125 | *.sqlite3 126 | fabfile.py 127 | 128 | 129 | 130 | #-------------------------------- 131 | 132 | 133 | #java 134 | *.class 135 | 136 | # Package Files # 137 | *.jar 138 | *.war 139 | *.ear 140 | 141 | # Mobile Tools for Java (J2ME) 142 | .mtj.tmp/ 143 | 144 | 145 | # My configurations: 146 | db.ini 147 | deploy_key_rsa 148 | 149 | # Package Files # 150 | *.jar 151 | *.war 152 | *.ear 153 | 154 | #phpstorm 155 | *.idea 156 | 157 | #过滤数据库文件、sln解决方案文件、配置文件 158 | *.mdb 159 | *.ldb 160 | *.sln 161 | *.config 162 | 163 | #other 164 | data/error/ 165 | .idea*/ 166 | .idea/ 167 | *.htm 168 | 169 | # 忽略名称中末尾为ignore的文件夹 170 | *ignore/ 171 | *ignore*/ 172 | 173 | 174 | 175 | 176 | # Logs 177 | logs 178 | *.log 179 | npm-debug.log* 180 | # Runtime data 181 | pids 182 | *.pid 183 | *.seed 184 | *.pid.lock 185 | # Directory for instrumented libs generated by jscoverage/JSCover 186 | lib-cov 187 | # Coverage directory used by tools like istanbul 188 | coverage 189 | # nyc test coverage 190 | .nyc_output 191 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 192 | .grunt 193 | # Bower dependency directory (https://bower.io/) 194 | bower_components 195 | # node-waf configuration 196 | .lock-wscript 197 | # Compiled binary addons (http://nodejs.org/api/addons.html) 198 | build/Release 199 | # Dependency directories 200 | node_modules/ 201 | jspm_packages/ 202 | # Typescript v1 declaration files 203 | typings/ 204 | # Optional npm cache directory 205 | .npm 206 | # Optional eslint cache 207 | .eslintcache 208 | # Optional REPL history 209 | .node_repl_history 210 | # Output of 'npm pack' 211 | *.tgz 212 | # Yarn Integrity file 213 | .yarn-integrity 214 | # dotenv environment variables file 215 | .env 216 | .vscode 217 | # ignore sh 218 | sh/ 219 | # ignore test sdk.config.json 220 | sdk.config.json 221 | # ignore local config 222 | server/config.local.js 223 | 224 | 225 | #-------android 226 | # Built application files 227 | #*.apk 228 | *.ap_ 229 | 230 | # Files for the ART/Dalvik VM 231 | *.dex 232 | 233 | # Java class files 234 | *.class 235 | 236 | # Generated files 237 | bin/ 238 | gen/ 239 | out/ 240 | 241 | # Gradle files 242 | .gradle/ 243 | build/ 244 | 245 | # Local configuration file (sdk path, etc) 246 | local.properties 247 | 248 | # Proguard folder generated by Eclipse 249 | proguard/ 250 | 251 | # Log Files 252 | *.log 253 | 254 | # Android Studio Navigation editor temp files 255 | .navigation/ 256 | 257 | # Android Studio captures folder 258 | captures/ 259 | 260 | # Intellij 261 | *.iml 262 | .idea/ 263 | 264 | # Keystore files 265 | *.jks 266 | 267 | # External native build folder generated in Android Studio 2.2 and later 268 | .externalNativeBuild 269 | 270 | # DS_Store files 271 | *.DS_Store 272 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #

JDataBinding

2 | 3 |

4 | JDataBinding 是基于 DataBinding 封装的基础库,欢迎 star
5 |

6 | 7 |

8 | 9 |

10 | 11 | ![DataBindingDialog](http://cdn.51git.cn/2020-04-19-DataBindingDialog.png) 12 | 13 | 关于 JDataBinding 的解析可以查看我在掘金上的文章 [如何在项目中封装 Kotlin + Android Databinding](https://juejin.im/post/5e9c434a51882573663f6cc6) 14 | 15 | JDataBinding 是基于 DataBinding 封装的 基础库,可以在 `Activity` 、`AppCompatActivity` 、`FragmentActivity` 、`Fragment` 、 `Dialog` 、 `Adapter` 中直接使用 16 | 17 | ## Download 18 | 19 | **Gradle** 20 | 21 | 将下列代码添加进模块 build.gradle 文件内 22 | 23 | ``` 24 | dependencies { 25 | implementation 'com.hi-dhl:jdatabinding:1.0.5' 26 | } 27 | ``` 28 | 29 | ## Usage 30 | 31 | ### 如何使用 Dialog 中如何使用 32 | 33 | ``` 34 | class AppDialog( 35 | context: Context, 36 | val title: String? = null, 37 | val message: String? = null, 38 | val yes: AppDialog.() -> Unit 39 | ) : Dialog(context, R.style.AppDialog) { 40 | private val mBinding: DialogAppBinding by binding() 41 | 42 | init { 43 | requireNotNull(message) { "message must be not null" } 44 | } 45 | 46 | override fun onCreate(savedInstanceState: Bundle?) { 47 | super.onCreate(savedInstanceState) 48 | requestWindowFeature(Window.FEATURE_NO_TITLE) 49 | 50 | mBinding.apply { 51 | display.text = message 52 | btnNo.setOnClickListener { dismiss() } 53 | btnYes.setOnClickListener { yes() } 54 | } 55 | 56 | } 57 | } 58 | ``` 59 | 60 | **Step2: 简洁的调用方式** 61 | 62 | ``` 63 | AppDialog( 64 | context = this@MainActivity, 65 | message = msg, 66 | yes = { 67 | // do something 68 | }).show() 69 | ``` 70 | 71 | ### 如何使用 DataBindingActivity 72 | 73 | * jdatabinding >= 1.0.4 的用法 74 | 75 | ``` 76 | class MainActivity : FragmentActivity() { 77 | private val mBinding: ActivityMainBinding by binding() 78 | } 79 | 80 | class MainActivity : AppCompatActivity() { 81 | private val mBinding: ActivityMainBinding by binding() 82 | } 83 | 84 | class MainActivity : Activity() { 85 | private val mBinding: ActivityMainBinding by binding() 86 | } 87 | ``` 88 | 89 | * jdatabinding <= 1.0.3 的用法 90 | 91 | 92 | ``` 93 | 94 | class MainActivity : DataBindingAppCompatActivity() { 95 | private val mBinding: ActivityMainBinding by binding(R.layout.activity_main) 96 | 97 | override fun onCreate(savedInstanceState: Bundle?) { 98 | super.onCreate(savedInstanceState) 99 | mBinding.apply {} 100 | } 101 | } 102 | 103 | class MainActivity : DataBindingActivity() { 104 | private val mBinding: ActivityMainBinding by binding(R.layout.activity_main) 105 | 106 | override fun onCreate(savedInstanceState: Bundle?) { 107 | super.onCreate(savedInstanceState) 108 | mBinding.apply {} 109 | } 110 | } 111 | 112 | class MainActivity : DataBindingFragmentActivity() { 113 | private val mBinding: ActivityMainBinding by binding(R.layout.activity_main) 114 | 115 | override fun onCreate(savedInstanceState: Bundle?) { 116 | super.onCreate(savedInstanceState) 117 | mBinding.apply {} 118 | } 119 | } 120 | ``` 121 | 122 | ### 如何使用 DataBindingFragment 123 | 124 | * jdatabinding >= 1.0.5 的用法 125 | 126 | ``` 127 | class FragmentTest(val mainViewModel: MainViewModel) : Fragment(R.layout.fragment_test) { 128 | 129 | val bind: FragmentTestBinding by binding() 130 | } 131 | ``` 132 | 133 | * jdatabinding <= 1.0.3 的用法 134 | 135 | ``` 136 | class FragmentTest : DataBindingFragment() { 137 | val testViewModel: MainViewModel by viewModel() 138 | 139 | override fun onCreateView( 140 | inflater: LayoutInflater, 141 | container: ViewGroup?, 142 | savedInstanceState: Bundle? 143 | ): View? { 144 | 145 | return binding( 146 | inflater, 147 | R.layout.fragment_test, container 148 | ).apply { 149 | }.root 150 | } 151 | } 152 | ``` 153 | 154 | ### 如何使用 DataBindingListAdapter 155 | 156 | **Step1: 继承BaseViewHolder** 157 | 158 | 创建一个自定义的 ViewHolder 类,继承 DataBindingListAdapter,通过 viewHolderBinding 可以快速实现 DataBinding 的绑定 159 | 160 | ``` 161 | class TestViewHolder(view: View) : BaseViewHolder(view) { 162 | 163 | val binding: RecycieItemTestBinding by viewHolderBinding(view) 164 | 165 | override fun bindData(data: Model, position: Int) { 166 | binding.apply { 167 | model = data 168 | executePendingBindings() 169 | } 170 | } 171 | 172 | } 173 | ``` 174 | 175 | **Step2: 继承 DataBindingListAdapter** 176 | 177 | 实现带头部和尾部的 Adapter,创建自定义的 Adapter,继承 DataBindingListAdapter 178 | 179 | ``` 180 | class TestAdapter : DataBindingListAdapter(Model.CALLBACK) { 181 | 182 | override fun viewHolder(layout: Int, view: View): DataBindingViewHolder = when (layout) { 183 | R.layout.recycie_item_header -> HeaderViewHolder(view) 184 | else -> TestViewHolder(view) 185 | } 186 | 187 | override fun layout(position: Int): Int = when (position) { 188 | 0 -> R.layout.recycie_item_header 189 | getItemCount() - 1 -> R.layout.recycie_item_footer 190 | else -> R.layout.recycie_item_test 191 | } 192 | 193 | override fun getItemCount(): Int = super.getItemCount() + 2 194 | } 195 | ``` 196 | 197 | 构造方法传入了 Model.CALLBACK,Model.CALLBACK 实现了 DiffUtil.ItemCallback,用于计算 list 的两个非空 item 的不同。具体要写两个抽象方法 areItemsTheSame 和 areContentsTheSame 198 | 199 | ``` 200 | val CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { 201 | // 判断两个Objects 是否代表同一个item对象, 一般使用Bean的id比较 202 | override fun areItemsTheSame(oldItem: Model, newItem: Model): Boolean = 203 | oldItem.id == newItem.id 204 | 205 | // 判断两个Objects 是否有相同的内容。 206 | override fun areContentsTheSame(oldItem: Model, newItem: Model): Boolean = true 207 | } 208 | ``` 209 | 210 | **Step3: 绑定 RecyclerView 和 Adapter** 211 | 212 | ``` 213 | 214 | 215 | 218 | 219 | 222 | 223 | 224 | 230 | 231 | ``` 232 | 233 | 这里用到了 DataBinding 的自定义数据绑定部分,可以百度、Google具体的用法,具体实现可以参考 demo 下面 fragment_test.xml 文件 234 | 235 | 236 | 237 | 238 | ### 联系我 239 | 240 | * 个人微信:hi-dhl 241 | * 公众号:ByteCode,包含 Jetpack ,Kotlin ,Android 10 系列源码,译文,LeetCode / 剑指 Offer / 多线程 / 国内外大厂算法题 等等一系列文章 242 | 243 | 244 | 245 | --- 246 | 247 | 最后推荐我一直在更新维护的项目和网站: 248 | 249 | * 计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看:[AndroidX-Jetpack-Practice](https://github.com/hi-dhl/AndroidX-Jetpack-Practice) 250 | 251 | * LeetCode / 剑指 offer / 国内外大厂面试题 / 多线程 题解,语言 Java 和 kotlin,包含多种解法、解题思路、时间复杂度、空间复杂度分析
252 | 253 | 254 | 255 | * 剑指 offer 及国内外大厂面试题解:[在线阅读](https://offer.hi-dhl.com) 256 | * LeetCode 系列题解:[在线阅读](https://leetcode.hi-dhl.com) 257 | 258 | * 最新 Android 10 源码分析系列文章,了解系统源码,不仅有助于分析问题,在面试过程中,对我们也是非常有帮助的,仓库持续更新,欢迎前去查看 [Android10-Source-Analysis](https://github.com/hi-dhl/Android10-Source-Analysis) 259 | 260 | * 整理和翻译一系列精选国外的技术文章,每篇文章都会有**译者思考**部分,对原文的更加深入的解读,仓库持续更新,欢迎前去查看 [Technical-Article-Translation](https://github.com/hi-dhl/Technical-Article-Translation) 261 | 262 | * 「为互联网人而设计,国内国外名站导航」涵括新闻、体育、生活、娱乐、设计、产品、运营、前端开发、Android 开发等等网址,欢迎前去查看 [为互联网人而设计导航网站](https://site.51git.cn) 263 | 264 | ## 感谢 265 | 266 | [https://github.com/skydoves/BaseRecyclerViewAdapter](https://github.com/skydoves/BaseRecyclerViewAdapter) 267 | 268 | 269 | 请参看 BaseRecyclerViewAdapter 相关协议。 270 | 271 | 项目最初部分内容参考 BaseRecyclerViewAdapter,从 BaseRecyclerViewAdapter 扩展而来, 272 | 273 | 同时也要感谢这篇文章 [Simple one-liner ViewBinding in Fragments and Activities with Kotlin](https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c) 274 | 和 [ViewBindingDelegate](https://github.com/hoc081098/ViewBindingDelegate) 开源库提供了思路 275 | 276 | 欢迎大家前去查看,思路非常的好 277 | 278 | 279 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | import com.hi.dhl.Config 2 | import com.hi.dhl.Deps 3 | 4 | apply plugin: 'com.android.application' 5 | apply plugin: 'kotlin-android' 6 | apply plugin: 'kotlin-android-extensions' 7 | apply plugin: 'kotlin-kapt' 8 | 9 | android { 10 | compileSdkVersion Config.compileSdkVersion 11 | buildToolsVersion Config.buildToolsVersion 12 | 13 | defaultConfig { 14 | applicationId "com.hi.dhl.jdatabinding.demo" 15 | minSdkVersion Config.minSdkVersion 16 | targetSdkVersion Config.targetSdkVersion 17 | versionCode Config.versionCode 18 | versionName Config.versionName 19 | 20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | dataBinding { 31 | enabled = true 32 | } 33 | } 34 | 35 | 36 | dependencies { 37 | ext.remote = false 38 | 39 | implementation fileTree(dir: 'libs', include: ['*.jar']) 40 | implementation Deps.kotlinStdlibJdk7 41 | 42 | implementation Deps.appcompat 43 | implementation Deps.ktsCode 44 | implementation Deps.constraintlayout 45 | 46 | implementation Deps.Lifecycle.ktsViewmodel 47 | implementation Deps.recyclerview 48 | 49 | implementation Deps.Koin.viewModel 50 | implementation Deps.Koin.fragment 51 | 52 | if (remote) { 53 | implementation 'com.hi-dhl:jdatabinding:1.0.5' 54 | } else { 55 | implementation project(':jdatabinding') 56 | } 57 | 58 | 59 | testImplementation Deps.junit 60 | androidTestImplementation Deps.junitExt 61 | androidTestImplementation Deps.espressoCore 62 | } 63 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hi/dhl/jdatabinding/demo/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo 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.hi.dhl.jdatabinding.demo", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/App.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo 2 | 3 | import android.app.Application 4 | import com.hi.dhl.jdatabinding.demo.di.appModules 5 | import org.koin.android.ext.koin.androidContext 6 | import org.koin.android.logger.AndroidLogger 7 | import org.koin.androidx.fragment.koin.fragmentFactory 8 | import org.koin.core.context.loadKoinModules 9 | import org.koin.core.context.startKoin 10 | import org.koin.core.logger.Level 11 | 12 | /** 13 | *
14 |  *     author: dhl
15 |  *     date  : 2020/4/19
16 |  *     desc  :
17 |  * 
18 | */ 19 | class App : Application() { 20 | 21 | override fun onCreate() { 22 | super.onCreate() 23 | startKoin { 24 | AndroidLogger(Level.DEBUG) 25 | androidContext(this@App) 26 | fragmentFactory() 27 | loadKoinModules(appModules) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/AppDialog.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.Window 7 | import com.hi.dhl.jdatabinding.binding 8 | import com.hi.dhl.jdatabinding.demo.databinding.DialogAppBinding 9 | 10 | /** 11 | *
12 |  *     author: dhl
13 |  *     date  : 2020/4/9
14 |  *     desc  :
15 |  * 
16 | */ 17 | class AppDialog( 18 | context: Context, 19 | val title: String? = null, 20 | val message: String? = null, 21 | val yes: AppDialog.() -> Unit 22 | ) : Dialog(context, R.style.AppDialog) { 23 | private val mBinding: DialogAppBinding by binding() 24 | 25 | init { 26 | requireNotNull(message) { "message must be not null" } 27 | } 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | requestWindowFeature(Window.FEATURE_NO_TITLE) 32 | 33 | mBinding.apply { 34 | display.text = message 35 | btnNo.setOnClickListener { dismiss() } 36 | btnYes.setOnClickListener { yes() } 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/binding/RecycleViewBinding.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.binding 2 | 3 | import androidx.databinding.BindingAdapter 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.hi.dhl.jdatabinding.demo.ui.Model 6 | import com.hi.dhl.jdatabinding.demo.ui.adapter.TestAdapter 7 | 8 | /** 9 | *
10 |  *     author: dhl
11 |  *     date  : 2020/3/23
12 |  *     desc  :
13 |  * 
14 | */ 15 | 16 | @BindingAdapter("adapter") 17 | fun bindAdapter(recyclerView: RecyclerView, adapter: TestAdapter) { 18 | recyclerView.adapter = adapter 19 | } 20 | 21 | @BindingAdapter("adapterList") 22 | fun bindAdapterList(recyclerView: RecyclerView, data: List?) { 23 | val adapter = recyclerView.adapter as? TestAdapter 24 | ?: throw RuntimeException(" adapter is null") 25 | data?.let { adapter.submitList(it, SubmitListCallback(recyclerView)) } 26 | recyclerView.requestFocus() 27 | recyclerView.scrollToPosition(0) 28 | } 29 | 30 | class SubmitListCallback(val recyclerView: RecyclerView) : Runnable { 31 | override fun run() { 32 | recyclerView.scrollToPosition(0) 33 | recyclerView.requestFocus() 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.di 2 | 3 | import com.hi.dhl.jdatabinding.demo.ui.FragmentTest 4 | import com.hi.dhl.jdatabinding.demo.ui.MainViewModel 5 | import org.koin.androidx.fragment.dsl.fragment 6 | import org.koin.androidx.viewmodel.dsl.viewModel 7 | import org.koin.dsl.module 8 | 9 | /** 10 | *
11 |  *     author: dhl
12 |  *     date  : 2020/4/19
13 |  *     desc  :
14 |  * 
15 | */ 16 | val viewModelsModule = module { 17 | viewModel { MainViewModel() } 18 | } 19 | 20 | val fragmentModules = module { 21 | fragment { FragmentTest(get()) } 22 | } 23 | 24 | val appModules = listOf(fragmentModules, viewModelsModule) -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/ui/FragmentTest.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.ui 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.Fragment 6 | import com.hi.dhl.jdatabinding.binding 7 | import com.hi.dhl.jdatabinding.demo.R 8 | import com.hi.dhl.jdatabinding.demo.databinding.FragmentTestBinding 9 | import com.hi.dhl.jdatabinding.demo.ui.adapter.TestAdapter 10 | 11 | /** 12 | *
13 |  *     author: dhl
14 |  *     date  : 2020/4/9
15 |  *     desc  :
16 |  * 
17 | */ 18 | 19 | class FragmentTest(val mainViewModel: MainViewModel) : Fragment(R.layout.fragment_test) { 20 | 21 | val bind: FragmentTestBinding by binding() 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | bind.apply { 26 | viewModel = mainViewModel 27 | testAdapter = TestAdapter() 28 | lifecycleOwner = this@FragmentTest 29 | } 30 | } 31 | 32 | companion object { 33 | const val KEY_NAME = "FragmentTest" 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.ui 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import android.widget.Toast 6 | import androidx.fragment.app.FragmentActivity 7 | import com.hi.dhl.jdatabinding.binding 8 | import com.hi.dhl.jdatabinding.demo.AppDialog 9 | import com.hi.dhl.jdatabinding.demo.R 10 | import com.hi.dhl.jdatabinding.demo.databinding.ActivityMainBinding 11 | import org.koin.androidx.fragment.android.setupKoinFragmentFactory 12 | 13 | class MainActivity : FragmentActivity() { 14 | private val mBinding: ActivityMainBinding by binding() 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | setupKoinFragmentFactory() 18 | super.onCreate(savedInstanceState) 19 | mBinding.apply { 20 | dialog.setOnClickListener { 21 | val msg = getString(R.string.dialog_msg) 22 | AppDialog( 23 | context = this@MainActivity, 24 | message = msg, 25 | yes = { 26 | Toast.makeText(this@MainActivity, msg, Toast.LENGTH_SHORT).show() 27 | }).show() 28 | } 29 | 30 | if (savedInstanceState == null) { 31 | addFragment() 32 | } 33 | } 34 | } 35 | 36 | private fun addFragment() { 37 | val arguments = Bundle().apply { 38 | putString(FragmentTest.KEY_NAME, "来源于 MainActivity") 39 | } 40 | 41 | supportFragmentManager.beginTransaction() 42 | .replace(R.id.container, FragmentTest::class.java, arguments) 43 | .commit() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/ui/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.ui 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.recyclerview.widget.DiffUtil 6 | 7 | /** 8 | *
 9 |  *     author: dhl
10 |  *     date  : 2020/4/19
11 |  *     desc  :
12 |  * 
13 | */ 14 | class MainViewModel : ViewModel() { 15 | public val mLiveData: MutableLiveData> by lazy { MutableLiveData>() } 16 | private val mData: MutableList by lazy { mutableListOf() } 17 | 18 | init { 19 | (1..30).forEach { index -> 20 | val model = Model( 21 | index, 22 | "title" + index, 23 | System.currentTimeMillis() 24 | ); 25 | mData.add(model) 26 | } 27 | 28 | mLiveData.value = mData 29 | } 30 | } 31 | 32 | data class Model(val id: Int, val title: String, val time: Long) { 33 | companion object { 34 | val CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { 35 | // 判断两个Objects 是否代表同一个item对象, 一般使用Bean的id比较 36 | override fun areItemsTheSame(oldItem: Model, newItem: Model): Boolean = 37 | oldItem.id == newItem.id 38 | 39 | // 判断两个Objects 是否有相同的内容。 40 | override fun areContentsTheSame(oldItem: Model, newItem: Model): Boolean = true 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/ui/adapter/HeaderViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.ui.adapter 2 | 3 | import android.view.View 4 | import com.hi.dhl.jdatabinding.DataBindingViewHolder 5 | import com.hi.dhl.jdatabinding.demo.databinding.RecycieItemHeaderBinding 6 | import com.hi.dhl.jdatabinding.demo.ui.Model 7 | 8 | /** 9 | *
10 |  *     author: dhl
11 |  *     date  : 2020/4/19
12 |  *     desc  :
13 |  * 
14 | */ 15 | class HeaderViewHolder(view: View) : DataBindingViewHolder(view) { 16 | 17 | val binding: RecycieItemHeaderBinding by viewHolderBinding(view) 18 | 19 | override fun bindData(data: Model, position: Int) { 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/ui/adapter/TestAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.ui.adapter 2 | 3 | import android.view.View 4 | import com.hi.dhl.jdatabinding.DataBindingListAdapter 5 | import com.hi.dhl.jdatabinding.DataBindingViewHolder 6 | import com.hi.dhl.jdatabinding.demo.R 7 | import com.hi.dhl.jdatabinding.demo.ui.Model 8 | 9 | /** 10 | *
11 |  *     author: dhl
12 |  *     date  : 2020/4/19
13 |  *     desc  :
14 |  * 
15 | */ 16 | class TestAdapter : DataBindingListAdapter(Model.CALLBACK) { 17 | 18 | override fun viewHolder(layout: Int, view: View): DataBindingViewHolder = when (layout) { 19 | R.layout.recycie_item_header -> HeaderViewHolder(view) 20 | else -> TestViewHolder(view) 21 | } 22 | 23 | override fun layout(position: Int): Int = when (position) { 24 | 0 -> R.layout.recycie_item_header 25 | getItemCount() - 1 -> R.layout.recycie_item_footer 26 | else -> R.layout.recycie_item_test 27 | } 28 | 29 | override fun getItemCount(): Int = super.getItemCount() + 2 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hi/dhl/jdatabinding/demo/ui/adapter/TestViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.hi.dhl.jdatabinding.demo.ui.adapter 2 | 3 | import android.view.View 4 | import com.hi.dhl.jdatabinding.DataBindingViewHolder 5 | import com.hi.dhl.jdatabinding.demo.databinding.RecycieItemTestBinding 6 | import com.hi.dhl.jdatabinding.demo.ui.Model 7 | 8 | /** 9 | *
10 |  *     author: dhl
11 |  *     date  : 2020/4/19
12 |  *     desc  :
13 |  * 
14 | */ 15 | class TestViewHolder(view: View) : DataBindingViewHolder(view) { 16 | 17 | val binding: RecycieItemTestBinding by viewHolderBinding(view) 18 | 19 | override fun bindData(data: Model, position: Int) { 20 | binding.apply { 21 | model = data 22 | executePendingBindings() 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 |