DataBinding是谷歌官方发布的一个框架,它的目的是降低布局和逻辑的耦合性,使代码的逻辑更清晰。它能够很简单的省去findViewById()
的步骤,大量减少Activity
的代码,数据直接能写在layout
文件上,而且它能自动进行空检测,很多地方对象为空不会引起空指针异常。
下面我将从以下几个方面介绍DataBinding框架:
- DataBinding在AndroidStudio下的环境搭建
- DataBinding的简单使用
- DataBinding的事件处理
- layout文件细节
- 观察者对象
- 生成Binding
- 属性setters
###DataBinding在AndroidStudio下的环境搭建
由于DataBinding是谷歌的官方框架,所以环境搭建很简单,只需在model下的build.gradle文件上加上如下代码:
- android {
- ···
- dataBinding {
- enabled = true
- }
- ···
- }
- 复制代码
不过这要求你的Gradle是 1.5.0-alpha1或者更新的版本,AndroidStudio1.3或更新的版本才可以。DataBinding是一个兼容库,他能运行在Android2.1以上(Api level7+)。
###DataBinding的简单使用
DataBinding的layout
文件与我们一般写的layout
文件不一样,它包含数据和视图两方面,所以其的layout
文件如下:
- <?xml version="1.0" encoding="utf-8"?>
- <layout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- tools:context="com.example.databindingtest.MainActivity">
- <data>
- <variable
- name="user"
- type="com.example.databindingtest.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.firstName}"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{user.lastName}"/>
- </LinearLayout>
- </layout>
- 复制代码
user
是一个java对象,它是被用于这个layout
的一个变量。 User
类如下
- public class User
- {
- public String firstName;
- public String lastName;
- public User(String firstName, String lastName)
- {
- this.firstName = firstName;
- this.lastName = lastName;
- }
- }
- 复制代码
当在layout
文件中写@{user.firstName}
时,会使用user
对象的firstName
属性。 一个用DataBinding的方式写出的layout
文件会产生一个类,类名为layout
文件名的驼峰式写法加上Binding
,所以activity_main.xml
会对应于ActivityMainBinding
类,这个类包含了layout
文件的性能,包括数据和视图两部分,可以用如下方式绑定Activity
和布局文件:
- DataBindingUtil.setContentView(MainActivity.this, R.layout.main_activity);
- 复制代码
完整的Activity文件如下:
- public class MainActivity extends AppCompatActivity
- {
- private ActivityMainBinding mBinding;
- private User mUser;
-
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
- mUser = new User("李", "晓峰");
- mBinding.setUser(mUser);
- mBinding.setHandler(new MyHandlers());
- mBinding.setPresenter(new Presenter());
- }
- }
- 复制代码
运行效果:
如此就完成了DataBinding的最简单体验。也可以把User类写成这样:
- public class User
- {
- 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;
- }
-
- private String firstName;
- private String lastName;
-
- public User(String firstName, String lastName)
- {
- this.firstName = firstName;
- this.lastName = lastName;
- }
- }
- 复制代码
此时布局文件不能直接调用user
的firstName
属性,如果你Ctrl+左键点击ActivityMainBinding
类,会跳到对应的布局文件,这也就能理解为什么布局文件不能访问user
的除public
权限外的其余的属性和方法了。 但是其实布局文件不用做任何修改,也能达到同样的效果,这是因为@{user.firstName}
会去调用user
的getFirstName()
方法,也可以把布局文件写成@{user.getFirstName}
,将会直接调用getFirstName()
方法。
如果布局文件写成@{user.firstName}
,User
类同时包含firstName()
方法和getFirstName()
方法,会优先调用哪个方法呢? 将User
类改成
- public class User
- {
- ···
- public String getFirstName()
- {
- Log.e("User", "getFirstName");
- return firstName;
- }
- public String firstName()
- {
- Log.e("User", "firstName");
- return "12132";
- }
- ···
- }
- 复制代码
打印日志如下:
- com.example.databindingtest E/User: getFirstName
- 复制代码
所以说getFirstName()
方法被调用了,而firstName()
方法并没有被调用。
###DataBinding的事件处理 DataBinding允许我们直接将事件写在控件上,事件的属性名决定于Listener
的方法名,例如长按事件View.OnLongClickListener
有方法onLongClick()
,那么其对应的属性为android:onLongClick
。 有两种处理事件的方式:
- Method References 事件为一个对象及其的方法
- Listener Bindings 事件为一个Lambda表达式
######Method References Method References设置事件和使用android:onClick
属性,将方法写在Activity
是很相似的,主要的区别是DataBinding的Method References对表达式的检验是在编译期,因此如果方法为空或者不正确,会在编译期被发现。且它的方法不必写在Activity
里。
事件处理类:
- public class MyHandlers
- {
- public void click(View view)
- {
- Log.e("MyHandlers","onClick");
- }
- public boolean longClick(View view)
- {
- Log.e("MyHandlers","onLongClick");
- return true;
- }
- }
- 复制代码
注意,方法的输入参数和返回值必须和事件监听器的方法一样。 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"
- tools:context="com.example.databindingtest.MainActivity">
- <variable
- name="handler"
- type="com.example.databindingtest.MyHandlers"/>
- </data>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="@{handler::click}"
- android:onLongClick="@{handler::longClick}"
- android:text="button"/>
- </LinearLayout>
- </layout>
- 复制代码
Activity
加上
- mBinding.setHandler(new MyHandlers());
- 复制代码
分别点击和长按按钮,打印日志如下:
- com.example.databindingtest E/MyHandlers: onClick
- com.example.databindingtest E/MyHandlers: onLongClick
- 复制代码
######Listener Bindings Listener Bindings是当事件发生的时候绑定表达式,它和Method References是类似的,但是它能绑定任意的输入类型方法,而不必和事件监听器的一样,不过返回值要和事件监听器一样。它的表达式要写成Lambda表达式。
事件处理类:
- public class Presenter
- {
- public void click()
- {
- Log.e( "Presenter"," onClick");
- }
- public boolean longClick()
- {
- Log.e( "Presenter","onLongClick");
- return true;
- }
- }
- 复制代码
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"
- tools:context="com.example.databindingtest.MainActivity">
-
- <data>
- <variable
- name="presenter"
- type="com.example.databindingtest.Presenter"/>
- </data>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="@{()->presenter.click()}"
- android:onLongClick="@{()->presenter.longClick()}"
- android:text="button"/>
- </LinearLayout>
- </layout>
-
- 复制代码
Activity
加上
- mBinding.setPresenter(new Presenter());
- 复制代码
日志如下:
- com.example.databindingtest E/Presenter: onClick
- com.example.databindingtest E/Presenter: onLongClick
- 复制代码
所以我们在使用MVP模式的时候,就可以不必去Activity
里去绑定控件的事件与Presenter
里的方法了。
在这里我们没有传递view
属性到View.onClick
方法中, Listener bindings提供给我们两种选择:忽略方法的所有输入参数或者为方法的所有输入参数命名。 例如我们可以写成:
- android:onClick="@{(view)->presenter.click()}"
- 复制代码
如果我们想要在表达式中使用view,那么可以这样写:
- public void click(View view)
- {
- if (view instanceof Button)
- {
- Log.e("Presenter", " onClick");
- }
- }
-
- android:onClick="@{(button)->presenter.click(button)}"
- 复制代码
表达式也可以有自己的输入参数:
- public void click(Task task)
-
- android:onClick="@{()->presenter.click(task)}"
- 复制代码
- public void click(View view,Task task)
-
- android:onClick="@{(view)->presenter.click(view,task)}"
- 复制代码
有一些点击事件与android:onClick
有冲突,DataBinding给他们分配了一些特别的属性名:
View | Listener Setter | Attribute |
---|---|---|
SearchView | setOnSearchClickListener(View.OnClickListener) | android:onSearchClick |
ZoomControls | setOnZoomInClickListener(View.OnClickListener) | android:onZoomIn |
ZoomControls | setOnZoomOutClickListener(View.OnClickListener) | android:onZoomOut |
###layout文件细节 前面我们对于Databinding的布局文件做了简单的介绍,现在详细介绍布局文件细节。 ######类的导入 就像Java一样,类的使用可以是带包名的类名,也支持导入:
- <data>
- <import type="com.example.databindingtest.User"/>
- <variable
- name="user"
- type="com.example.databindingtest.User"/>
- </data>
- 复制代码
java.lang
包下的类不需要导入,可以直接使用
- <variable
- name="str"
- type="String"/>
- 复制代码
在布局文件中可以直接使用类的静态变量和方法,不需要调用ViewDataBinding.setXxx()
- public class StaticClass
- {
- public static String name = "StaticClass";
-
- public static void printf(View v)
- {
- Log.e("StaticClass", "printf");
- }
- }
- 复制代码
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{StaticClass.name}"
- android:onClick="@{StaticClass::printf}"/>
- 复制代码
注意,此时使用Listener Bindings引用方法会报错。
类名支持设置别名:
- <import
- alias="V"
- type="android.view.View"/>
- 复制代码
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{user.lastName}"
- android:visibility="@{user.adult?V.VISIBLE:V.GONE}"/>
- 复制代码
这样能防止出现类名相同的情况而造成的类名无法识别。
定义和使用集合:
- <data>
- <import type="com.example.User"/>
- <import type="java.util.List"/>
- <variable name="userList" type="List<User>"/>
- </data>
- 复制代码
- <TextView
- android:id="@+id/name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{userList[0].firstName}"/>
- 复制代码
其中"<"和">"要使用Html的转义代替,此时AndroidStudio可能会爆红,可是是正确的,可以正确运行。
Map
集合与其类似
- android:text="@{map["key"]}"
- 复制代码
但此时引号有冲突了,所以将外层引号改成单引号:
- android:text='@{map["key"]}'
- 复制代码
######Binding类名自定义 Binding
类的类名可以自定义:
- <data class="ContactItem">
- ...
- </data>
- 复制代码
指定包名:
- <data class="com.example.ContactItem">
- ...
- </data>
- 复制代码
######DataBinding运算符和空检测 DataBinding
的表达式是支持简单的运算符的
- Mathematical (数学计算符) + - / * %
- String concatenation(字符串拼接符) +
- Logical(逻辑运算符) && ||
- Binary(位运算符) & | ^
- Unary(单目运算符) + - ! ~
- Shift(位移运算符) >> >>> <<
- Comparison(比较运算符) == > < >= <=
- instanceof
- Grouping ()
- Literals - character, String, numeric, null
- Cast
- Method calls
- Field access
- Array access []
- Ternary operator(三元运算符) ?:
基本上和Java保持一致。
举例:
- android:text="@{String.valueOf(index + 1)}"
- android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
- android:transitionName='@{"image_" + id}'
- 复制代码
不支持this,super,new
三个关键词。 空检测:
- android:text="@{user.displayName ?? user.lastName}"
- 复制代码
它会根据??运算符左边的对象是否为空来选择值,左边为空现在左边,否则选择右边。 等价于:
- android:text="@{user.displayName != null ? user.displayName : user.lastName}"
- 复制代码
注意,DataBinding
自动进行了很多空指针检测,对象为空调用它的属性或方法不会引起程序崩溃,而是赋予默认值。例如对于这个
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{user.lastName}"
- android:onClick="@{(button)->user.click(button)}"/>
- 复制代码
如果user
对象为空,那么user.lastName
会被分配它的默认值,对象是null
,int
是0
等等;TextView的点击事件会相当于没有设置。 ######资源使用 在表达式中使用xml文件定义的资源是可以的:
- android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
- 复制代码
常用的资源和其对应注解如下:
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 |
###观察者对象 在我们目前的代码中,如果对象改变了某个属性,UI
是无法自动更新的,其实很好理解,在User.setFirstName()
方法中,它只是改变了user
类的属性,而没有通知UI
层,DataBinding
已经封装好了通知UI
层的方法:
- public 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);
- }
- }
- 复制代码
在字段的get
方法或定义处添加@Bindable
注解,就可以在BR
类生成对应的字段,然后继承BaseObservable
类,就可以调用通知UI
层具体属性修改的方法了。 在按钮的点击事件上加上如下方法实现,就能看到UI
的更改动画:
- valueAnimator = ValueAnimator.ofInt(0, 100);
- valueAnimator.setDuration(10000);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
- {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator)
- {
- int animatedValue = (int) valueAnimator.getAnimatedValue();
- mUser.setFirstName("李"+animatedValue);
- }
- });
- valueAnimator.start();
- 复制代码
凡是在layout
文件里面出现了的属性,均可以在BR
类里面找到其的对应。
######ObservableFields 使用@Bindable
注解并调用notifyPropertyChanged(BR.xxx)
方法能达到自动更新UI
的目的,可是过于繁琐,所以DataBinding
为我们封装好了更简单易用的类ObservableFields
,它的源代码很简单:
- public class ObservableField<T> extends BaseObservable implements Serializable {
- private T mValue;
- public ObservableField(T value) {
- mValue = value;
- }
- public ObservableField() {
- }
- public T get() {
- return mValue;
- }
- public void set(T value) {
- if (value != mValue) {
- mValue = value;
- notifyChange();
- }
- }
- }
- 复制代码
在每次调用set()
方法的时候,均会调用notifyChange()
方法,这个方法也是BaseObservable
提供的,效果等同于notifyPropertyChanged(BR._all)
。
使用举例: 实体类
- public class Person
- {
- public ObservableField<String> name = new ObservableField<>();
- public ObservableField<String> address = new ObservableField<>();
- public ObservableInt age = new ObservableInt();
- }
- 复制代码
布局文件
- <?xml version="1.0" encoding="utf-8"?>
- <layout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:app="http://schemas.android.com/apk/res-auto">
-
- <data>
- <variable
- name="person"
- type="com.example.databindingtest.Person"/>
- </data>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.example.databindingtest.SecondActivity">
- <TextView
- android:text='@{person.name}'
- android:id="@+id/tv_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- </layout>
- 复制代码
改变属性的方法调用
- Person person = new Person();
- mBinding.setPerson(person);
- person.name.set("lei");
- 复制代码
当数据是集合时,使用ObservableArrayMap
- ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
- mBinding.setUser(user);
- user.put("firstName","1437");
- user.put("lastName","dufjklsg");
- 复制代码
- <data>
- <variable
- name="user"
- type="android.databinding.ObservableArrayMap<String,Object>"/>
- </data>
- ···
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.example.databindingtest.SecondActivity">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text='@{user["firstName"]}'/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text='@{user["lastName"]}'/>
- </LinearLayout>
- 复制代码
在布局文件和java代码上的写法和普通集合基本一致。 如果key
不是字符串而是数字下标,则使用ObservableArrayList
和之前ArrayList
的用法在形式上基本上一致。
###生成Binding 之前我们已经展示了一种生成Binding
类绑定Activity
的方法
- mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
- 复制代码
还有另一种方式:
- mBinding = ActivitySecondBinding.inflate(getLayoutInflater());
- setContentView(mBinding.getRoot());
- 复制代码
第一行生成Binding
类,第二行绑定Activity
,这种方式在使用RecyclerView
的时候会很有用,能够拿到Binding
类,还可以通过ViewDataBinding.getRoot()
获取根布局。
适配器的例子:
- public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder>
- {
-
- private LayoutInflater mInflater;
- private ArrayList<Message> messageArrayList;
- private Context context;
-
- public MessageAdapter(ArrayList<Message> messageArrayList, Context context)
- {
- this.messageArrayList = messageArrayList;
- this.context = context;
- mInflater = LayoutInflater.from(context);
- }
-
- @Override
- public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
- {
- return MyViewHolder.create(mInflater);
- }
-
- @Override
- public void onBindViewHolder(MyViewHolder holder, int position)
- {
- holder.mBinding.setMessage(messageArrayList.get(position));
- }
-
- @Override
- public int getItemCount()
- {
- return messageArrayList == null ? 0 : messageArrayList.size();
- }
-
- static class MyViewHolder extends RecyclerView.ViewHolder
- {
-
- private ThirdBinding mBinding;
-
- private MyViewHolder(ThirdBinding binding)
- {
- super(binding.getRoot());
- mBinding = binding;
- }
- private static MyViewHolder create(LayoutInflater inflater)
- {
- ThirdBinding mBinding = ThirdBinding.inflate(inflater);
- return new MyViewHolder(mBinding);
- }
- private void bindData(Message message)
- {
- mBinding.setMessage(message);
- }
- }
- }
- 复制代码
######自动生成控件对象 在生成Binding
类的同时,DataBinding
会根据我们在布局文件中设置的id
自动生成对应的字段:
- <Button
- android:id="@+id/button_test"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="12312"/>
- 复制代码
在具体的Binding
类上的字段
- public final Button buttonTest;
- 复制代码
依旧像之前自动转换成驼峰式命名,使用直接调用这个字段就可以:
- mBinding.buttonTest.setText("");
- 复制代码
这就是我们完全没必要使用findViewById()
了。
######变量的set,get方法
正如我们之前所看到的那样,我们在data
标签下所申明的变量会生成对应的set
,get
方法:
- <variable
- name="user"
- type="com.example.databindingtest.User"/>
- 复制代码
- mBinding.setUser(mUser);
- mBinding.getUser();
- 复制代码
###属性setters
对于在布局文件中控件的每一个用表达式描述的属性,DataBinding
会试着寻找方法来设置属性。属性的名称空间不必匹配,仅仅根据属性名本身。例如,用表达式关联的TextView
的属性android:text
会寻找方法setText(String)
,如果表达式返回的是int
,则会寻找方法setText(int)
。如果在已给出的属性中没有某一个属性名,但是有set
方法,那么我们能很简单的设置属性。例如对于DrawerLayout
,他有方法public void setScrimColor(@ColorInt int color)
但是没有属性android:scrimColor
,我们可以自动的设置上这个属性:
- <android.support.v4.widget.DrawerLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:scrimColor="@{@color/scrim}"
- app:drawerListener="@{fragment.drawerListener}"/>
- 复制代码
基于此,我们就能很简单的自定义控件,而不必去写属性值的xml
文件,例如写一个能设置头的TextView:
- public class HeadTextView extends android.support.v7.widget.AppCompatTextView
- {
-
- public HeadTextView(Context context, @Nullable AttributeSet attrs)
- {
- super(context, attrs);
- }
-
- public void setStartText(String startText)
- {
- String text = startText + getText().toString();
- setText(text);
- }
- }
- 复制代码
- <?xml version="1.0" encoding="utf-8"?>
- <layout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:bind="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- tools:context="com.example.databindingtest.MainActivity">
-
- <data>
- <variable
- name="head"
- type="String"/>
- </data>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.example.databindingtest.HeadTextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="183****0038"
- app:startText="@{head}"/>
-
- </LinearLayout>
- </layout>
-
- 复制代码
- mBinding.setHead("电话号码:");
- 复制代码
当然,这个自定义控件还有更多细节需要完善。
注意,app:startText
的属性值必须要是表达式,而不能像常规的那样写成app:startText="电话号码:"
,只有表达式才能引起DataBinding
的机制。不过可以写成app:startText="@{@string/phone}"
,因为表达式里是可以引用资源的。
一些属性有set
方法但是名称不匹配,可以使用BindingMethods
注解来联系起方法和属性,例如对于android:hint
属性,它对应的方法是setImageTintList(ColorStateList)
,而不是setTint
,可以在类的上面加上以下注解来完成匹配:
- @BindingMethods({
- @BindingMethod(type = ImageView.class,
- attribute = "android:tint",
- method = "setImageTintList"),
- })
- 复制代码
注意,此处官方文档写的有误。
Android的框架已经帮我们把framework
层的属性做了匹配。 对于HeadTextView
,可以改成:
- @BindingMethods({
- @BindingMethod(type = TextView.class,
- attribute = "app:startText",
- method = "setStartText111"),
- })
- public class HeadTextView extends android.support.v7.widget.AppCompatTextView
- {
-
- public HeadTextView(Context context, @Nullable AttributeSet attrs)
- {
- super(context, attrs);
- }
-
- public void setStartText111(String startText)
- {
- String text = startText + getText().toString();
- setText(text);
- }
- }
- 复制代码
我们可以自己为属性写set方法,例如对于android:paddingLeft
属性,并没有对应的方法,而有方法setPadding(left, top, right, bottom)
存在,使用BindingAdapter
注解能定制属性的set
方法。
- @BindingAdapter("android:paddingLeft")
- public static void setPaddingLeft(View view, int padding) {
- view.setPadding(padding,
- view.getPaddingTop(),
- view.getPaddingRight(),
- view.getPaddingBottom());
- }
- 复制代码
之前的自定义控件可以这样修改:
- public class AttrAdapter
- {
- @BindingAdapter("app:startText")
- public static void setHead(TextView textView, String head)
- {
- textView.setText(head + textView.getText());
- }
- }
- 复制代码
- <import type="com.example.databindingtest.AttrAdapter"/>
- 复制代码
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="183****0038"
- app:startText="@{@string/phone}"/>
- 复制代码
也就是说我们可以在已有的控件上任意的拓展属性!
我们也可以用适配器来接受多个属性:
- @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);
- }
- 复制代码
- <ImageView app:imageUrl="@{venue.imageUrl}"
- app:error="@{@drawable/venueError}"/>
- 复制代码
当app:imageUrl
和app:error
两个属性都被设置了的时候,会调用loadImage
方法。
适配器里也可以接受之前的属性:
- @BindingAdapter("android:paddingLeft")
- public static void setPaddingLeft(View view, int oldPadding, int newPadding)
- {
- Log.e("AttrAdapter", "oldPadding=" + oldPadding + " newPadding=" + newPadding);
- if (oldPadding != newPadding)
- {
- view.setPadding(newPadding,
- newPadding,
- view.getPaddingRight(),
- view.getPaddingBottom());
- }
- }
- 复制代码
- <variable
- name="left"
- type="int"/>
- 复制代码
- <TextView
- android:paddingLeft="@{left}"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="183****0038"
- app:startText="@{@string/phone}"/>
- 复制代码
- mBinding.setLeft(10);
- 复制代码
打印Log如下:
- com.example.databindingtest E/AttrAdapter: oldPadding=0 newPadding=10
- 复制代码
表达式的输入值可以与属性要求的值不一样,它会自动寻找要求的方法:
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{user}"/>
- 复制代码
- @BindingAdapter("android:text")
- public static void setText(TextView view, User s)
- {
- view.setText(s.toString());
- }
- 复制代码
###结语
至此,DataBinding
的主要特性已经学习完毕了,绝大部分内容只是对官方文档的简单翻译。 在这里我想说一句,学习新的知识最正确的方式是直接看官方文档,因为那是第一手的资料,如果英文实在是太差,才考虑去看别人写的相关文章。