DataBinding学习笔记(一)

引入DataBinding

要在当前module的build.gradle文件中添加如下代码

android{
    ...
    dataBinding {
        enabled = true
    }
    ...
}

快速使用

第一步 :创建对象

一个普通的java对象即可

public class User {
    public String name;
    public String phone;
}

第二步 :修改布局

  1. 规范布局:在布局文件最外层添加一个 layout 的根标签
  2. 引入数据:在 layout 标签下,添加 data 标签
  3. 声明对象:在 data 标签中添加 variable 的标签,其中 name 表示对象名,type 表示类名(包含包名)
  4. 关联属性:通过表达式 “@{}” 获取对象的属性,并将他们绑定到控件中
<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    
    <data>
        <variable name="user" type="cn.com.ursus.User"/>
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"/>
    </RelativeLayout>
</layout>

注意事项

  1. User 中的有公有属性 name 时,@{user.name} 相当于 user.name
    User 中的无公有属性 name 时,@{user.name} 相当于 user.getName()

  2. android.text 绑定属性的时候,注意转成字符串,如果是整型,会被当成资源id处理,可以参考下面的代码(字符串用双引号 " " 原先外面那层双引号转成单引号' '

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text='@{student.age + ""}'/>

第三步 :绑定对象

  1. ActivityonCreate 方法中,用 DataBindingUtil.setContentView 来替换原来的 setContentView ,得到一个名为 ActivityMainBinding 对象。( ActivityMainBinding 对象是根据布
    局文件自动生成的,名称来自于布局文件的名称配合上驼峰规则。)
  2. 通过刚才生成的 ActivityMainBinding 将和布局绑定的对象设置进去
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    User user = new User();
    user.name = "luffy";
    user.phone = "130****5678";
    binding.setUser(user);
}

这样一个最基本的数据绑定就完成了。

Observable

绑定完之后,肯定希望的是 User 对象中的属性值改变之后,绑定的控件也跟着自动刷新,然而并没有,于是乎,需要对 User 对象进行如下改造。

public class User extends BaseObservable{

    private String name;
    private String phone;

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
    
    @Bindable
    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
        notifyPropertyChanged(BR.phone);
    }
}

注解 @Bindable 修饰 getName 方法可在 BR 类中自动生成一个对应属性 name 的整型常量 BR.name。 使用 notifyPropertyChanged 方法即可刷新绑定改属性的控件。至于 BR 是什么,可以类比为 Android 中的 R

如果一个类中只有个别属性别绑定到ui,需要即使刷新,而整个类又不想继承
BaseObservable ,可以使用 ObservableField , 具体可以参考下面的代码

public class Student {
    public final ObservableField<String> name = new ObservableField<>();
    public final ObservableField<String> grade = new ObservableField<>();
    public final ObservableInt age = new ObservableInt();
}

...

final Student student = new Student();
student.name.set("Ace");
student.grade.set("grade2");
student.age.set(1);
binding.setStudent(student);

表达式和事件

前面提过为了避免 android:text 将整型识别为资源文件,需要将整型转成字符串。

android:text='@{student.age + ""}'

由此可见在 @{} 中进行一些简单的表达式操作。

三目运算符 ?:

android:text="@{ user.phone != null ? user.phone : @string/no_phone}"

Null Coalescing Operator ??

这个不是 java 代码的语法,Databinding 自定义的,类似于三目运算符特殊情况的一种简易写法

android:text="@{ user.phone ?? @string/no_phone}"

这和上面那种写法是等价的

使用静态属性和静态方法

上面代三目运算符的例子,如果我们要控制某个控件的显示与否可以这么写

android:visibility="@{ user.phone != null ? View.GONE : View.VISIBLE}"

这里不可以使用 gonevisible , 必须使用 View.GONEView.VISIBLE
可是这个 View 是哪里来的? 我们可以在 data 标签中 import 进来

<data>
    <import type="android.view.View"/>
    <import type="android.text.TextUtils"/>
</data>

import 进来之后,我们就可以也只能使用其中的静态属性静态方法

android:visibility="@{TextUtils.isEmpty(user.phone) ? View.GONE : View.VISIBLE}

注意:
如果两个 import 进来的两个类,类名相同,我们可以给他们设置别名

<import alias="MainActivityPresenter"
        type="cn.com.ursus.PermissionUtils"/>
<import alias="ActivityPresenter"
        type="cn.com.ursus.presenter.PermissionUtils"/>

资源文件

上面的几个例子中在 @{} 中用到了 @string 资源文件,那么可以使用带占位符的 @string 吗?当然可以

<string name="welcome_name">Welcome,%s</string>
...
android:text="@{@string/welcome_name(user.name)}"

当然除了 @string@dimen@color 等资源文件也肯定是支持的

事件

我们可以在 @{} 中可以用表达式来响应事件,比如最常用的 onClick,我们可以之间在之前的 User 类中编写相应的方法来响应,不过此处我重新创建一个类专门处理响应事件。

class Presenter{
    public void clickUserName(View v){...}
    public void userNameChanged(CharSequence s, int start, int before, int count) {...}
}
...
android:onClick="@{presenter.clickUserName}"
android:onTextChanged="@{presenter.userNameChanged}"

在 Databinding 中只需要响应 onTextChanged ,无需理会 beforeTextChanged afterTextChanged ,然而如果在代码中实现 onTextChanged 我们一般都会采用如下的方式,就会显得有些臃肿。

tvName.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

我们自定义的响应方法必须和常规监听方法保持一致的参数吗?答案是否定的,我们可以自己定义响应方法的参数,不过表达式和先前略有不同,类似lambda

public void clickUserName(View v,String username){
    ...
}

...
android:onClick="@{(v)->presenter.clickUserName(v,user.name)}

还有一些表达式就不一一列举了,下面是从官方Data Binding Guide上复制下来的目前 @{} 支持的表达式

  • Mathematical + - / * %
  • String concatenation +
  • Logical && ||
  • Binary & | ^
  • Unary + - ! ~
  • Shift >> >>> <<
  • Comparison == > < >= <=
  • instanceof
  • Grouping ()
  • Literals - character, String, numeric,null
  • Cast
  • Method calls
  • Field access
  • Array access []
  • Ternary operator ?:
  • Null Coalescing Operator ??

自定义属性

自动属性

我们现在自定义了一个控件,其中有一个如下的 setPhoneNumber 方法

public class MyTextView extends TextView {
    ...
    public void setPhoneNumber(String phone) {
      if (!isPhoneNumber(phone)) {
         throw new IllegalArgumentException("手机号格式不正确");
      }
      String show = phone.substring(0, phone.length() - (phone.substring(3)).length())
              + "****"
              + phone.substring(7);
      setText(show);
    }
    ...
}

神奇的一幕发生了 ,我们可以直接在布局文件中使用 phoneNumber 的布局属性,虽然 MyTextView 中并没有该属性

<cn.com.ursus.view.MyTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:phoneNumber="@{user.phone}"/>

自定义属性

现在有个需求,项目用的 Picasso 图片框架,我们需要在ImageView中自定义一个布局属性,使得我们可以给该属性设置一个网络url时,自动使用 Picasso 图片框架来加载网络图片,该如何做?我们只需写一个静态方法,给他打上一个 @BindingAdapter

@BindingAdapter({"image_url"})
public static void setImageUrl(ImageView view, String url){
   Picasso.with(MainApplication.getContext())
            .load(url)
            .placeholder(R.mipmap.ic_launcher)
            .into(view);
}

然后就可以在布局中使用该属性了

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:image_url="@{user.icon}"/>

那么这个 setImageUrl 方法该放在哪个类里呢?需要将那个类导入布局么?
其实 setImageUrl 方法可以放在任意类里面,而且不需要导入布局中,不过同一个控件的自定义属性一般放在一起管理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,795评论 25 707
  • DataBinding整体使用流程 开发阶段 UserModel.java activity_main.xml 在...
    listen2code阅读 3,140评论 7 15
  • 我是在第三次被杨鹏拒绝的时候,开始怀疑起了他的性取向。 “都说女追男,隔层纱,可我和杨鹏中间隔的哪是什么纱,这特么...
    岸上行走的鱼阅读 5,585评论 168 262
  • 无戒365训练营极限挑战第13天 人物的巨大转变对于故事是否好看的重要性在于,它让一个角色拥有了两面或者多面的形象...
    扬菁阅读 674评论 0 1