Android DataBinding 简介:
Rain.tang
QQ:410990689
DataBingding导入:
在高版本的Android Studio及gradle 中已经支持了DataBinding的使用,只需要在build.gradle中将其开启
android {
...
//导入dataBinding支持
dataBinding{
enabled true
}
...
}
分主线功能使用介绍:
简单的替换findViewById :
<!--布局以layout作为根布局-->
<layout>
<!--我们需要展示的布局-->
<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="www.zhang.com.databinding.MainActivity">
<Button
android:id="@+id/main_tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="不用findViewById" />
</LinearLayout>
</layout>
MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通过DataBInding加载布局都会对应的生成Binding对象,如ActivityMainBinding,对象名在布局文件名称后加上了一个后缀Binding
ActivityMainBinding binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main);
//使用xml中使用控件只需要 binding.控件id直接使用
binding.main_tv1
}
控件绑定基础数据类型:
布局文件
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<!--绑定基本数据类型及String-->
<!--name: 和java代码中的对象是类似的,名字自定义-->
<!--type: 和java代码中的类型是一致的-->
<!--目前了解同一xml中layout中只能包括一层<data>-->
<variable
name="content"
type="String" />
<variable
name="enabled"
type="boolean" />
</data>
<!--我们需要展示的布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--绑定基本数据类型及String的使用是通过 @{数据类型的对象} 通过对应数据类型的控制显示-->
<Button
android:id="@+id/main_tv2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="@{enabled}"
android:text="@{content}" />
</LinearLayout>
</layout>
Activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通过DataBInding加载布局都会对应的生成一个对象,如ActivityMainBinding,对象名在布局文件名称后加上了一个后缀Binding
ActivityMainBinding binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main);
//2.绑定基本数据类型及String
binding.setContent("对String类型数据的绑定");//setContent就是给布局文件text属性绑定的content设置值
}
绑定自定义实体类数据类型(简单使用):
model类:
public class User {
private String text;
public User(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<!--绑定Model数据2中形式,一中是导入该类型的,一种指定类型的全称,和java一样-->
<!-- 方式一 -->
<variable
name="user"
type="User类的全包名路径" />
<!-- 方式二 -->
<!--<import type="User类的全包名路径" />-->
<!--<variable-->
<!--name="user"-->
<!--type="User" />-->
</data>
<!--我们需要展示的布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/main_btn3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.text}" />
<!--这里user.text相当于user.getText()-->
</LinearLayout>
</layout>
Activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通过DataBInding加载布局都会对应的生成一个对象,如ActivityMainBinding,对象名在布局文件名称后加上了一个后缀Binding
ActivityMainBinding binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main);
//3.绑定model对象数据
User user = new User("绑定Model数据类型");
binding.setUser(user);//或者 binding.setVariable(BR.user,user);
}
事件绑定:
EventListener接口类(可以自定义自己的点击事件,包括其参数或名称):
public interface OnClickListener{
void clickText(String s);
void clickView(View s);
}
布局文件:
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<variable
name="event"
type="自定义EventListener全包名路径" />
<variable
name="title1"
type="String" />
<variable
name="title3"
type="String" />
<variable
name="title4"
type="String" />
</data>
<!--我们需要展示的布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--通过onclick绑定到上面所声明的event中的具体接口-->
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{event.clickView}"
android:text="@{title1}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{()->event.clickText(title4)}"
android:text="@{title3}" />
</LinearLayout>
</layout>
Activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通过DataBInding加载布局都会对应的生成一个对象,如ActivityMainBinding,对象名在布局文件名称后加上了一个后缀Binding
binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main);
binding.setTitle1("事件绑定1");
binding.setTitle2("事件绑定2");
binding.setTitle3("事件绑定3");
binding.setTitle4("change ok");
//点击时间可以直接通过xml中声明的name来设置
binding.setEvent(new OnClickListener() {
@Override
public void clickView(View v) {
binding.setTitle1("事件1方法调用");
}
@Override
public void clickText(String s) {
binding.setTitle3(s);
}
});
}
直接调用静态方法:
布局文件:
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<!--调用静态方法,需要先导入需要的包 当然java中的lang包可以不用导包-->
<import type="静态方法类全包名路径例如(com.example.test.utils.Utils)" />
</data>
<!--我们需要展示的布局-->
<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="www.zhang.com.databinding.MainActivity">
<Button
android:id="@+id/main_btn5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{Utils.getName()}" />
<!--就和java中写代码一样,只要符合数据类型就好,静态方法可以返回具体返回值进行数据刷新-->
</LinearLayout>
</layout>
通过运算操作符操作数据:
布局文件:
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<variable
name="user2"
type="www.zhang.com.databinding.model.User" />
</data>
<!--我们需要展示的布局-->
<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="www.zhang.com.databinding.MainActivity">
<Button
android:id="@+id/main_btn5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@{user2.state ? @dimen/largepadding : (int)@dimen/smallpadding}"
android:text="@{user2.content ?? @string/app_name}" />
<!-- android:text="@{user2.content ?? @string/app_name}"
等价于
android:text="@{user2.content!=null? user2.content : @string/app_name}"-->
<Button
android:id="@+id/main_btn6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{`Hello World`+ @string/app_name}" /><!-- ``字符包裹的表示字符串,可用作拼接字符串 -->
</LinearLayout>
</layout>
注:
DataBinding支持的表达式有:
数学表达式: + - / * %
字符串拼接 +
逻辑表达式 && ||
位操作符 & | ^
一元操作符 + - ! ~
位移操作符 >> >>> <<
比较操作符 == > < >= <=
instanceof
分组操作符 ()
字面量 - character, String, numeric, null
强转、方法调用
字段访问
数组访问 []
三元操作符 ?:
为null判断 ??
部分逻辑符号是需要转移的,不然运行会报错的:
&(逻辑与) &
<(小于) <
>(大于) >
"(双引号) "
'(单引号) '
自定义Binding的类名 :
data标签有个class属性,它可以用来对Binding对象重新命名(一般是以布局文件名加上Binding后缀作为该Binding类名) 文件目录如下
###自定义类名有3中方式
-
通过指定全类名的方式
<data class="www.zhang.com.databinding.activity.Item"> ... </data>
import www.zhang.com.databinding.activity.Item; Item binding = DataBindingUtil.setContentView(FiveActivity.this, R.layout.activity_five);
-
直接生成在项目的包目录下()
<data class=".Item"> ... </data>
import www.zhang.com.databinding.Item; Item binding = DataBindingUtil.setContentView(FiveActivity.this, R.layout.activity_five);
绑定相同model的操作 :
绑定相同model我的理解有2种,一种是同类的2个对象,另一种是不同类的2个类,但类名相同,具体看代码:
第一种,同一个类的2个对象,只需对象名不同就可以 布局文件 :
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<import type="User全包名路径" />
<variable
name="user3"
type="User" />
<variable
name="user4"
type="User" />
</data>
<!--我们需要展示的布局-->
<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="www.zhang.com.databinding.MainActivity">
<Button
android:id="@+id/main_btn7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user3.text}" />
<Button
android:id="@+id/main_btn8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user4.text}" />
</LinearLayout>
</layout>
Activity:
User user3 = new User("相同类,不同对象1");
binding.setUser3(user3);
User user4 = new User("相同类,不同对象2");
binding.setUser4(user4);
第二种不同的2个类,对象名相同 :
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<import type="User全路径包名" />
<variable
name="user4"
type="User" />
<!--因为type="User"都为User类,会导致不知道到那个包,所以可以通过alias属性重命名type的类型,但实际上alias被指定的那个类型(如:com.databinding.model.User) 换而言之,alias可以重命名实体类type-->
<import type="另一个同名User的全路径包名" alias="Model"/>
<variable
name="user5"
type="Model" />
</data>
<!--我们需要展示的布局-->
<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="www.zhang.com.databinding.MainActivity">
<Button
android:id="@+id/main_btn8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user4.text}" />
<Button
android:id="@+id/main_btn9"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user5.content}" />
</LinearLayout>
</layout>
Activity:
User user4 = new User("相同类,不同对象2");
binding.setUser4(user4);
com.databinding.model.User user5 = new com.databinding.model.User("类名相同,但不是相同的2个类",false);
binding.setUser5(user5);
自定义实体类变量改变自动更新数据
public class Person extends BaseObservable {
private String firstName;
private String lastName;
private int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
@Bindable
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
@Bindable
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
@Bindable
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(BR.age);
}
}
//。DataBinding实现类依然负责通知当属性改变时。这是通过指定一个Bindable注解给getter以及setter内通知来完成的notifyPropertyChanged(BR.参数名)通知更新这一个参数,需要与@Bindable注解配合使用。notifyChange()通知更新所有参数,可以不用和@Bindable注解配合使用
绑定List/Map集合:
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<!--注意(<>)等特殊字符需要使用转义避免报错-->
<import type="java.util.ArrayList" />
<import type="java.lang.String" />
<variable
name="list"
type="ArrayList<String>" />
<import type="java.util.Map" />
<variable
name="map"
type="Map<String,String>" />
<variable
name="arrays"
type="String[]" />
</data>
<!--我们需要展示的布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{list[0]}" />
<!--List集合既可以和数组一样通过索引获取值list[index]方式,也可以调用API-->
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{list.get(1)}" />
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{map[`name`]}" />
<!--Map集合既可以通过map[key]的方式,也可以通过调用API-->
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{map.get(`age`)}" />
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{arrays[0]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{arrays[1]}" />
</LinearLayout>
</layout>
Activity:
ArrayList<String> list = new ArrayList<>();
list.add("first");
list.add("second");
binding.setList(list);
Map<String, String> map = new HashMap<>();
map.put("name", "zhangsan");
map.put("age", "40");
binding.setMap(map);
String[] arrays = {"lisi", "laowang"};
binding.setArrays(arrays);
Map,List等实现数据实体类改变自动更新:
Obervable是一个接口,它的子类有BaseObservable,其主要作用是封装数据类型改变自动更新UI的作用
ObservableField,
ObservableBoolean,
ObservableByte,
ObservableChar,
ObservableShort,
ObservableInt,
ObservableLong,
ObservableFloat,
ObservableDouble,
ObservableParcelable,
ObservableArrayList,
ObservableArrayMap
Demo(实体类运用简单的数据类型和ObservableArrayMap,ObservableArrayList的使用):
<?xml version="1.0" encoding="utf-8"?><!--布局以layout作为根布局-->
<layout>
<data>
<import type="www.zhang.com.databinding.model.Animal"/>
<!--该数据类中的变量字段可以使用以上封装好的数据字段标识声明-->
<variable
name="animal"
type="Animal"/>
<variable
name="list"
type="android.databinding.ObservableArrayList<String>"/>
<variable
name="map"
type="android.databinding.ObservableArrayMap<String,String>"/>
</data>
<!--我们需要展示的布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{animal.field}" />
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{String.valueOf(animal.age)}" />
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{list[0]}" />
<!--Map集合既可以通过map[key]的方式,也可以通过调用API-->
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{list[1]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{map[`name`]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@{map[`age`]}" />
<Button
android:id="@+id/four_btn"
android:layout_width="match_parent"
android:text="改变数据"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
Activity:
final Animal animal = new Animal();
animal.field.set("cat");
animal.age.set(2);
binding.setAnimal(animal);
final ObservableArrayList<String> list = new ObservableArrayList<>();
list.add("dog");
list.add("mouse");
binding.setList(list);
final ObservableArrayMap<String, String> map = new ObservableArrayMap<>();
map.put("name","Tom");
map.put("age","4");
binding.setMap(map);
binding.fourBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
animal.field.set("dog");
animal.age.set(4);
list.set(0,"cat");
list.set(1,"dog");
map.put("name","Sam");
map.put("age","5");
//对应数据类型的值发生改变,那么UI会自动更新。省去更新UI的操作
}
});
DataBinding 与RecyclerView的使用:
layout_item:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="name"
type="String" />
</data>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true">
<TextView
android:id="@+id/tv_item"
android:layout_width="match_parent"
android:layout_height="100px"
android:gravity="center"
android:text="@{name ?? @string/app_name}"
android:textSize="25sp"
tools:ignore="MissingConstraints" />
</android.support.constraint.ConstraintLayout>
</layout>
Adapter:
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.TestViewHodler> {
private List<String> datas;
private Context mContext;
public TestAdapter(Context context, List<String> infos) {
this.mContext = context;
this.datas = infos;
}
@NonNull
@Override
public TestViewHodler onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
ItemRecyclerviewBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.item_recyclerview, viewGroup, false);
return new TestViewHodler(binding);
}
@Override
public void onBindViewHolder(@NonNull TestViewHodler testViewHodler, int i) {
testViewHodler.binding.setName(datas.get(i));
}
@Override
public int getItemCount() {
return datas.size();
}
public static class TestViewHodler extends RecyclerView.ViewHolder {
private ItemRecyclerviewBinding binding;
TestViewHodler(ItemRecyclerviewBinding itemView) {
super(itemView.getRoot());
this.binding = itemView;
}
}
}
Activity:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Activity的layout就不放出来了,里面就是一个recyclerView
mDataBindingUtil = DataBindingUtil.setContentView(this, R.layout.activity_second);
initRecyclerView();
}
private void initRecyclerView() {
for (int i = 0; i <= 10; i++) {
data.add("My Name is : " + i);
}
mTestAdapter = new TestAdapter(this, data);
mDataBindingUtil.recyclerView.setLayoutManager(new LinearLayoutManager(this));
mDataBindingUtil.recyclerView.setFocusable(true);
mDataBindingUtil.recyclerView.setAdapter(mTestAdapter);
}
DataBinding 与 fragment使用:
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ViewDataBinding itemBinding = DataBindingUtil.inflate(inflater, R.layout.item_recyclerview, container, false);
View view = itemBinding.getRoot();
return view;
}
写在最后的话:
文章只是总结了部分DataBinding简单的使用方法,而且是2015 Google IO推出的库,到目前为止也很久了,近年google也推出了很多关于数据周期及UI生命周期的官方库,目前通过Android开发交流群,了解到了外面基本上专注手机App的公司基本已经大面积的在使用DataBinding了,极少数的开始切换viewModel及liveData了。在平常基础库及练手测试demo里面也可以先掌握一下基础使用方法了。
在空闲时间各位有学习到什么新技术或有什么高效解决某类问题的途径都可以进行分享归档。