Jetpack-DataBinding数据双向绑定(我只用到了单向绑定)
DataBinding
用来替代 findViewById 等的实例化控件。(DataBinding是为了能更好的实现MVVM架构而设计的)
优势:
- 项目更简洁,可读性更高。部分与UI控件相关的代码可以在布局文件中完成
- 不再需要 findViewById() 方法
- 布局文件可以包含简单的业务逻辑。UI控件能够直接与数据模型中的字段绑定,甚至能相应用户的交互。
用viewmodel、livedata、databinding架构图
此时把findViewById等绑定控件的操作都抽到databinding中进行管理
DataBinding基本用法
打开databinding开关
app/build.gradle--android--defaultConfig
最后添加
1 | dataBinding{ |
布局中使用databinding布局
最外面套一层layout,再加data标签(用来管理数据)
1 |
|
其中
@{写类似java的代码}
。其中若调用方法则用()->函数
<variable />
内的数据类型可以是自定义类型,也可以是基本类型,如String
绑定事件的另一种写法
1 | public class EventHanleActivity extends AppCompatActivity{ |
1 | <layout> |
通过布局表达式调用;或者通过双冒号::
来调用;
使用databinding后解放Activity中的findViewById的关联
1 | import androidx.appcompat.app.AppCompatActivity |
给布局文件传递对象
可以在 Activity 中调用
setVariable(BR.myData, 具体对象)
把具体对象传递给布局文件BR类类似于Android中的R类,由DataBinding库自动生成,用于统一存放所有布局变量的id。
BR.myData
,后面就是布局中变量的name也可以在 Activity 中调用
setMyData(myData)
来传递对象DataBinding为了方便我们使用,为布局变量提供了Setter类。
DataBindingUtil源码内有好几种获取Binding对象的方法
1 | inflate |
二级页面的绑定【嵌套的子view中使用databinding】
二级页面:在布局文件中通过<include>
标签引用的页面
一级页面传递数据
一级页面传递给二级页面。在一级页面中添加 xmlns:app
命名空间,再通过命名空间xmlns:app
引用布局变量将数据对象传递给二级页面。(如:app:book="@{book}"
)
1 |
layout_content.xml
中
1 |
BindingAdapter的原理
在 gradle 启用了 DataBinding 库后会自动生成绑定所需要的各种类,包括大量针对 UI 控件的、名为 XXXBindingAdapter 的类。这些类中包含各种静态方法,且这些静态方法前都有 @BindingAdapter 标签,标签中的别名对应 UI 控件在布局中的属性。
如:DataBinding 库针对 android:padding
属性所编写的代码
1 | public class ViewBindingApdapter{ |
如:DataBinding
库为 TextView
生成的 TextViewBindingAdapter
类的部分源码。源码展示了 DataBinding
库针对 android:text
属性所编写的代码
1 | public class TextViewBindingAdapter{ |
DataBinding
库以静态方法的形式为 UI 控件的各个属性绑定了相应的代码。若工程师在 UI 控件的 属性中使用了布局表达式,那么当布局文件被渲染时,属性所绑定的静态方法就会被自动调用。
1 | <TextView |
上面这个代码,在TextView
被渲染时,android:padding
和android:text
属性会分别自动调用ViewBindingAdapter.setPadding()
和TextViewBindingAdapter.setText()
。
自定义BindingAdapter
BindingAdapter
中的方法均为静态方法。第一个参数是调用者本身。第二个参数是布局文件在调用该方法时传递过来的参数。
如:对 ImageView
写个自定义 BindingAdapter
app/build.gradle
导入Picasso
这个加载图片的库
1 | dependencies{ implementataion 'com.squareup.picasso:picasso:2.71828'} |
清单文件中添加权限
1 | <uses-permission android:name="andorid.permission.INTERNET"/> |
写自定义 BindingAdapter
类
1 | public class ImageViewBindingAdapter{ |
然后在布局文件中调用这个 BindingAdapter
1 | <layout ...> |
在 Activity 中为布局变量赋值
1 | public class BindingAdapterActivity extends Activity{ |
多参数重载
1 | public class ImageViewBindingAdapter{ |
1 | <layout ...> |
可选旧值
某些情况下希望在方法中得到该属性的旧值。如修改padding这个属性,想要得到修改前的padding,以防止方法重复调用。代码如下:
1 |
|
注意:使用可选旧值时,方法中参数顺序需要先写旧值,后写新值。即oldPadding在前,newPadding在后。
例子演示可选旧值
1 | <layout> |
1 | public class BindingAdapterActivity extends Activity{ |
双向绑定
不是继承 ViewModel,而是继承 BaseObservable。本质都是观察者模式。BaseObservable 是 DataBinding 库为了方便实现观察者模式而提供的类。
1 | /** |
notifyPropertyChanged()
是 BaseObservable
类中的一个方法
1 | /** |
完成双向绑定
1 | <layout> |
注意用的是 “
@={}
”
运行程序,当修改EditText值时,类中的Setter会被自动调用,userName字段会随着EditText内容的变化而变化。
使用ObservableField优化双向绑定
ObservableField<T>
将普通对象包装成一个观察对象。ObservableField可用于包装各种基本类型、集合数组类型和自定义类型的数据。
上面的例子用 ObservableField 进行优化
1 | public class TwoWayBindingViewModel{ |
其他的不变,只改了继承 BaseObservable 的ViewModel类
ObservableField与LiveData
ObservableField的使用方式与LiveData的很像。实际上,二者可以替换使用。
二者区别在于,LiveData与生命周期相关,通常 在ViewModel中使用,并且在页面中通过 observe() 方法对变化进行监听。而本示例中的双向绑定无须加入额外的代码,耦合度更低。
生成的绑定类
可以自定义绑定类名称
默认情况下,绑定类是根据布局文件的名称生成的,以大写字母开头,移除下划线
_
,将后一个字母首字母大写并加后缀Binding
组成。该类位于模块包下databinding
包中。例如布局文件是contact_item.xml
,模块包是com.example.my.app
,则绑定类是com.example.my.app.databinding.ContactItemBinding
我们在布局文件的
<data>
标签中添加class
属性,可以指定绑定类的名称。(模块包路径是不变的)1
2<data class="ContactItem">
</data>也可以指定绑定类的在其他文件夹中生成(也可以只是加“句点”
.ContactItem
)1
2<data class="com.example.ContactItem">
</data>则生成的绑定类
com.example.databinding.ContactItem
RecyclerView的绑定机制
app/build.gradle
1 | android{ |
编写RecyclerView的布局文件
1 | <layout > |
编写每个 Item 的布局文件
1 | <layout> |
1 | /** |
1 | /** |
自定义属性,xml给java传值
java中
1 | public void setClick(int a){ |
xml中
1 | app:click="@{}" |
注意:
在java中,定义的方法要
public
、set
开头且第二个单词首字母大写、要有入参在xml中,自定义属性是小写的(去掉set的方法名),入参要用
@{}