Data Binding 的使用之一:简单的数据绑定
2017-02-06 17:00
351 查看
一、简介
Data Binding是google发布的用以实现数据和UI绑定的框架,使用此框架可方便的实现MVVM开发模式。借用阮一峰老师对MVVM模式的概括:“MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。”使用Data Binding的优点:
1. 避免了反复使用findViewById函数查找view,直接将view作为ViewDataBinding的成员变量。
2. 数据采用双向绑定,借助android.databinding.BaseObservable类,实现数据与视图两者的同步问题
3. 提供了多种表达式,可在xml布局文件中直接使用表达式完成数据处理
4. 为view提供多种callback,并提供lambda表达式功能,实现view的事件响应
二、使用步骤
0.开发环境IDE:
Android Studio
1.在module的
build.gradle文件中,打开
dataBinding
android{ ... dataBinding{ enabled = true } }
由于
build.gradle更改后,
Android Studio会在右上角自动提示是否
sync now,点击
sync now。
2.
Data Binding将
view与
model进行解耦,避免在
controller中或者在
presenter中进行
view与数据的处理。使用时,在界面对应的
layout文件中,添加标签
<layout> ... </layout>
使原来的布局放置在两个
layout标签中,即
layout标签为根标签。注意
layout为小写。并将命名空间
xmlns移到
layout标签下,即:
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
或者
<layout xmlns:android="http://schemas.android.com/apk/res/android">
3.当添加完
layout标签后,
Android Studio就会自动根据
layout布局文件的名称,生成对应的java文件。
如,在
activity_main.xml文件中添加
layout根标签后,会生成
ActivityMainBinding.java,此类继承自
android.databinding.ViewDataBinding。
由此可见,动态生成的
ViewDataBinding类的命名规则是:
根据相应的
xml文件名称,去掉下划线,采用驼峰法,首字母大写,最后添加
Binding后缀,
activity_main.xml对应的
ViewDataBinding类为
ActivityMainBinding.java。
4.在java文件中使用
Data Binding时,去掉之前的
context.setContentView,采用
DataBindingUtil的函数:
public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId)
针对此例,在
activity的
onCreate函数中使用如下代码替换
setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding对象根据对应的
layout文件中的内容,实时动态的改变。如当为某个
view添加
id后,
binding对象会自动根据新添加的id生成对应的成员变量,此例中,我为一个
TextView添加了
android:id="@+id/title_text_view",
binding对象中自动添加了
titleTextView成员变量,这样也就省去了
findViewById的工作。同时我们可以看到,
binding对象的命名规则是将布局文件中
id的命名去掉下划线,转换为驼峰命名。如果文件中id的命名中没有下划线,则不会进行改变。
此时文件为:
package cn.carbs.android.testdatabinding; import android.databinding.DataBindingUtil; import android.databinding.ViewDataBinding; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import cn.carbs.android.testdatabinding.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.titleTextView.setText("hello data binding"); } }
MainActivity对应的布局文件为:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <RelativeLayout android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="cn.carbs.android.testdatabinding.MainActivity"> <TextView android:id="@+id/title_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> </RelativeLayout> </layout>
运行后,
activity中的
TextView显示
"hello data binding"
到此为止我们采用了在java代码中使用
binding.viewName.setXxx()的方式对
view进行数据填充绑定。
下面使用另一种方式:在
layout文件中添加
data标签进行数据绑定:
5.接着上面的步骤,在布局文件中,
layout标签下添加
data标签,并将
data标签做为
layout标签中的首个子标签
<data> <variable name="aStudent" type="cn.carbs.android.testdatabinding.Student"/> </data>
name是变量的名称,
type是变量的类型,相当于
cn.carbs.android.testdatabinding.Student aStudent;语句。(
Student是一个
java bean)
在相应的
View中采用如下方式绑定数据:
<TextView android:id="@+id/title_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{aStudent.name}" />
在对应的java文件中,相应的
ViewDataBinding对象就会根据变量名称(
data标签中
variable标签下的
name指定的变量名称),生成
set方法,此例中,可通过如下方式,在
java类中绑定数据
Student s = new Student(); s.name = "Rick"; ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setAStudent(s);
即java类中负责设置
bean,
xml布局文件中,负责将
bean的信息分别设置到对应的
view上。
另外,
android:text中的文字可以添加表达式,如下所示:
android:text='@{String.valueOf(1) + "_prefix_" + aStudent.name}'
可进行字符串连接,
String的转换等。如果需要使用到双引号,即添加指定的字符串,那么将外层的双引号改为单引号即可
6.在
xml布局文件中绑定事件:
android:onClick
android:onLongClick
android:onTextChanged等,其中
onClick是
Android中本身带有的属性,而
android:onLongClick
android:onTextChanged则是
Data Binding添加的。
首先在声明响应
onClick等事件的类,如:
public class Presenter{ public void onClick(View v){ //do something... } }
其次在
xml文件中,添加此类对应的对象
<variable name="aPresenter" type="cn.carbs.android.testdatabinding.MainActivity.Presenter" />
并在
TextView中添加对应的
onclick说明
android:onClick="@{aPresenter.onClick}"
三、原理说明
注意到,在编译运行后,在app\build\generated\source\apt\debug\your_package_name\databinding文件夹中找到
ActivityMainBinding.java文件,如下所示:
package cn.carbs.android.testdatabinding.databinding; import cn.carbs.android.testdatabinding.R; import cn.carbs.android.testdatabinding.BR; import android.view.View; public class ActivityMainBinding extends android.databinding.ViewDataBinding { private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes; private static final android.util.SparseIntArray sViewsWithIds; static { sIncludes = null; sViewsWithIds = new android.util.SparseIntArray(); sViewsWithIds.put(R.id.title_text_view, 1); } // views public final android.widget.RelativeLayout activityMain; public final android.widget.TextView titleTextView; // variables // values // listeners // Inverse Binding Event Handlers public ActivityMainBinding(android.databinding.DataBindingComponent bindingComponent, View root) { super(bindingComponent, root, 0); final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds); this.activityMain = (android.widget.RelativeLayout) bindings[0]; this.activityMain.setTag(null); this.titleTextView = (android.widget.TextView) bindings[1]; setRootTag(root); // listeners invalidateAll(); } @Override public void invalidateAll() { synchronized(this) { mDirtyFlags = 0x1L; } requestRebind(); } @Override public boolean hasPendingBindings() { synchronized(this) { if (mDirtyFlags != 0) { return true; } } return false; } public boolean setVariable(int variableId, Object variable) { switch(variableId) { } return false; } @Override protected boolean onFieldChange(int localFieldId, Object object, int fieldId) { switch (localFieldId) { } return false; } @Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } // batch finished } // Listener Stub Implementations // callback impls // dirty flag private long mDirtyFlags = 0xffffffffffffffffL; public static ActivityMainBinding inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) { return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent()); } public static ActivityMainBinding inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent) { return android.databinding.DataBindingUtil.<ActivityMainBinding>inflate(inflater, cn.carbs.android.testdatabinding.R.layout.activity_main, root, attachToRoot, bindingComponent); } public static ActivityMainBinding inflate(android.view.LayoutInflater inflater) { return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent()); } public static ActivityMainBinding inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) { return bind(inflater.inflate(cn.carbs.android.testdatabinding.R.layout.activity_main, null, false), bindingComponent); } public static ActivityMainBinding bind(android.view.View view) { return bind(view, android.databinding.DataBindingUtil.getDefaultComponent()); } public static ActivityMainBinding bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) { if (!"layout/activity_main_0".equals(view.getTag())) { throw new RuntimeException("view tag isn't correct on view:" + view.getTag()); } return new ActivityMainBinding(bindingComponent, view); } /* flag mapping flag 0 (0x1L): null flag mapping end*/ //end }
Data Binding在java编译之前,首先会使用
apt技术,解析相关的
xml等文件,并生成类文件,因此
Data Binding使用的并非反射机制,这样也就避免了使用反射造成的性能损耗。
四、注意事项
1.类似于ButterKnife、
GreenDao3.x等第三方扩展库,
Data Binding会在运行时,根据布局文件通过一定的规则自动生成java文件,如果发现相应的类没有自动生成(
Android Studio中会报错,找不到响应的类文件),可以采用
clean或者
rebuild的方式,让
AndroidStudio主动生成代码。
参考:http://www.imooc.com/learn/719
相关文章推荐
- Android 官方数据绑定框架 Data Binding 简单使用
- 使用objectdatasource结合数据绑定控件进行简单三层架构的开发
- 使用objectdatasource结合数据绑定控件进行简单三层架构的开发
- 【转】详解Data Binding 通过几个简单示例深入了解WinForm数据绑定特性
- Android开发教程 - 使用Data Binding(五)数据绑定
- 使用简单工厂模式和反射机制优化MVP数据绑定流程
- 使用 AJAX,局部刷新 GridView 进行数据绑定的简单实现
- WPF中使用MVVM模式进行简单的数据绑定
- Android中的MVP笔记之二: Data Binding 库的使用之layout绑定数据与事件。
- 数据绑定库Data Binding的使用
- WPF中的数据绑定Data Binding使用小结
- SilverLight中datagrid数据绑定的简单使用
- 使用 AJAX,局部刷新 GridView 进行数据绑定的简单实现
- 使用 AJAX,局部刷新 GridView 进行数据绑定的简单实现@孟宪会
- Android中的MVP笔记之四: Data Binding 库的使用之数据绑定是否是双向的
- 使用objectdatasource结合数据绑定控件进行简单三层架构的开发
- 【译文】详解Data Binding - 通过几个简单示例深入了解WinForm数据绑定特性
- uwp开发:数据绑定——值转换器 的简单使用
- ASP.NET中DropDownList控件的简单使用--DDL绑定数据库表中值,选择DDL中值直接检索数据
- 使用原生js onkeyup+jQuery实现简单的双向数据绑定