Android Databinding(一)

2015年12月24日

[TOC]

1、環(huán)境需要:

1.Android 2.1 (API level 7+)
2.Gradle 1.5.0-alpha1
3.Android Studio 1.3

2、環(huán)境搭建

在build.gradle的android中加入如下字段,等待系統(tǒng)重新編譯。

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

3、快速開(kāi)始

1、編寫(xiě)布局布局文件
和傳統(tǒng)的布局文件不同,databinding的布局文件的跟標(biāo)簽為layout,他有兩個(gè)子標(biāo)簽,分別為data和原始布局標(biāo)簽,也就是說(shuō)在原來(lái)的布局基礎(chǔ)上多加了一層layout,然后里面又多了一個(gè)data標(biāo)簽。在寫(xiě)完布局文件之后別忘記MakeProject一下,以便系統(tǒng)自動(dòng)生成對(duì)應(yīng)的類(lèi),類(lèi)名為布局文件的名字首字母大寫(xiě)。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 配置變量,name字段為下面想要引用的類(lèi),type為全類(lèi)名,
    AS中快捷鍵ctrl+shift+alt+c -->
    <data>
        <variable
            name="person"
            type="com.znke.hellodatabinding.Person"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!-- 需要填充的字段用@{}表示 -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="25sp"
            android:text="@{person.name}"
            />
        <!-- 這里要轉(zhuǎn)換一下,不然會(huì)引用int值所對(duì)應(yīng)的R文件中的id -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="25sp"
            android:text="@{String.valueOf(person.age)}"
            />
    </LinearLayout>
</layout>

2、編寫(xiě)數(shù)據(jù)對(duì)象
一個(gè)簡(jiǎn)單POJO即可

public class Person {
    public final String name;
    public final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

3、在Acticity中進(jìn)行綁定
在Acticity的onCreate(一個(gè)參數(shù))方法里面進(jìn)行綁定。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //當(dāng)編寫(xiě)完布局文件的時(shí)候,他會(huì)自動(dòng)生成一個(gè)對(duì)應(yīng)的類(lèi),名字為布局文件名稱(chēng)+Binding
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        Person person = new Person("liucl", 22);
        //綁定
        binding.setPerson(person);
    }

*** 注意:***編譯過(guò)中程如果出現(xiàn)xxx.databingding不存在,那么就是你布局文件寫(xiě)錯(cuò)了。仔細(xì)檢查,現(xiàn)在as還不支持語(yǔ)法提示。

運(yùn)行正常如下:


quickstart
quickstart

成功了,就是這三步!?。?/em>

4、綁定點(diǎn)擊事件

Databinding支持把事件綁定到對(duì)象中,使用方法和上面大同小異
1、編寫(xiě)事件對(duì)象

public class ClickEvent {

    public void click(View v){
        Toast.makeText(v.getContext(), "測(cè)試成功 \n Context對(duì)象為"
                + v.getContext().toString()
                + "\n View對(duì)象為"
                + v.toString(),
                Toast.LENGTH_SHORT).show();
    }

}

在這里我們吐司出v所在Context對(duì)象和View對(duì)象以及他所屬的布局。
2、修改我們的布局文件
首先新建一個(gè)variable引入這個(gè)類(lèi)。在data字段中加入

<variable
    name="event"
    type="com.znke.hellodatabinding.ClickEvent"/>

其實(shí),我們可以使用import直接導(dǎo)入這個(gè)類(lèi),值得注意的是,這個(gè)區(qū)別于javaimport關(guān)鍵字,java中這個(gè)是導(dǎo)入包,而這個(gè)是導(dǎo)入類(lèi)。完整的data代碼如下:

<data>
    <import type="com.znke.hellodatabinding.Person"/>
    <import type="com.znke.hellodatabinding.ClickEvent"/>
    <variable
        name="person"
        type="Person"/>
    <variable
        name="event"
        type="ClickEvent"/>
</data>

之后,我們?cè)诓季治募屑尤胍粋€(gè)Button,在onClick屬性中引用這個(gè)類(lèi)

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="測(cè)試"
    android:onClick="@{event.click}"/>

最后別忘記在Acticity中綁定?。?!

...
    binding.setEvent(new ClickEvent());
...

運(yùn)行正常如下:


quickstart
quickstart

5、Include對(duì)象傳遞

Databinding支持include對(duì)象傳遞,在A布局里面的對(duì)象可以傳遞到他include進(jìn)來(lái)的布局,

  1. 在A布局里定義號(hào)自定義命名空間。xmlns:app="http://schemas.android.com/apk/res-auto"下面要用到。
  2. A布局里面的include標(biāo)簽
    <include layout="@layout/include_click"
        app:event="@{event}"/>
  1. B布局代碼
    在B中也要引用一個(gè)傳遞過(guò)來(lái)的類(lèi),這個(gè)布局不用在Acticity中綁定??催^(guò)這個(gè)代碼之后,你可能回想怎么不用merge優(yōu)化呢,——暫時(shí)不支持
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="event"
            type="com.znke.hellodatabinding.ClickEvent"/>
    </data>

    <!-- 暫不支持merge標(biāo)簽 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="測(cè)試"
            android:onClick="@{event.click}"/>
    </LinearLayout>
</layout>

6、表達(dá)式符號(hào)

布局文件支持這些表達(dá)式,我直接引用谷歌文檔
https://developer.android.com/tools/data-binding/guide.html

Mathematical  + - / * %
String  + 這個(gè)符號(hào)可能會(huì)引起引號(hào)沖突,需把文字放到資源文件
Logical  && ||
Binary & | ^
Unary + - ! ~
Shift >> >>> <<
Comparison == > < >= <=
instanceof
Grouping ()
Literals - character, String, numeric, null
Cast
Method calls
Field access
Array access []
Ternary operator ?:

7、綁定對(duì)象與視圖,使他們能聯(lián)動(dòng)

通過(guò)更改對(duì)象,視圖自動(dòng)就會(huì)隨之改變,這個(gè)功能實(shí)現(xiàn)View與model層邏輯代碼的解耦和。
谷歌給出兩種方法

  1. Observable Objects
    將你的POJO繼承BaseObservable,這個(gè)類(lèi)實(shí)現(xiàn)了Observable??梢詫?shí)現(xiàn)POJO值改變,framework層會(huì)自動(dòng)更新布局文件數(shù)據(jù)
private static class Person extends BaseObservable {
       private String name;
       private String age;
       @Bindable
       public String getName() {
           return name;
       }
       @Bindable
       public String getAge() {
           return age;
       }
       public void setName(String name) {
           this.name = name;
           notifyPropertyChanged(BR.name);
       }
       public void setAge(String age) {
           this.age = age;
           notifyPropertyChanged(BR.age);
       }
}
  1. ObservableFields
    其實(shí)他繼承了BaseObservable這個(gè)類(lèi),在他的泛型中添上你的類(lèi)型。同時(shí)
    ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble,ObservableParcelable
    也都是繼承了BaseObservable。
public class Person {
        public ObservableField<String> name = new ObservableField<>();
        public ObservableInt age = new ObservableInt();
}

注:谷歌文檔也給了一個(gè)Observable Collections,但我沒(méi)試驗(yàn)成功,但是使用上面那個(gè)方法,是可以實(shí)現(xiàn)集合更改的。

通過(guò)上面的修改后,無(wú)論在那里改這個(gè)對(duì)象里面的值,相應(yīng)的布局文件里面的引用都會(huì)隨之改變。

8、拋棄你的findViewById()

有了Databinding再也不用謝冗長(zhǎng)的findViewById()了,框架會(huì)為你自動(dòng)生成的BR文件。
首先在我們的布局文件中,為我們兩個(gè)TextView加上id,回到Acticity中的Binding對(duì)象,是不是多出了兩個(gè)屬性。

    binding.tvText1.setText("");
    binding.tvText2.setText("");

這樣你就可以直接給他設(shè)置文本了,但是有一種更好的方法,直接binding.setVariable(com.znke.hellodatabinding.BR.person,new Person());就可以直接給布局文件中的變量賦值。

BR存儲(chǔ)了布局中data標(biāo)簽中的variable,相當(dāng)于R文件。

9、Attribute Setters(屬性設(shè)置)

這東西就好像Button里面的onClick屬性,然后給他在Acticity里面寫(xiě)一個(gè)對(duì)應(yīng)的實(shí)現(xiàn)方法。他的強(qiáng)大之處就是它可以什么都向里面設(shè)置。不關(guān)是點(diǎn)擊事件。他對(duì)于的實(shí)現(xiàn)方法只需要用一個(gè)注解聲明就可以了,寫(xiě)在那里都可以,即使是一個(gè)沒(méi)有使用的java文件。
通過(guò)一個(gè)Demo來(lái)說(shuō)明,他有一個(gè)輸入框,輸入框輸入文字,上面的TextView也會(huì)隨之改變,這里用到了屬性設(shè)置對(duì)象綁定。
布局代碼

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="輸入內(nèi)容"
        app:textwatcher="@{person}"/>

屬性設(shè)置代碼

public class BindingAdapter {

    private static final String TAG = BindingAdapter.class.getSimpleName();

    /**
     * 添加上這個(gè)注解,方法為靜態(tài)無(wú)返回值,
     * 方法有兩個(gè)參數(shù),第一個(gè)是控件本身,第二個(gè)為傳進(jìn)來(lái)的對(duì)象
     * 注意這個(gè)方法會(huì)被調(diào)用多次,其中會(huì)有對(duì)象為空的情況。So...
     *
     * @param e      控件本身
     * @param person 傳進(jìn)來(lái)的對(duì)象
     */
    @android.databinding.BindingAdapter({"app:textwatcher"})
    public static void editTextWatcher(final EditText e,final Person person) {
        if (person != null) {
            e.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) {
                    person.name.set(s.toString());
                    e.setSelection(s.length());
                }
            });
        }
    }
}

BindingAdapter生命周期所在
View所在ActivityonCreate方法之后。當(dāng)然,自定義監(jiān)聽(tīng)除外

12-21 11:39:51.289 3388-3388/? E/TAG: onFinishInflate: 
12-21 11:39:51.295 3388-3388/? E/TAG: onCreate: null
12-21 11:39:51.331 3388-3388/? E/TAG: initAdapter: 
12-21 11:39:51.331 3388-3388/? E/TAG: initManager: 
12-21 11:39:51.333 3388-3388/? E/TAG: onAttachedToWindow: 
12-21 11:39:51.338 3388-3388/? E/TAG: onMeasure: 
12-21 11:39:51.406 3388-3388/? E/TAG: onLayout: 
12-21 11:39:51.429 3388-3388/? E/TAG: onMeasure: 
12-21 11:39:51.431 3388-3388/? E/TAG: onLayout: 
12-21 11:39:51.433 3388-3388/? E/TAG: onDraw: 
12-21 11:39:51.502 3388-3388/? E/TAG: onMeasure: 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容