您的位置:首页 > 其它

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: