DataBinding框架(一)
用途:
- 去掉Activity或Fragment中的处理UI的逻辑代码
- XML成为UI唯一的真实来源
- 不再使用findViewById(cached)
优势:
1 性能高 安全
2 保证执行在主线程
类似方案:
ButterKnife
android annotation
RoBoBinding
简单的数据绑定
1 添加依赖
app的gradle中的android中:
dataBinding {
enabled = true
}
2 在XML中,在layout根节点下(没有则创建) 并创建< data>标签
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!--(后面写Variable)-->
</data>
<!-- 其他界面布局以及组件-->
// 这里一定注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// 后面的这部分 相当于是之前普通的布局 **要有一个大的布局类型** 里面才是小的布局、组件
<LinearLayout>
....
</LinearLayout>
</layout>
3 创建POJO类 (或javaBean类)
public class User {
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return lastName;
}
}
4 定义 Variable
<data>
<variable
name="User"
<!-- 包下的User类的对象-->
type="com.scnu.sihao.testmvvm.User"/>
<!-- 也可以用String类型,下面的//*****后的与这里对应-->
<variable name="firstName" type="String" />
<variable name="lastName" type="String" />
</data>
在data内描述了一个名为user的变量属性,使其可以在这个layout中使用
其中 type 属性就是我们在 Java 文件中定义的 User 类
也可以采用import形式
要具体到类 不能用 *
<data>
<import type="com.example.User" />
<variable name="user" type="User" />
</data>
如果导入不同的包中有相同的类名,使用import 中的 alias 属性
<import type="com.example.home.data.User" />
<!--别名为DetailUser-->
<import type="com.examle.detail.data.User" alias="DetailUser" />
<variable name="user" type="DetailUser" />
**5 编译之后,插件会根据 xml 的命名(activity_main),在project->app->build-> output会生成ActivityMainBinding类,并且该类继承ViewDataBinding **
将对应XML的首字符大写,其他单词首字符也大写,然后加上Binding
记得要编译,ActivityMainBinding类才会出现
在
app\build\generated\source\apt\debug\com\scnu\sihao\sinaweibodemo\databinding
中可以找到
也可以在< data >标签中自定义class名
<data class=“XXXXXX”
...
...>
</data>
6 绑定 Variable
修改MainActivity中的onCreate,用 DatabindingUtil.setContentView() 来替换掉 setContentView(),然后创建一个 user 对象,通过 binding.setUser(user) 与 variable 进行绑定
// import android.databinding.DataBindingUtil;
// import 包名.databinding.ActivityMainBinding;
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User("testFirst", "testLast");
binding.setUser(user); // 根据XML的variable的name值,setUser方法自动生成
//或者通过
// binding.setVariable();
//**************************与上面的string对应
binding.setFirstName("自定义值");//根据XML的variable的name值,setFirstName方法自动生成
binding.setLastName("自定义值");//根据XML的variable的name值,setLastName方法自动生成
如果使用的 ListView 或者RecyclerView可以使用这个
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
有时候不能预先知道 Bingding 类的种类,这时候可以使用DataBindingUtil 类
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
7 可以在XML中使用Variable了
android:text="@{user.firstName}"
android:text="@{firstName}"
android:text="@{lastName}"
使用 通过Binding.setXXX()自动生成的方法传值给Bean类,Bean类再通过set方法分字段,XML中的通过对象.XXX去调用getXXX方法,去获得值。
不支持的语法:
this
super
new
其他都类似java
在Activity中,创建Databinding连接XML,然后setViewModel()连接model
在ViewModel中 进行数据处理和事件监听
至此,一个简单的数据绑定就完成了
8 事件绑定
- 方法绑定:
在XML中
<variable
name="clickListener"
type="com.scnu.sihao.testmvvm.MyClickListener" />
<!-- 使用创建的包下的点击事件类-->
创建MyClickListener
public void myOnClick(View view){
Toast.makeText(view.getContext(),"onclick",Toast.LENGTH_SHORT).show();
Log.i("onclick","onclick is ok!");
}
public boolean myOnLongClick(View view){
Toast.makeText(view.getContext(),"onLongclick",Toast.LENGTH_SHORT).show();
Log.i("onLongclick","onLongclick is ok!");
return true;
}
接着在XML中的按钮中引用
android:onClick="@{clickListener::myOnClick}"
android:onLongClick="@{clickListener::myOnLongClick}"
//或者
android:onClick="@{clickListener.myOnClick}"
android:onLongClick="@{clickListener.myOnLongClick}"
最后 在 onCreate 中
mybinding.setClickListener(new MyClickListener());
-监听器绑定:
lambda 表达式
可以传参数到代码中
创建方法 参数为从XML中得到的数据
public void myClickListener(User user){
Log.i("myclickListener",user.getFirstName());
}
XML中使用
<!-- 传入user参数-->
android:onClick="@{() -> clickListener.myClickListener(user)}"
9 使用类方法
- 普通方法的绑定,直接在布局文件中,通过变量名调用方法即可,和java代码里面使用相似
android:text="@{user.getName(user.firstName, user.lastName)}
10 自动数据绑定
直接修改数据对象并不能直接更新 UI,Android的Data Binding模块给提供了通知机制,有3种类型,分别对应于类(Observable),字段(ObservableField),集合类型(Observable Collections)
- Observable(类)
思想步骤:
1 XML中定义包下的类对象
如
<variable
name="user"
type="com.scnu.sihao.User"/>
2 创建Bean类继承BaseObservable,然后在getXX方法上加入@Bindable注解
3 在Apdater或Activity中,通过binding.setUser(new User(AAA) ) 传入对象给XML的User(创建对象时可以传递参数AAA)
4 在Bean(User)中 setXX中获取这个参数并且针对不同字段的需要获取AAA的具体值,AAA.XXX,并且set中写notifyChange()方法 去自动同步更新
再在getXXX中retrun this.XXX就好了
5 也可以new Bean(AAA) AAA这里直接传数值,然后get去get就好了
例子:
// 第一步 继承一个BaseObservable
public class User extends BaseObservable {
private String firstName;
private String lastName;
// 第二步 加上Bindable注释
@Bindable
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
// 第三步 当调用setFirstName函数时 自动调用notify去更新UI显示的数据
notifyPropertyChanged(包名.项目名.BR.firstName);
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(包名.项目名.BR.lastName);
}
}
@Bindable注解是为了在编程的时候生成 BR 类,Bindable会在 BR 类中生成一个域变量 ,来表明这个域有没有被改变。通过代码可以看出,当数据发生变化时还是需要手动发出通知。 通过调用 notifyPropertyChanged(BR.firstName) 可以通知系统 BR.firstName 这个 entry 的数据已经发生变化,需要更新 UI。
-
普通字段绑定:
见:
http://blog.csdn.net/weixin_37577039/article/details/78725838
的第二点
注意这里不需要set和get方法 因为set get方法是针对Bean的对象的 -
ObservableFields(字段)
-具体到成员变量,这种方式无需继承 BaseObservable
-如果变量比较少,都是简单的数据类型时,可以用ObservableFields
ObservableFields 自包含具有单个字段的observable对象。它有所有基本类型和一个是引用类型。要使用它需要在data对象中创建public final字段
private static class User extends BaseObservable {
public final ObservableField<String> firstName =
new ObservableField<>();
public final ObservableField<String> lastName =
new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
// 对这些初始化值 也是使用 set和get操作
//当firstName、lastName变化时,UI 会得到通知,使用的赋值语句为
user.firstName.set("Google");
int age = user.age.get();
在ViewModel中
//在相应的逻辑处理中,也是使用setAge,setFirstName去修改参数
- Observable Collections(集合类型)
一些app使用更多的动态结构来保存数据。Observable集合允许键控访问这些data对象。ObservableArrayMap用于键是引用类型,如String。
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
//当 key 是 inter 是, ObservableArrayList 比较适用
ObservableArrayList< Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);
在layout文件中,通过String键可以访问map
<data>
<import type="android.databinding.ObservableMap"/>
<variable name="user" type="ObservableMap<String, Object>"/>
</data>
…
<!-- 通过key直接访问-->
<TextView
android:text='@{user["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user["age"])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
// 当key是int
<data>
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user" type="ObservableList<Object>"/>
</data>
…
<TextView
android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
- 立即绑定
变量或Observable改变后,在下一帧才进行改变,如果需要立即执行,则可以通过
executePendingBindings();
11 带 ID 的 View
Data Binding 有效降低了代码的冗余性,甚至完全没有必要再去获取一个 View 实例,但是情况不是绝对的,万一我们真的就需要了呢?不用担心,只要给 View 定义一个 ID,Data Binding 就会为我们生成一个对应的 final 变量。
<TextView
android:id="@+id/firstName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
上面代码中定义了一个 ID 为 firstName的 TextView,那么它对应的变量就是
public final TextView firstName; 就可以在java代码中使用这个firstName了
比如可以通过 binding.firstName.setText();赋值 (一般不这么用吧?)
12 ViewStubs
xml 中的 ViewStub 经过 binding 之后会转换成 ViewStubProxy, 具体代码可参考 ViewStubActivity.java
简单用代码说明一下,xml 文件与之前的代码一样,根节点改为 layout,在 LinearLayout 中添加一个 ViewStub,添加 ID。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
...>
<ViewStub
android:id="@+id/view_stub"
android:layout="@layout/view_stub"
... />
</LinearLayout>
</layout>
在 Java 代码中获取 binding 实例,为 ViewStubProy 注册 ViewStub.OnInflateListener 事件:
binding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
ViewStubBinding binding = DataBindingUtil.bind(inflated);
User user = new User("fee", "lang");
binding.setUser(user);
}
});
13 通过 @{} 可以直接把 Java 中定义的属性值赋值给 xml 属性
< TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
POJO:
普通java类 ,但不允许有业务方法,也不能携带有connection之类的方法
Java Bean :
可重用组件,类必须是具体的和公共的,并且具有无参数的构造器,有get,set的方法
inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById()的区别,inflate是加载一个布局文件,而findViewById则是从布局文件中查找一个控件。
ViewStub
ViewStub使用的是惰性加载的方式,即使将其放置于布局文件中,如果没有进行加载那就为空,不像其它控件一样只要布局文件中声明就会存在
Databinding结合RecyclerView和ImageView使用,以及Databinding的表达式,如数组字段,资源引用,参考下一篇博客:
http://blog.csdn.net/weixin_37577039/article/details/78725838