四篇文章带你快速入门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.simpleName
val viewModelOne by ViewModelLazy<ViewModelOne>(ViewModelOne::class,
{ viewModelStore },
{ defaultViewModelProviderFactory })
val viewModelOnes by viewModels<ViewModelOne> { defaultViewModelProviderFactory }
//android view model
val 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.simpleName
var counter = 0
init {
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.simpleName
var counter = 0
init {
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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android: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
的特殊符号需要转义类似&
-
绑定
xml
与data
格式:@{}
、@={}
(双向绑定) -
??
判空 -
?:
三目运算符 -
@string/str_name
资源引用,可用占位符format
-
+
拼接字符,使用``反引号 -
default
设置默认值 -
include
绑定 -
点击事件
在RecyclerView中使用DataBinding
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha06'
<androidx.recyclerview.widget.RecyclerView
app: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" />
高阶用法
-
@BindingConversion
如果属性不能匹配类型参数将自动根据类型参数匹配到该注解修饰的方法来转换
@BindingConversion
fun str2Color(str: String): Drawable {
return when (str) {
"red" -> {
ColorDrawable(Color.RED)
}
"blue" -> ColorDrawable(Color.BLUE)
else -> {
ColorDrawable(Color.YELLOW)
}
}
}
-
@BindingAdapter
设置自定义属性. 可以覆盖系统原有属性
@BindingAdapter("android:textColor", requireAll = false)
fun getColor(view: TextView, type: Int) {
val color = when (type) {
0 -> R.color.colorAccent
1 -> R.color.colorPrimaryDark
2 -> android.R.color.holo_red_dark
3 -> android.R.color.holo_orange_dark
else -> R.color.colorPrimary
}
view.setTextColor(view.context.resources.getColor(color))
}
-
@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 = null
address = "TianJin 天津"
observeName = ObservableField("observeName")
user = User()
observeUser = ObserveUser()
observeFieldUser = ObserveFieldUser()
btnChange.setOnClickListener {
name = "姚鑫"
address = null
user?.name = "孙悟空"
observeUser?.age = 18
observeUser?.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>
<variable
name="name"
type="String" />
<variable
name="address"
type="String" />
<variable
name="observeName"
type="androidx.databinding.ObservableField<String>" />
<variable
name="user"
type="com.yx.androidseniorpreparetest.fifth.User" />
<variable
name="observeUser"
type="com.yx.androidseniorpreparetest.fifth.ObserveUser" />
<variable
name="observeFieldUser"
type="com.yx.androidseniorpreparetest.fifth.ObserveFieldUser" />
<variable
name="adapter"
type="androidx.recyclerview.widget.RecyclerView.Adapter" />
<variable
name="info"
type="com.yx.androidseniorpreparetest.fifth.ItemBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="演示DataBinding"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{name}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{name??`Null Name`}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{address,default=`tianjin`}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{name==null?`null`:`nonull`}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{@string/str_name(name)}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{observeName}"
android:textColor="#000000"
android:textSize="20sp" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={observeName}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={user.name}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{observeUser.name + observeUser.age}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={observeUser.age+``}"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@={observeFieldUser.name}"
android:textColor="#000000"
android:textSize="20sp" />
<Button
android: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.RecyclerView
android: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" />
<include
info="@{info}"
layout="@layout/item_data_binding" />
</LinearLayout>
</layout>
user
class User {
var name = "姚鑫"
var age = 30
var desc = "DataBinding真好用!"
}
class ObserveUser : BaseObservable() {
var age = 31
var name = ""
set(value) {
notifyPropertyChanged(BR.name)
field = value + "yao"
}
@Bindable
get() {
return "$field 姚鑫Name"
}
var desc = "这里是ObserveUser描述"
set(value) {
notifyPropertyChanged(BR.desc)
field = value + "xin"
}
@Bindable
get() {
return "$field 姚鑫Desc"
}
var str = ""
set(value) {
field = value
notifyPropertyChanged(BR.str)
}
@Bindable
get() {
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 = bean
binding.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" />
<variable
name="info"
type="com.yx.androidseniorpreparetest.fifth.ItemBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android: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.colorAccent
1 -> R.color.colorPrimaryDark
2 -> android.R.color.holo_red_dark
3 -> android.R.color.holo_orange_dark
else -> R.color.colorPrimary
}
view.setTextColor(view.context.resources.getColor(color))
}
网友评论