文章目录

  • 四篇文章带你快速入门Jetpck(中)之ViewModel,DataBinding
    • Jetpack
      • 官方推荐架构
    • ViewModel
      • 添加依赖
      • 创建ViewModel
      • 初始化ViewModel的方法
      • 生命周期自动感知
      • 向ViewModel传递参数
      • 示例
    • DataBinding
      • 添加依赖
      • UI改造
      • UI中关联xml的DataBinding
      • xml中变量的使用
      • 在RecyclerView中使用DataBinding
      • 高阶用法
      • 示例

四篇文章带你快速入门Jetpck(中)之ViewModel,DataBinding

Jetpack

Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。

Android Architecture Component (AAC)

官方推荐架构

请注意,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是唯一依赖于其他多个类的类;在本例中,存储区依赖于持久性数据模型和远程后端数据源。

ViewModel

ViewModel应该可以算是JetPack组建中最重要的组件之一。

ViewModel是专门用于存放与界面相关的数据。

ViewModel的生命周期贯穿于Activity整个生命周期,只有Activity销毁时,ViewModel才会销毁。

添加依赖

implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01'
implementation 'androidx.activity:activity-ktx:1.2.0-beta01'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.3.0-beta01'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-beta01'

创建ViewModel

class ViewModelOne : ViewModel() {}

初始化ViewModel的方法

val viewModelOne by ViewModelLazy<ViewModelOne>(ViewModelOne::class,
{ viewModelStore },
{ defaultViewModelProviderFactory })
val viewModelOnes by viewModels<ViewModelOne> { defaultViewModelProviderFactory }

非懒加载模式必须要等Activity 在OnCreate后才能初始化,否则运行报错。

val viewModelPro = ViewModelProvider(viewModelStore,defaultViewModelProviderFactory
).get(ViewModelOne::class.java)

生命周期自动感知

onClear回调函数处理资源回收closeWithRuntimeException

ViewModel绝对不能引用View、Lifecycle或任何可能包含对Activity上下文的引用的类

向ViewModel传递参数

class VmFactory(private val count: Int) : ViewModelProvider.Factory {override fun <T : ViewModel?> create(modelClass: Class<T>): T {if (modelClass.isAssignableFrom(ViewModelOne::class.java)) {return ViewModelOne() as T} else if (modelClass.isAssignableFrom(ViewModelTwo::class.java)) {return ViewModelTwo(count) as T} else {throw ClassNotFoundException("class $modelClass ViewModel没有注册到工厂类")}}
}

示例

ViewModelActivity

class ViewModelActivity : AppCompatActivity() {val TAG = this.javaClass.simpleNameval viewModelOne by ViewModelLazy<ViewModelOne>(ViewModelOne::class,{ viewModelStore },{ defaultViewModelProviderFactory })val viewModelOnes by viewModels<ViewModelOne> { defaultViewModelProviderFactory }//android view modelval viewModelTwo by viewModels<ViewModelTwo> { VmFactory(10) }override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_view_model)val viewModelPro = ViewModelProvider(viewModelStore,defaultViewModelProviderFactory).get(ViewModelOne::class.java)Log.d(TAG, "viewModelOne ${viewModelOne.getNow()}")Log.d(TAG, "viewModelOnes ${viewModelOnes.getNow()}")Log.d(TAG, "viewModelPro ${viewModelPro.getNow()}")Log.d(TAG, "viewModelTwo ${viewModelTwo.getNow()}")btn_again.setOnClickListener {startActivity(Intent(this, ViewModelActivity::class.java))}}
}

ViewModelKt

class ViewModelOne : ViewModel() {val TAG = this.javaClass.simpleNamevar counter = 0init {Log.d(TAG, "ViewModelOne创建")}fun getNow(): String {return "ViewModelOne: ${System.currentTimeMillis()}"}override fun onCleared() {super.onCleared()Log.d(TAG, "ViewModelOne销毁")}
}class ViewModelTwo(count: Int) : ViewModel() {val TAG = this.javaClass.simpleNamevar counter = 0init {Log.d(TAG, "ViewModelTwo创建")counter = count}fun getNow(): String {return "ViewModelTwo: ${System.currentTimeMillis()}  ${this.counter}"}override fun onCleared() {super.onCleared()Log.d(TAG, "ViewModelTwo销毁")}
}

VmFactory

class VmFactory(private val count: Int) : ViewModelProvider.Factory {override fun <T : ViewModel?> create(modelClass: Class<T>): T {if (modelClass.isAssignableFrom(ViewModelOne::class.java)) {return ViewModelOne() as T} else if (modelClass.isAssignableFrom(ViewModelTwo::class.java)) {return ViewModelTwo(count) as T} else {throw ClassNotFoundException("class $modelClass ViewModel没有注册到工厂类")}}
}

ViewModel.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:id="@+id/btn_again"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="启动界面" /></LinearLayout></LinearLayout>

DataBinding

添加依赖

//在module的build.gradle中
apply plugin: 'kotlin-kapt'//必须
android{//AS 4.0 以下,dataBinding{enabled true}//AS 4.1之后bindingFeature{dataBinding = true// for view binding :// viewBinding = true}
}

UI改造

<layout><data class=""></data><!-- 原有的UI的xml布局放在layout标签内即可。data标签内存放用于xml的数据变量,类型 --><LinearLayout></LinearLayout></layout>

UI中关联xml的DataBinding

val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>(this,R.layout.activity_data_binding
)

xml中变量的使用

  • variable声明变量;import导入类型;对于xml的特殊符号需要转义类似&amp;

  • 绑定xmldata格式:@{}@={}(双向绑定)

  • ??判空

  • ?:三目运算符

  • @string/str_name资源引用,可用占位符format

  • +拼接字符,使用**``**反引号

  • default设置默认值

  • include绑定

  • 点击事件

在RecyclerView中使用DataBinding

implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha06'
<androidx.recyclerview.widget.RecyclerViewapp:adapter="@{adapter}"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"tools:itemCount="5"tools:listitem="@layout/item_data_binding" />

高阶用法

  1. @BindingConversion

    如果属性不能匹配类型参数将自动根据类型参数匹配到该注解修饰的方法来转换

@BindingConversion
fun str2Color(str: String): Drawable {return when (str) {"red" -> {ColorDrawable(Color.RED)}"blue" -> ColorDrawable(Color.BLUE)else -> {ColorDrawable(Color.YELLOW)}}
}
  1. @BindingAdapter

    设置自定义属性. 可以覆盖系统原有属性

@BindingAdapter("android:textColor", requireAll = false)
fun getColor(view: TextView, type: Int) {val color = when (type) {0 -> R.color.colorAccent1 -> R.color.colorPrimaryDark2 -> android.R.color.holo_red_dark3 -> android.R.color.holo_orange_darkelse -> R.color.colorPrimary}view.setTextColor(view.context.resources.getColor(color))
}
  1. @Bindable

    设置数据刷新视图. 自动生成BR的ID

    注解才会自动在build目录BR类中生成entry, 要求方法名必须以get开头

示例

DatabindingActivity

class DataBindingActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>(this,R.layout.activity_data_binding)binding.apply {name = nulladdress = "TianJin 天津"observeName = ObservableField("observeName")user = User()observeUser = ObserveUser()observeFieldUser = ObserveFieldUser()btnChange.setOnClickListener {name = "姚鑫"address = nulluser?.name = "孙悟空"observeUser?.age = 18observeUser?.name = "张曼玉"observeFieldUser?.name?.set("林志玲")}adapter = DataBindingAdapter()info = ItemBean(2,"Activity Item")}}
}

DatabindingActivity.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"><data><variablename="name"type="String" /><variablename="address"type="String" /><variablename="observeName"type="androidx.databinding.ObservableField&lt;String>" /><variablename="user"type="com.yx.androidseniorpreparetest.fifth.User" /><variablename="observeUser"type="com.yx.androidseniorpreparetest.fifth.ObserveUser" /><variablename="observeFieldUser"type="com.yx.androidseniorpreparetest.fifth.ObserveFieldUser" /><variablename="adapter"type="androidx.recyclerview.widget.RecyclerView.Adapter" /><variablename="info"type="com.yx.androidseniorpreparetest.fifth.ItemBean" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="演示DataBinding"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{name}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{name??`Null Name`}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{address,default=`tianjin`}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{name==null?`null`:`nonull`}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{@string/str_name(name)}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{observeName}"android:textColor="#000000"android:textSize="20sp" /><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@={observeName}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@={user.name}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{observeUser.name + observeUser.age}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@={observeUser.age+``}"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@={observeFieldUser.name}"android:textColor="#000000"android:textSize="20sp" /><Buttonandroid:id="@+id/btn_change"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@{`pink`}"android:gravity="center"android:text="改变值"android:textColor="#000000"android:textSize="20sp" /><androidx.recyclerview.widget.RecyclerViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"app:adapter="@{adapter}"app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"tools:itemCount="5"tools:listitem="@layout/item_data_binding" /><includeinfo="@{info}"layout="@layout/item_data_binding" /></LinearLayout></layout>

user

class User {var name = "姚鑫"var age = 30var desc = "DataBinding真好用!"
}class ObserveUser : BaseObservable() {var age = 31var name = ""set(value) {notifyPropertyChanged(BR.name)field = value + "yao"}@Bindableget() {return "$field 姚鑫Name"}var desc = "这里是ObserveUser描述"set(value) {notifyPropertyChanged(BR.desc)field = value + "xin"}@Bindableget() {return "$field 姚鑫Desc"}var str = ""set(value) {field = valuenotifyPropertyChanged(BR.str)}@Bindableget() {return "age $age name $name desc $desc"}
}class ObserveFieldUser {var name = ObservableField("姚鑫")var age = ObservableInt(30)var desc = ObservableField("DataBinding真好用!")var str = "age $age name $name desc $desc"
}

ItemBean


data class ItemBean(val type: Int, val text: String)

DataBindingAdapter

class DataBindingAdapter : RecyclerView.Adapter<DataBindingAdapter.DataBindingViewHolder>() {private val mList = mutableListOf<ItemBean>()init {for (i in 0..5) {mList.add(ItemBean(i, "明细 $i"))}}class DataBindingViewHolder(private val binding: ItemDataBindingBinding) :RecyclerView.ViewHolder(binding.root) {fun bind(bean: ItemBean) {binding.info = beanbinding.executePendingBindings()}}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataBindingViewHolder {return DataBindingViewHolder(ItemDataBindingBinding.inflate(LayoutInflater.from(parent.context),parent,false))}override fun onBindViewHolder(holder: DataBindingViewHolder, position: Int) {holder.bind(mList[position])}override fun getItemCount(): Int = mList.size
}

ItemDataBinding.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><data><import type="com.yx.androidseniorpreparetest.fifth.DataBindingUtilsKt" /><variablename="info"type="com.yx.androidseniorpreparetest.fifth.ItemBean" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{DataBindingUtilsKt.getTitle(info.text)}"android:textColor="@{info.type}"android:textSize="20sp"tools:text="content" /></LinearLayout>
</layout>

DataBindingUtils

@BindingConversion
fun str2Color(str: String): Drawable {return when (str) {"red" -> {ColorDrawable(Color.RED)}"blue" -> ColorDrawable(Color.BLUE)else -> {ColorDrawable(Color.YELLOW)}}
}fun getTitle(type: String): String {return "type:$type"
}@BindingAdapter("android:textColor", requireAll = false)
fun getColor(view: TextView, type: Int) {val color = when (type) {0 -> R.color.colorAccent1 -> R.color.colorPrimaryDark2 -> android.R.color.holo_red_dark3 -> android.R.color.holo_orange_darkelse -> R.color.colorPrimary}view.setTextColor(view.context.resources.getColor(color))
}

项目代码

四篇文章带你快速入门Jetpck(中)之ViewModel,DataBinding相关推荐

  1. 递归很套娃怎么办?来,这篇文章带你快速领悟递归的奥义

    文章目录 递归的定义和必要条件 初步了解 打印一个数的每一位 计算一个数的每位之和 计算一个数的阶乘 进阶探讨 二分查找法递归实现 递归实现字符串函数strlen 递归实现字符串逆序 总结 递归的定义 ...

  2. 想自学Python却不知该如何入门?这篇文章带你轻松入门Python

    以2019年的资料为蓝本,2018年的资料为补充,还参考了一些网上的教程,旨在更好地总结Python基础知识,力求简明扼要,以供实战演练时能够快速查询遗忘的知识点.学一门语言贵在坚持用它,不用就淡忘了 ...

  3. 还不会 Vue3 ?一篇笔记带你快速入门

    1.Vue3简介 2020年9月18日,Vue.js发布3.0版本,代号:One Piece(海贼王) 耗时2年多.2600+次提交.30+个RFC.600+次PR.99位贡献者 github上的ta ...

  4. 带你快速入门AXI4总线--AXI4-Full篇(3)----XILINX AXI4-Full接口IP源码仿真分析(Master接口)

    写在前面 接slave接口篇,本文继续打包一个AXI4-Full-Master接口的IP,学习下源码,再仿真看看波形. 带你快速入门AXI4总线--AXI4-Full篇(2)----XILINX AX ...

  5. 带你快速入门AXI4总线--AXI4-Stream篇(1)----AXI4-Stream总线

    写在前面 随着对XILINX器件使用的深入,发现越来越多的IP都选配了AXI4的接口.这使得只要学会了AXI4总线的使用,基本上就能对XILINX IP的使用做到简单的上手.所以学会AXI4总线,对X ...

  6. 带你快速入门AXI4总线--AXI4-Full篇(1)----AXI4-Full总线

    写在前面 AXI4系列链接:带你快速入门AXI4总线--汇总篇(直达链接) 1.什么是AXI4-Full? AXI 表示 Advanced eXtensible Interface(高级可扩展接口), ...

  7. openGauss数据库源码解析系列文章——openGauss开发快速入门(二)

    在上一篇openGauss数据库源码解析系列文章--openGauss开发快速入门(上)中,我们介绍了openGauss的安装部署方法,本篇将具体介绍openGauss基本使用. 二. openGau ...

  8. 【效率】超详细!手把手带你快速入门 GitHub!

    作者:Peter     编辑:JackTian 来源:公众号「杰哥的IT之旅」 快速入门GitHub GitHub在程序开发领域家喻户晓,现在几乎整个互联网的开发者都将版本管理工具GitHub作为版 ...

  9. 设计模式一网打尽,40余篇文章带你领略设计模式之美

    文章末尾附带GitHub开源下载地址. 该文章的最新版本已迁移至个人博客[比特飞],单击链接 设计模式一网打尽,40余篇文章带你领略设计模式之美 | .Net中文网 访问. 设计模式概述 20世纪80 ...

最新文章

  1. java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...
  2. android按钮点击后闪退_CAD如何恢复自动崩溃、闪退的文件?
  3. ElasticSearch查询 基础篇
  4. c ++产生不同的随机数_C ++程序生成随机密码
  5. GlusterFS-Kubernetes云原生存储
  6. Spark SQL中的DataFrame
  7. JavaScript的原型和原型链
  8. 虚拟机访问本地mysql_如何在本地远程连接linux虚拟机上面的mysql
  9. mybatis-generator 根据表生成对应文件
  10. 查询字符串中子字符串所有出现位置
  11. 将 .json 格式 转换成 .xml格式
  12. selenium自动化测试登录qq空间遇到的错误
  13. SQL Server的3种恢复模式(Simple,Full,Bulk-logged)
  14. 携程2021年国庆出游报告出炉
  15. MATLAB实现在不同Es/N0情况下,QPSK、16QAM、64QAM误码率结果仿真图(包含软硬判决)
  16. 移动端测试=== 两个概念 内存泄漏 和 内存溢出 【转】
  17. 【摘记】ABD-Net: Attentive but diverse Person Re-Id
  18. ionic自动生成icon和splash
  19. 云服务器怎么划分虚拟主机,云服务器 划分虚拟主机
  20. 要命啦!Word中快速录入大全,内含快捷键小技巧,快来一起学习!

热门文章

  1. 修复因更新iCloud设置而卡住的iPhone / iPad的六种方法
  2. 并发-FutureTask
  3. MySQL 实现不同分组取不同值
  4. 思科无线AP (AIR-AP1832I-K9)瘦ap 刷 胖ap
  5. 近距离参观VR与汽车技术的结合,了解汽车如何使用VR技术
  6. EA周报 | 字节跳动上线搜索引擎;电影《哪吒之魔童降世》累计综合票房破15亿;鸿蒙系统首发设备欲屏蔽开机广告...
  7. 802.11协议精读10:节能模式(PSM)
  8. mysql slap_mysqlslap压力测试介绍
  9. RPA为大型企业带来发展新契机
  10. 《Rethinking Video Anomaly Detection - A Continual Learning Approach》异常检测 WACV-2022