第一步 配置
在build.gradle文件
android {
....
dataBinding {
enabled =true
}
}
<?xml version="1.0" encoding="utf-8"?><variable/> 用来声明变量,name变量名字,type类型包名+类名
<layoutxmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variablename="user"type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</layout>
使用:@{user.username},会调用user.getUsername方法或username()方法
<TextViewandroid:text="@{user.username}"
Data Binding布局文件,会生成布局文件名对应的类,例如:
main_activity.xml,生成MainActivityBinding类,如果<data/>没有子标签,不会生成
在onCreate方法中,不要调用setContentView,用下面代码替代
MainActivityBinding binding =DataBindingUtil.setContentView(this, R.layout.main_activity);可以这样
User user =newUser("Test","User");
binding.setUser(user);
MainActivityBinding binding =MainActivityBinding.inflate(getLayoutInflater());如果布局文件,包括ListView,RecyclerView
ListItemBinding binding =ListItemBinding.inflate(layoutInflater, viewGroup,false);
//or
ListItemBinding binding =DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup,false);
和绑定数据差不多
publicclass ButtonHandler{
publicvoid click(View view){...}
}
<variablename="handler"type="com.example.Handler"/>
<Button android:onClick="@{handlers.click}"/>
特殊点击事件
Class | Listener Setter | Attribute |
---|---|---|
SearchView | setOnSearchClickListener(View.OnClickListener) | android:onSearchClick |
ZoomControls | setOnZoomInClickListener(View.OnClickListener) | android:onZoomIn |
ZoomControls | setOnZoomOutClickListener(View.OnClickListener) | android:onZoomOut |
Imports:引入类,像Java一样引入类。
<data>
<importtype="android.view.View"/>
</data>
<TextViewandroid:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>如果引入多个相同类名,可以使用alias指定别名
<importtype="com.example.real.estate.View"alias="Vista"/>集合,后面User表示泛型
<variablename="userList"type="List<User>"/>
引入静态field或方法
java.lang.*
里的类自动引入
Variables
有个默认的context变量,被binding表达式使用。root View的getContext()。如果声明了context变量,覆盖功能就不起作用。
自定义生成Binding Class名字
<data
class
=
"ContactItem"
>
</data>
<data class=".ContactItem"> </data>
<data class="com.demo.ContactItem"> </data>
Includes
指定命名空间
xmlns:bind
=
"http://schemas.android.com/apk/res-auto"
,name.xml必须有个
user
变量
<LinearLayout>
<includelayout="@layout/name"bind:user="@{user}"/>
</LinearLayout>
不支持
<merge>
<includelayout="@layout/name"
bind:user="@{user}"/>
<includelayout="@layout/contact"
bind:user="@{user}"/>
</merge>
有很多像Java expression
- Mathematical
+ - / * %
- String concatenation
+
- Logical
&& ||
- Binary
& | ^
- Unary
+ - ! ~
- Shift
>> >>> <<
- Comparison
== > < >= <=
instanceof
- Grouping
()
- Literals - character, String, numeric,
null
- Cast
- Method calls
- Field access
- Array access
[]
- Ternary operator
?:
没有的操作
this
super
new
- Explicit generic invocation
Null Coalescing Operator (??)
空操作,左边表达式不空时,使用左边表达式值,否则使用右边表达式值
android:text="@{user.displayName ?? user.lastName}"
避免空指针错误
@{user.name} 如果user为空,那么user.name为null,基本类型是默认值
集合,array,list,map可以使用
[]
操作符访问
android:text="@{list[index]}"
android:text="@{map[key]}"
字符串,可以使用单双引号交替,就像前端那种写法
android:text='@{map["firstName"]}'Resources
android:text="@{@string/nameFormat(firstName, lastName)}"后面firstName,lastName是参数,等于调用getResources.getString(R.string.nameFormat, firstName, lastName);
Type | Normal Reference | Expression Reference |
---|---|---|
String[] | @array | @stringArray |
int[] | @array | @intArray |
TypedArray | @array | @typedArray |
Animator | @animator | @animator |
StateListAnimator | @animator | @stateListAnimator |
color int | @color | @color |
ColorStateList | @color | @colorStateList |
Data Binding 使用的POJO对象,POJO对象改变,不会通知UI更新的。Data Binding提供三种数据改变通知机制:
Observalbe Objects
Observable 接口有增加和移除listener方法,通过变换由开发者调用,可以继承它的子类BaseObservable.
在getter方法上添加
@Bindable注解,该注解在编译时生成BR类,在module package默认根包下
在setter通知数据变化
notifyPropertyChanged
(
BR
.
firstName
);
ObservableFields
还有
ObservableBoolean
, ObservableByte
, ObservableChar
, ObservableShort
, ObservableInt
, ObservableLong
, ObservableFloat
,ObservableDouble
, and ObservableParcelable
publicfinalObservableField<String> sex =newObservableField<>();使用:
<TextView android:text="@{user.sex}"/>
user.sex.set("男");Observable Collections
int age = user.age.get();
把原来使用的集合类,换成下面这种
ObservableArrayMap<String,Object> user =newObservableArrayMap<>();
ObservableArrayList<String> lists = new ObservableArrayList<>();
创建
MyLayoutBinding binding =MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding =MyLayoutBinding.inflate(layoutInflater, viewGroup,false);
MyLayoutBinding binding =MyLayoutBinding.bind(viewRoot);
ViewDataBinding binding =DataBindingUtil.inflate(LayoutInflater, layoutId,根据Id生成View对象
parent, attachToParent);
ViewDataBinding binding =DataBindingUtil.bindTo(viewRoot, layoutId);
这机制比findViewById更快
<TextView
android:id="@+id/username"/>
<TextView
android:id="@+id/userAge"/>
变量
每个变量都提供访问方法
ViewStubs
当viewstub inflate布局时,binding需要为新的布局重新创建,
ViewStubProxy监听
ViewStub
's
ViewStub.OnInflateListener并及时创建binding,ViewStubProxy允许开发者设置OnInflateListener
,并创建binding
Advanced Binding
binding class需要动态生成,例如RecyclerView.Adapter,在Holder提供getBinding方法返回Binding对象
publicvoid onBindViewHolder(BindingHolder holder,int position){
final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}
变量数据变化时,binding将会下一帧改变,如果需要立即执行,调用 executePendingBindings()方法
只要不是集合变量数据,可以后台线程进行修改数据值,不用担心同步问题。Data binding will localize each variable / field
Attribute Setters
对于一个属性,data binding尽量找setAttribute方法,命名空间不重要,主要是属性名字
例如:DrawerLayout 没有任何属性,但提供大量的setter方法,所有能这样使用。
Renamed Setters<android.support.v4.widget. DrawerLayoutapp:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
更改属性关联的setter方法,替换原来的setter方法,通过BindingMethods注解关联一个setter方法,
@BindingMethods({Custom Setters
@BindingMethod(type ="android.widget.ImageView",
attribute ="android:tint",
method ="setImageTintList"),
})
有的属性需要自定义binding逻辑,例如:
android:paddingLeft没有对应的setter方法,只存在setPadding(left, top, right, bottom)
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view,int padding){
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
@BindingAdapter({"bind:imageUrl","bind:error"})
public static void loadImage(ImageView view,String url,Drawable error){
Picasso.with(view.getContext()).load(url).error(error).into(view);
}
<ImageViewapp:imageUrl=“@{venue.imageUrl}”
app:error=“@{@drawable/venueError}”/>
@BindingAdapter("android:paddingLeft")
publicstaticvoid setPaddingLeft(View view,int oldPadding,int newPadding){
if(oldPadding != newPadding){
view.setPadding(newPadding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
}
@BindingAdapter("android:onLayoutChange")
publicstaticvoid setOnLayoutChangeListener(View view,View.OnLayoutChangeListener oldValue,
View.OnLayoutChangeListener newValue){
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.HONEYCOMB){
if(oldValue !=null){
view.removeOnLayoutChangeListener(oldValue);
}
if(newValue !=null){
view.addOnLayoutChangeListener(newValue);
}
}
}
@BindingAdapter("android:onViewAttachedToWindow")
public static void setListener(View view, OnViewAttachedToWindow attached) {
setListener(view, null, attached);
}
@BindingAdapter("android:onViewDetachedFromWindow")
public static void setListener(View view, OnViewDetachedFromWindow detached) {
setListener(view, detached, null);
}
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
public static void setListener(View view, final OnViewDetachedFromWindow detach,
final OnViewAttachedToWindow attach) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
final OnAttachStateChangeListener newListener;
if (detach == null && attach == null) {
newListener = null;
} else {
newListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
if (attach != null) {
attach.onViewAttachedToWindow(v);
}
}
@Override
public void onViewDetachedFromWindow(View v) {
if (detach != null) {
detach.onViewDetachedFromWindow(v);
}
}
};
}
final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
newListener, R.id.onAttachStateChangeListener);
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener);
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener);
}
}
}
Converters
在binding activity添加下面代码
Android Studio Support for Data Binding
Object Conversions 对象转换
<TextView android:text='@{userMap["lastName"]}'/>
userMap返回的对象,会传进setText(CharSequence)方法当为参数
Custom Conversions
当
@{}返回的类型和android属性需要类型不一样时,可以是Conversions进行转换
<Viewandroid:background="@{isError ? @color/red : @color/white}"/>
@BindingConversion
publicstaticColorDrawable convertColorToDrawable(int color){
returnnewColorDrawable(color);
}
不允许混合类型
<Viewandroid:background="@{isError ? @drawable/error : @color/white}"/>
- Syntax highlighting
- Flagging of expression language syntax errors
- XML code completion
- References, including navigation (such as navigate to a declaration) and quick documentation
<TextViewandroid:text="@{user.username, default=测试}"/> 提供默认值
参考资源