<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.xiyoukeji.databingtest.MainActivity">
<data>
<variable
name="user"
type="com.xiyoukeji.databingtest.User"/>
data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
LinearLayout>
layout>
注意外层标签为layout, 并且不能有layout_width和layout_height, 否则appt就报xml属性重复了. 内层为data和布局两层
public class User {
public String firstName;
public String lastName;
// public String getFirstName() {
// return firstName;
// }
//
public void setFirstName(String firstName) {
this.firstName = firstName;
}
//
// public String getLastName() {
// return lastName;
// }
//
public void setLastName(String lastName) {
this.lastName = lastName;
}
//
// public String firstName() {
// return "hhh";
// }
}
user.firstName会怎样调用以上方法呢.后面说说结果.代码就不贴了
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User();
user.setFirstName("haha");
user.setLastName("aiyouwei");
binding.setUser(user);
}
}
这里说说user.firstName调用User里方法的顺序. 如果字段firstName是公有的优先顺序是调用getFirstName(), 其次是firstName() 再次是firstName字段, 当firstName是私有的 那user.firstName不可能指向firstName字段 编译时报错 data绑定无法访问数据. 当然只能调用一次 这里说的是优先级
这里比较容易迷茫, 做个记录. 哈哈 - -
事件处理机制(google示例如下)
public class MyHandlers {
public void onClickFriend(View view) { ... }
}
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.MyHandlers"/>
<variable name="user" type="com.example.User"/>
data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"
android:onClick="@{handlers::onClickFriend}"/>
LinearLayout>
layout>
注意:方法参数和返回值必须和事件监听器的方法一样
public class Presenter {
public void onSaveClick(Task task){}
}
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="task"
type="com.android.example.Task"/>
<variable name="presenter"
type="com.android.example.Presenter"/>
data>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() ->
presenter.onSaveClick(task)}" />
LinearLayout>
layout>
android:onClick="@{(view) -> presenter.onSaveClick(task)}"
如果想用表达式的值的话,则可以这么写
public class Presenter {
public void onSaveClick(View view, Task task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
或者使用lambda来使用更多参数
public class Presenter {
public void onCompletedChanged(Task task, boolean completed){}
}
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
如果你监听的方法的返回值不为void, 那么你的表达式的返回值必须和该方法一致
public class Presenter {
public boolean onLongClick(View view, Task task){}
}
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
If the expression cannot be evaluated due to null objects, Data
Binding returns the default Java value for that type. For example,
null for reference types, 0 for int, false for boolean, etc.If you need to use an expression with a predicate (e.g. ternary), you
can use void as a symbol.
表达式可能为空也没有关系 DataBinding会返回一个java的默认值给你
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
<data>
<import type="android.view.View"/>
data
"@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
名字相同则可以使用别名
type="android.view.View"/>
type="com.example.real.estate.View"
alias="Vista"/>
<data>
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
%lt;代表<, >代表>, 这里表示为List<User>类型 备注 代表空格
Imported types may also be used when referencing static fields and methods in expressions: //也可以导入静态属性和方法哦
<data>
<import type="com.example.MyStringUtils"/>
<variable name="user" type="com.example.User"/>
</data>
…
<TextView
android:text="@{MyStringUtils.capitalize(user.lastName)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Just as in Java, java.lang.* is imported automatically //java.lang包是自动导入的. 比如String类
android:text="@{user.displayName ?? user.lastName}"
等价于
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
LinearLayout>
layout>
这样user这个变量就在name.xml和contact.xml中存在了
Data binding does not support include as a direct child of a merge element. For example, the following layout is not supported:
不支持merge标签作为直接子控件, 如下
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
data>
<merge>
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
merge>
layout>
能使用的表达式和符号基本和java一致。 三元操作符之类的, 不支持this,super,new
而且dataBinding支持非空校验。 不会出现空指针, 如果user为null, user.name为String 默认值就为null user.age为int 值就为0. 依次类推(同java)
<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]}"
如果key为string 需要使用单引号区分开来. 内外层引号不一样就好
前面讲的是数据变化没有自动更新ui的. 接下来看看怎么实现自动更新
private static class User extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return this.firstName;
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
添加@Bindable注解 继承BaseObservable,在layout中出现的属性都可以在BR类中找到对应的字段
来个升级版, 上面还是有点麻烦
A little work is involved in creating Observable classes, so developers who want to save time or have few properties may use ObservableField and its siblings ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable. ObservableFields are self-contained observable objects that have a single field. The primitive versions avoid boxing and unboxing during access operations. To use, create a public final field in the data class:
public class User {
public final ObservableField firstName = new ObservableField<>();
public final ObservableField lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
final User user = new User();
user.firstName.set("haha");
user.lastName.set("aiyouwei");
binding.setUser(user);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
user.firstName.set("a jaslkdjfklajdlf");
}
}, 1000L);
}
}
注意到变化了么.user.firstName.set(“xxx”) 设置属性.
来看看Observable的源码
public class ObservableField<T> extends BaseObservable implements Serializable {
static final long serialVersionUID = 1L;
private T mValue;
/**
* Wraps the given object and creates an observable object
*
* @param value The value to be wrapped as an observable.
*/
public ObservableField(T value) {
mValue = value;
}
/**
* Creates an empty observable object
*/
public ObservableField() {
}
/**
* @return the stored value.
*/
public T get() {
return mValue;
}
/**
* Set the stored value.
*/
public void set(T value) {
if (value != mValue) {
mValue = value;
notifyChange();
}
}
}
注意到了么, 继承BaseObservable 在set()的时候notifyChange();
相当于更新了整个BaseObservable, 而这个类相当于User的一个属性firstName. 代码也简洁了不少.good job!
也可以使用ObservableArrayMap, ObservableArrayList
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
<data>
<import type="android.databinding.ObservableMap"/>
<variable name="user" type="ObservableMap<String, Object>"/>
data>
…
<TextView
android:text='@{user["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user["age"])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
可以告别findViewById()和butterKnife了, 在xml中添加id, 对应的binding文件就会生成对应的View
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.xiyoukeji.databingtest.MainActivity">
<data>
<variable
name="user"
type="com.xiyoukeji.databingtest.User"/>
data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/lastName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
<TextView
android:id="@+id/firstName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
LinearLayout>
layout>
binding.firstName.setVisibility(View.GONE);
dataBinding会为layout的所有变量生成get, set方法
<data>
<import type="android.graphics.drawable.Drawable"/>
<variable name="user" type="com.example.User"/>
<variable name="image" type="Drawable"/>
<variable name="note" type="String"/>
</data>
public abstract com.example.User getUser();
public abstract void setUser(com.example.User user);
public abstract Drawable getImage();
public abstract void setImage(Drawable image);
public abstract String getNote();
public abstract void setNote(String note);
还有一些替换原生方法就不一一细说了。 一般都是自定义类对控件扩展, 或者是自定义控件添加额外属性方法,有需要再去细看了。暂时先到这。