Android之Databinding学习笔记
来源:互联网 发布:遗传算法简单实例 编辑:程序博客网 时间:2024/05/18 03:50
Android之DataBinding学习笔记
简介
Data binding 在2015年7月发布的Android Studio v1.3.0 版本上引入,在2016年4月Android Studio v2.0.0 上正式支持。目前为止,Data Binding 已经支持双向绑定了。
Databinding 是一个实现数据和UI绑定的框架,是一个实现 MVVM 模式的工具,有了 Data Binding,在Android中也可以很方便的实现MVVM开发模式。
Data Binding 是一个support库,最低支持到Android 2.1(API Level 7+)。
Data Binding 之前,我们不可避免地要编写大量的重复的代码,如 findViewById()、setText(),setVisibility(),setEnabled() 或 setOnClickListener() 等,通过 Data Binding , 我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局,这样就不用编写大量的重复的代码了。
studio环境构建
1.在模块的build.gradle文件中添加dataBinding配置
android { ...... dataBinding{ enabled = true }......}
注意:如果app依赖了一个使用 Data Binding 的库,那么app module 的 build.gradle 也必须配置 Data Binding
Data Binding 布局文件 - (View)
Data binding 的布局文件与传统布局文件有一点不同。它以一个 layout 标签作为根节点,里面是 data 标签与 view 标签。view 标签的内容就是不使用 Data Binding 时的普通布局文件内容。以下是一个例子:
activity_main.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> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cn.itrealman.databindingdemo.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp" android:text="@{entry.text}" android:textColor="@{entry.color}"/> </RelativeLayout></layout>
在上面的布局中,对于data里面的variable nama=”entry”表示的是一个变量名entry,type表示你所对应实体的包名路径,在TextView中,我们采用@{}的语法来引用实体类Entry中的属性。
数据对象 - (Model)
Entry.java
public class Entry { private String text; private int color; public String getText() { return text; } public void setText(String text) { this.text = text; } public int getColor() { return color; } public void setColor(int color) { this.color = color; }}
上面就是我们所说的实体类Entry,用于TextView的android:text
属性的表达式@{entry.text}
和android:textColor
属性表达式@{entry.color}
绑定数据 - (ViewModel)
在默认情况下,会基于布局文件生成一个继承于 ViewDataBinding 的 Binding 类,将它转换成和你的布局命名并在名字后面接上Binding。例如,布局文件叫 activity_main.xml,所以会生成一个 ActivityMainBinding类。这个类包含了布局文件中所有的绑定关系,会根据绑定表达式给布局文件赋值。在 inflate 的时候创建 binding 的方法如下:
MainActivity.java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main); Entry entry = new Entry(); entry.setText("文本数据1"); entry.setColor(0xff0000ff); binding.setEntry(entry); }}
事件处理
类似于 android:onClick 可以指定 Activity 中的函数,Data Binding 也允许处理从视图中发送的事件。
有两种实现方式:
方法调用
监听绑定
二者主要区别在于方法调用在编译时处理,而监听绑定于事件发生时处理。
方法调用
相较于 android:onClick ,它的优势在于表达式会在编译时处理,如果函数不存在或者函数签名不对,编译将会报错。
以下是个例子:
Entry.java
public class Entry { private String text; private int color; public String getText() { return text; } public void setText(String text) { this.text = text; } public int getColor() { return color; } public void setColor(int color) { this.color = color; } //在这个实体中添加一个点击时间的方法 public void onClick(View view){ Toast.makeText(view.getContext(),"已点击",Toast.LENGTH_SHORT).show(); }}
activity_main.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> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cn.itrealman.databindingdemo.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp" android:text="@{entry.text}" android:textColor="@{entry.color}" android:onClick="@{entry::onClick}"/> </RelativeLayout></layout>
通过在上面的TextView中添加一个android:onClick
属性表达式为@{entry.onClick}
这样就能调用点击事件 了,(注意:对应的方法名和监听器对象必须对应) 如果该方法不存在,则在编译的时候就不会通过了。除了上面的表达式可以表示之外,我们还可以使用以下的表达式方式进行设置:
android:onClick="@{entry.onClick}"
(不过这种方式已经过时了,因为这样的表示会和entry.text这样的属性引用难以区分)
监听绑定
监听绑定在事件发生时调用,可以使用任意表达式
此功能在 Android Gradle Plugin version 2.0 或更新版本上可用.
在方法引用中,方法的参数必须与监听器对象的参数相匹配。在监听绑定中,只要返回值与监听器对象的预期返回值相匹配即可。
Entry.java
public class Entry { private String text; private int color; public String getText() { return text; } public void setText(String text) { this.text = text; } public int getColor() { return color; } public void setColor(int color) { this.color = color; } //将点击监听事件中添加了一个参数 public void onClick(View view,String str){ Toast.makeText(view.getContext(),"已点击,产生了" + str,Toast.LENGTH_SHORT).show(); }}
activity_main.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> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> <variable name="str" type="String"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cn.itrealman.databindingdemo.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp" android:text="@{entry.text}" android:textColor="@{entry.color}" android:onClick="@{(v) -> entry.onClick(v,str)}"/> </RelativeLayout></layout>
MainAcitivty.java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main,new MyComponent(this)); Entry entry = new Entry(); entry.setText("文本数据1"); entry.setColor(0xff0000ff); //设置测试字符串 binding.setStr("我是监听绑定的数据测试"); binding.setEntry(entry); }}
当一个回调函数在表达式中使用时,数据绑定会自动为事件创建必要的监听器并注册监听。关于android:onClick="@{(v) -> entry.onClick(v,str)}"
这里采用的是java1.8中的lambda表达式的形式,因为这样我们可以传递相应的参数进去。
导入(Imports)
1.data 标签内可以有多个 import 标签。你可以在布局文件中像使用 Java 一样导入引用
activity_main.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="android.view.View"/> <variable name="show" type="boolean"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cn.itrealman.databindingdemo.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="@{show ? View.VISIBLE : View.GONE}" android:textSize="20sp"/> </RelativeLayout></layout>
2.当类名发生冲突时,可以使用 alias
<import type="android.view.View"/><import type="cn.itrealman.databindingdemo.utils.View" alias="UtilsView"/>
3.导入的类型也可以用于变量的类型引用和表达式中
<data> <import type="cn.itrealman.databindingdemo.Entry"/> <import type="java.util.List"/> <variable name="entry" type="Entry"/> <variable name="entryList" type="List<Entry>"/></data>
注意:Android Studio 还没有对导入提供自动补全的支持。你的应用还是可以被正常编译,要解决这个问题,你可以在变量定义中使用完整的包名。
4.导入也可以用于在表达式中使用静态方法
StringUtils.java
public class StringUtils { public static String show(String string){ string = string.toUpperCase(); return string; }}
activity_main.xml
<data> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> <import type="cn.itrealman.databindingdemo.utils.StringUtils"/> </data> ...... <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp" android:text="@{StringUtils.show(entry.text)}" android:textColor="@{entry.color}"/></layout>
5.java.lang.* 包中的类会被自动导入,可以直接使用,例如, 要定义一个 String 类型的变量
<variable name="str" type="String" />
变量 Variables
1.data 标签中可以有任意数量的 variable 标签。每个 variable 标签描述了会在 binding 表达式中使用的属性。
<data> <import type="android.graphics.drawable.Drawable"/> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> <variable name="image" type="Drawable"/> <variable name="str" type="String"/></data>
2.可以在表达式中直接引用带 id 的 view,引用时采用驼峰命名法。
<TextView android:id="@+id/m_entry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@={entry.text}" /><TextView android:text="@{user.entry}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{mEntry.getVisibility() == View.GONE ? View.GONE : View.VISIBLE}" /> <!-- 这里TextView直接引用第一个TextView,mEntry为m_entry id的驼峰命名,默认会去掉下划线 -->
3.binding 类会生成一个命名为 context 的特殊变量(其实就是 rootView 的 getContext() ) 的返回值),这个变量可用于表达式中。 如果有名为 context 的变量存在,那么生成的这个 context 特殊变量将被覆盖。
StringUtils.java
public class StringUtils { public static String show(Context context,String string){ Toast.makeText(context, string,Toast.LENGTH_SHORT).show(); string = string.toUpperCase(); return string; }}
<data> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> <import type="cn.itrealman.databindingdemo.utils.StringUtils"/></data>......<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp" android:text="@{StringUtils.show(context,entry.text)}" android:textColor="@{entry.color}"/>
自定义绑定类名
默认情况下,binding 类的名称取决于布局文件的命名,以大写字母开头,移除下划线,后续字母大写并追加 “Binding” 结尾。这个类会被放置在 databinding 包中。举个例子,布局文件 activity_main.xml 会生成 ActivityMainBinding 类。如果 module 包名为 com.example.my.app ,binding 类会被放在 com.example.my.app.databinding 中。
通过修改 data 标签中的 class 属性,可以修改 Binding 类的命名与位置。举个例子:
<data class="CustomBinding"> ...</data>
以上会在 databinding 包中生成名为 CustomBinding 的 binding 类。如果需要放置在不同的包下,可以在前面加 “.”:
<data class=".CustomBinding"> ...</data>
这样的话, CustomBinding 会直接生成在 module 包下。如果提供完整的包名,binding 类可以放置在任何包名中:
<data class="com.example.CustomBinding"> ...</data>
Includes
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> </data> <RelativeLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/include" app:entry="@{entry}"/> </RelativeLayout></layout>
include.xml
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> </data> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{entry.text}" android:textColor="@{entry.color}"> </TextView></layout>
需要注意, activity_main.xml 与 include.xml 中都需要声明 user 变量。
Data binding 不支持直接包含 merge 节点。举个例子, 以下的代码不能正常运行 :
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="entry" type="cn.itrealman.databindingdemo.Entry"/> </data> <merge> <include layout="@layout/include" app:entry="@{entry}"/> </merge></layout>
表达式语言
表达式语言与 Java 表达式有很多相似之处。下面是相同之处:
- 数学计算 + - / * %
- 字符串连接 +
- 逻辑 && ||
- 二进制 & | ^
- 一元 + - ! ~
- 位移 >> >>> <<
- 比较 == > < >= <=
- instanceof
- 组 ()
- 字面量 - 字符,字符串,数字, null
- 类型转换
- 函数调用
- 字段存取
- 数组存取 []
- 三元运算符 ?:
<!-- 内部使用字符串 & 字符拼接--><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{`num:` + String.valueOf(entry.num)}"/><!-- 三目运算--><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{show ? View.VISIBLE : View.GONE}"/>
在xml中转义是不可避免的,如 : 使用“&&”是编译不通过的,需要使用转义字符 “&&”
附:常用的转义字符
不支持的操作符
一些 Java 中的操作符在表达式语法中不能使用。
- this
- super
- new
- 显示泛型调用<T>
Null合并运算符
Null合并运算符 ?? 会在非 null 的时候选择左边的操作,反之选择右边。
android:text="@{entry.text ?? `Default Text`}"
等同于
android:text="@{entry.text != null ? entry.text : `Default Text`}"
容器类
通用的容器类:数组,List ,SparseArray ,和 Map,可以用 [] 操作符来存取
<data> <import type="android.util.SparseArray"/> <import type="java.util.Map"/> <import type="java.util.List"/> <variable name="list" type="List<String>"/> <variable name="sparse" type="SparseArray<String>"/> <variable name="map" type="Map<String, String>"/> <variable name="index" type="int"/> <variable name="key" type="String"/></data>…android:text="@{list[index]}"…android:text="@{sparse[index]}"…android:text="@{map[key]}"
字符串常量
使用单引号把属性包起来,就可以很简单地在表达式中使用双引号:
android:text='@{map["text"]}'
也可以用双引号将属性包起来。这样的话,字符串常量就可以用 ” 或者反引号 ( ` ) 来调用
android:text="@{map[`text`}"android:text="@{map["text"]}"
资源
也可以在表达式中使用普通的语法来引用资源:
android:text="@{@string/text(entry.text)"
字符串格式化和复数形式可以这样实现:
android:text="@{@plurals/sample_plurals(num)}"
当复数形式有多个参数时,应该这样写:
android:text="@{@plurals/numbers(num, num)}"
数据对象 (Data Objects)
任何 POJO 对象都能用在 Data Binding 中,但是更改 POJO 并不会同步更新 UI。Data Binding 的强大之处就在于它可以让你的数据拥有更新通知的能力。
Observable 对象
Observable 字段
Observable 容器类
当以上的 observable 对象绑定在 UI 上,数据发生变化时,UI 就会同步更新。
Observable 对象
Observable 接口有一个添加/移除 listener 的机制,但通知取决于开发者。为了简化开发,Android 原生提供了一个基类 BaseObservable 来实现 listener 注册机制。这个类也实现了字段变动的通知,只需要在 getter 上使用 Bindable 注解,并在 setter 中通知更新即可。
public class Entry extends BaseObservable{ private String text; private int color; @Bindable public String getText() { return text; } public void setText(String text) { this.text = text; notifyPropertyChanged(BR.text); } @Bindable public int getColor() { return color; } public void setColor(int color) { this.color = color; notifyPropertyChanged(BR.color); } public void onClick(View view,String str){ Toast.makeText(view.getContext(),"已点击,产生了" + str,Toast.LENGTH_SHORT).show(); setText(str); setColor(0xffff0000); }}
BR
是编译阶段生成的一个类,功能与 R.java 类似,用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry。
当点击 事件使数据发生变化时需要调用 notifyPropertyChanged(BR.text)
通知系统 BR.text
这个 entry 的数据已经发生变化以更新UI。
ObservableFields
创建 Observable 类还是需要花费一点时间的,如果想要省时,或者数据类的字段很少的话,可以使用 ObservableField 以及它的派生 ObservableBoolean、ObservableByte 、ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble、
ObservableParcelable 。
ObservableFields 是包含 observable 对象的单一字段。原始版本避免了在存取过程中做打包/解包操作。要使用它,在数据类中创建一个 public final 字段:
public class EntryField{ public ObservableField<String> mText = new ObservableField<>(); public ObservableField<Integer> mColor = new ObservableField<>(); public EntryField(String text, int color) { mText.set(text); mColor.set(color); }}
要存取数据,只需要使用 get() / set() 方法:
mEntryField.mText.set("text");mEntryField.mColor.set(0xffff0000);String text = mEntryField.mText.get();String color = mEntryField.mColor.get();
Observable Collections 容器类
一些应用会使用更加灵活的结构来保持数据。Observable 容器类允许使用 key 来获取这类数据。当 key 是类似 String 的一类引用类型时,使用 ObservableArrayMap 会非常方便。
ObservableArrayMap<String, Object> mEntry = new ObservableArrayMap<>();mEntry .put("text", "it_real_man");mEntry .put("color", 0xffff0000);binding.setEntry(mEntry);
在布局中,可以用 String key 来获取 map 中的数据:
<data> <import type="android.databinding.ObservableMap"/> <variable name="entry" type="ObservableMap<String, String>"/></data>…<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text='@{entry["text"]}'/><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor='@{entry["color"]}'/>
当 key 是整数类型时,可以使用 ObservableArrayList :
ObservableArrayList<String> sEntry= new ObservableArrayList<>();sEntry.add("entryData");sEntry.add("text");sEntry.add("color");binding.setSEntry(sEntry);
在布局文件中,使用下标获取列表数据:
<data> <import type = "android.databinding.ObservableList"/> <variable name="sEntry" type="ObservableList<String>"/></data>…<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text='@{sEntry[0]}'/><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text='@{sEntry[1]}'/><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text='@{sEntry[2]}'/>
- Android之Databinding学习笔记
- Android学习笔记之--------DataBinding使用一
- Android DataBinding 学习笔记
- Android学习笔记之MVVM----DataBinding(数据双向绑定)
- Android 官方框架DataBinding学习笔记
- Android 官方示例:android-architecture 学习笔记(四)之todo-databinding
- 学习笔记--databinding
- DataBinding学习笔记
- Android databinding笔记
- Android DataBinding 入门笔记
- android databinding实践之:databinding 注解
- 2016android新技术之DataBinding
- DataBinding学习笔记(一)源码分析
- Kotlin学习笔记——使用databinding
- [ExtJS5学习笔记]第十节 Extjs5新增特性之ViewModel和DataBinding
- DataBinding学习使用进阶之路
- 学习Android Architecture Blueprints(四)-databinding分支
- Android之DataBinding初体验(一)
- iOS中常见的一些宏
- HG850开局说明与GPON工作原理
- Android新技术
- 字符流中第一个不重复的字符
- HTML5学习_day01(2)--HBuilder工具tab快捷键使用
- Android之Databinding学习笔记
- Fragment的使用
- Android开发之详解五大布局
- 多渠道打包
- Qt用QML调用Android的摄像头简单示例
- gcc基础学习
- 使用WKWebView替换UIWebView
- android基础系列:MVP架构
- iOS--Umeng的使用攻略