Generated binding classes (產生綁定類別)

前言


Data Binding 會產生綁定類別(binding class),用來存取佈局變數(layout's variable)和視圖(View)。

以下描述了如何建立及客製化綁定類別。

綁定類別將佈局變量與佈局中的視圖連結起來,綁定類別的名稱和 package 可以自行定義。所有的綁定類別都繼承自 ViewDataBinding 類別。

每個 layout file 都會有一個對應的綁定類別。預設,類別的名稱會根據佈局文件的名稱,將其轉換為 Pascal 大小寫並添加 Binding。

若 layout 的名稱為 activity_main.xml,則對應的綁定類別為 ActivityMainBinding。

此類別包含佈局屬性(如user variable)到佈局視圖的所有綁定內容,並知道如何為綁定表達式指定值。

 

Create a binding object


在對佈局進行填充之後,應該快速的創建綁定對象(binding object),以確保在綁定到佈局中具有表達式的視圖之前不會修改視圖層次結構。

將對象綁定到佈局的最常用方法是使用綁定類別上的靜態方法。

可以透過使用綁定類別的 inflate 方法來擴展視圖層次結構並將對象綁定到該層次結構。如下
@Override  
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MyLayoutBinding binding = MyLayoutBinding.inflate(getLayoutInflater());
}

 

除了 LayoutInflater 對象之外,還有一個替換的 inflate 方法,它接受 ViewGroup對象,如下
MyLayoutBinding binding = MyLayoutBinding.inflate(getLayoutInflater(), viewGroup, false);

如果使用不同的機制對佈局進行填充,則可以單獨綁定,如下
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有時候沒辦法事先知道綁定類型。在這種情況下,可以使用 DataBindingUtil 類創建綁定,如下
View rootView = LayoutInflater.from(this).inflate(layoutId, parent, attachToParent);  
ViewDataBinding binding = DataBindingUtil.bind(viewRoot);

若開發者是在 Fragment, ListView, RecyclerView adapter 中使用 data binding item

則可以使用綁定類別或 DataBindingUtil 的 inflate方法。如下
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);  
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

 

具有 ID 的視圖


Data Binding 會在綁定類別中建立一個不可變的欄位,該欄位會對應於 layout 檔案中每個具有 id 的 View。如下 Data Binding 會建立 firstName 和 lastName 欄位
<layout xmlns:android="http://schemas.android.com/apk/res/android">  
<data>
<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:id="@+id/firstName"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:id="@+id/lastName"/>
</LinearLayout>
</layout>


Data Binding 會在單次傳遞中從視圖層次結構中提取具有 ID 的視圖。這個機制比為佈局中的每個視圖調用 findViewById 方法都要快。ID 對於 Data Binding 並不是必要的,但仍有些實體需要從 code 存取視圖。

 

Variables


Data Binding 會為每個宣告在 layout 中的變數建立存取方法(setter and getter)。如下,將會為 user, image, note 變數建立存取方法。
<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>


ViewStubs


與普通視圖不同,ViewStub 物件從一個不可見的視圖開始。

當它們被顯示或被明確告知要填充時,它們會通過填充另一個佈局來替換自己的佈局。

由於 ViewStub 基本上會從視圖層次結構中消失,因此綁定對像的視圖也必須消失以允許垃圾回收聲明。

因為視圖是不可變的,所以 ViewStubProxy 對象取代了生成綁定類中的 ViewStub,使您可以在 ViewStub 存在時訪問它,並在 ViewStub 填充時訪問視圖層次結構

當填充另一個佈局時,必須為該佈局建立綁定。因此 ViewStubProxy 必須監聽  ViewStub,並在需要時建立綁定。在同一時間內只能有一個監聽器存在,ViewStubProxy 予許開發者設定 OnInflateListener,該監聽器將在建立綁定之後被呼叫。

 

Immediate Binding


當變量或可觀察對象發生變化時,綁定將會被排程更改在下一幀之前。但是,有時必須立即執行綁定。要強制執行,可以使用 executePendingBindings 方法。

 

 

進階綁定


動態變數


動態變數適用於無法得知特定的綁定類別的時候。如當一個 RecyclerView.Adapter 對非特定的佈局進行操作時並不知道特定的綁定類別,但它還是必須在呼叫 onBindViewHolder 方法時指定綁定値。

在下面的範例中,RecyclerView 綁定的所有佈局都有個 item 變數。而 BindingHolder 物件具有 getBinding 方法,該方法可以回傳 ViewDataBinding 基本類別。
public void onBindViewHolder(BindingHolder holder, int position) {  
final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}

Data Binding 在 module package 會產生一個名為 BR 的類別,該類別包含用於數據綁定的資源的 ID。在上一個範例中 BR.item 是自動產生的。

 

背景執行緒


開發者可以在背景執行緒中更改數據模型(data model),只要它不是集合即可。Data Binding 會判斷每個變數/屬性以避免任何並發問題。

 

客製化綁定類別名稱


在預設情況下綁定類別的名稱是根據其相關佈局名稱而來,主要規則是將佈局名稱的底線去除並讓首字改為大寫,最後再加上 Binding,而綁定類別的位置會放置於 module package 的 databinding 資料夾下。 如佈局名稱為 contact_item.xml,其綁定類別為 ContactItemBinding。若 module package 為 com.example.my.app 則綁定類別的位置為 com.example.my.app.databinding。

可以透過 data 元素的 class 屬性來改變綁定類別的名稱或位置。

如下面的內容將會產生名為 ContactItem 綁定類別。
<data class="ContactItem">  

</data>

也可以使用完整名稱來指定綁定類別的位置。如下

將產生 ContactItem 綁定類別,其位置為 com.example
<data class="com.example.ContactItem">  

</data>

 

Orignal From: Generated binding classes (產生綁定類別)

Splash Activity

1.編輯 value/styles.xml 加入以下內容


<resources>  
...
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/launch_screen</item>
<!-- Optional, on Android 5+ you can modify the colorPrimaryDark color to match the windowBackground color for further branding-->
<!-- <item name="colorPrimaryDark">@android:color/white</item> -->
</style>
...
</resources>

其中 @drawable/launch_screen 目前還沒有,下一步製作。

2.在 drawable 新增 launch_screen.xml,內容如下


<?xml version="1.0" encoding="utf-8"?>  

<!-- The android:opacity="opaque" line — this is critical in preventing a flash of black as your theme transitions. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:opacity="opaque">

<!-- The background color, preferably the same as your normal theme -->
<item android:drawable="@android:color/white"/>

<!-- Your product logo - 144dp color version of your app icon -->
<item>
<bitmap
android:gravity="center"
android:src="@drawable/your_logo"/>
</item>

</layer-list>

其中 @drawable/your_logo 就是你想顯示的 logo 圖示

3. SplashActivity


該 Activity 除了在 onCreate 作了特殊處理之外,並沒有其它不同。如下
public class LogoActivity extends Activity {  
...

@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}

...

 

4. 在 AndroidManifest.xml 讓 LogoActivity 套用 AppTheme.Launcher


    <activity  
android:theme="@style/AppTheme.Launcher"
android:name=".logo.LogoActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

 

Orignal From: Splash Activity

work with observable data objects (使用可觀察數據對象) (Data Binding)

前言


可觀察性(Observability)是指一個物件具有當其數據發生變化時通知其他元件的能力。

Data Binding Library讓物件,欄位,集合具有可被觀察的能力

任何的 POJO 都可以使用於 Data Binding,但修改該物件時並不會讓 UI 也跟著更新。 Data Binding 可為數據對象(data object)提供在其數據更改時通知其他對象(稱為偵聽器)的能力。

可觀察類別(observable class)有3種類型,objects,fields,collections

當上列3種可觀察類別的物件綁定了 UI,且物件的屬性(property)發生變化時,UI 將會自動更新。

 

Observable fields


因為去建立實作 Observable 介面的類別需要一些額外工作,因此若開發者僅有少量的屬性其實是不值得的,在這種情況下可以使用一般性可觀察類別,如下





ObservableBoolean
ObservableByte
ObservableChar
ObservableShort

ObservableInt
ObservableLong
ObservableFloat
ObservableDouble

ObservableParcelable
 
 
 



 

Observable fields 是具有單一欄位的自包含可觀察物件,使用方式為建立一個 public final 屬性,如下
private static class User {  
public final ObservableField<String> firstName = new ObservableField<>();
public final ObservableField<String> lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}

要存取數值,使用 set 和 get 方法如下
user.firstName.set("Google");  
int age = user.age.get();

注意: Android Studio 3.1 或更高的版本可讓開發者以 LiveData objects 代替 observable fields,可提供額外的好處。參考 Use LiveData to notify the UI about data changes

 

Observable collections


有些 App 使用動態資料結構來持有數據,observable collections 為提供這些動態資料結構。

ObservableArrayMap 用於當鍵值為參考型別時特別有用,如下
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();  
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

在 layout 檔案中,map 使用如下
<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"/>

ObservableArrayList 則適用於當鍵值為 integer 時,如下
ObservableArrayList<Object> user = new ObservableArrayList<>();  
user.add("Google");
user.add("Inc.");
user.add(17);

在 layout 檔案中,list 使用如下
<data>  
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user" type="ObservableList<Object>"/>
</data>

<TextView
android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

 

Observable objects


當類別實作了 Observable 介面便可提供註冊監聽器,該監聽器可讓想觀察的元件可取得通知。

Observable 介面具有加入和移除監聽器的機制,但開發者必須決定何時送出通知。為了讓過程更簡單,Data Binding Library 提供了 BaseObservable 類別,該類別實作了監聽器註冊機制。

當屬性發生變化時,實作 BaseObservable 的類別便負責通知。透過在 getter方法加入 Bindable 註釋,並在 setter 方法呼叫 notifyPropertyChanged 方法,如下
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);
}
}

 

Data Binding 會在 module package 產生一個 BR 的類別,該類包含用於數據綁定的資源的 ID。

Bindable 註釋在編譯期間會產生一個 entity 到 BR 類別。

如果無法更改 data class 的 base class,則可以使用 PropertyChangeRegistry 實現 Observable 接口,以有效地註冊和通知監聽器。

Orignal From: work with observable data objects (使用可觀察數據對象) (Data Binding)

Layouts and binding expressions (佈局和綁定表達式)

Layouts and binding expressions


1.簡介


表達式語言(expression language)允許開發者編寫處理視圖調度事件的表達式。

Data Binding Library 會自動生成視圖與數據對象綁定所需的類別(綁定類別)(binding class)。

數據綁定佈局文件(data binding layout files)和一般佈局文件不同。基本上會以 layout 標籤開頭,後面跟著 data 元素和 view 的元件。這個 view 元件代表非綁定部分的根節點。如下範例所示
<?xml version="1.0" encoding="utf-8"?>  
<layout xmlns:android="http://schemas.android.com/apk/res/android">

   <data>
       <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}"/>

       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>

</layout>

 

在 data 元素的 user 變數代表可在這個佈局中使用的屬性。
<variable name="user" type="com.example.User" />

 

表達式透過 @{} 語法將變數和佈局結合,如下 TextView 將被設定給 user 變數的 firstname 屬性。
<TextView android:layout_width="wrap_content"  

android:layout_height="wrap_content"

android:text="@{user.firstName}"

/>

Note:

因為佈局表達式(layout expression)無法進行單元測試,因此應該保持小而簡單,開發者可以透過 binding adapter 保持簡潔。

 

2.Data Object(資料物件)


假設有個 User 類別如下
public class User {  
 
public final String firstName;
 
public final String lastName;
 
public User(String firstName, String lastName) {
     
this.firstName = firstName;
     
this.lastName = lastName;
 
}
}

這種類型的物件具有永不改變的數據,該數據通常會讀取一次且不會更改。也可以使用遵循一組約定的對象,例如 Java 中的訪問器方法,如以下
public class User {  
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
}

從數據綁定的角度來看,這兩個類別是相同的。

以下方表達式而言
<TextView   

android:text="@{user.firstName}"
/>

等同於存取第一個類別的 public final String firstName 變數,也等同於呼叫第二個類別的 getFirstName 方法

以編程方式來呈現如下
TextView firstName = (TextView)findViewById(….)  

firstName.setText(user.firstName);

or

firstName.setText(user.getFirstName());

 

3.Binding Data(綁定類別)


Data Binding 基本上會為每個佈局文件產生綁定類別(binding class)。

預設綁定類別的名稱會根據佈局文件的名稱建立,建立方式為將其轉換 Pascal 大小寫並添加 Binding 後綴。

舉例來說若佈局文件名稱為 activity_main.xml,則相對應的綁定類別為 ActivityMainBinding。

綁定類別將會持有對應佈局文件的所有綁定內容(如 user 變數)。並知道如何為綁定表達式指定數值。

建立綁定類別的推薦方式為在擴展佈局時建立。如下
@Override  
protected void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
   
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
   
User user = new User("Test", "User");
   binding
.setUser(user);
}

在執行 App 時,將在 UI 中顯示 Test。或可以使用 LayoutInflater 獲取視圖,如下:
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());

若開發者在 Fragment,ListView 或 RecyclerView adapter 內使用數據綁定,則可以使用綁定類別或 DataBindingUtil 類別的 inflate 方法,如下:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);  
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

4.Expression Language(表達式語言)


一般特性










數學
字串連接
邏輯
二進制
一元

+ - / * %
+
&& ||
& | ^
+ - ! ~

移位
比較

分組
文字

>> >>> <<
== > < >= <=
Instanceof
()
character, String, numeric, null

轉型
方法呼叫
欄位存取
陣列存取
三元運算符

Cast
Method calls
Field access
[]
? :



範例
android:text="@{String.valueOf(index + 1)}"  

android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"

android:transitionName='@{"image_" + id}'

目前不支援的運算符



This
Super
New
Explicit generic invocation
(顯式通用調用)




 

空結合運算符(Null coalescing operator) ??


?? 表示為空結合運算符,若該運算符的左邊為 null 則選擇運算符的左邊,若該運算符的左邊不為 null 則選擇運算符的右邊。如下
android:text="@{user.displayName ?? user.lastName}"

等同於
android:text="@{user.displayName != null ? user.displayName : user.lastName}"

 

屬性參考(Porperty Reference)


表達式可以參考類別的屬性,也可以使用於 fields, getters, ObservableField objects
android:text="@{user.lastName}"

 

避免空指標異常(avoiding null pointer exception)

綁定類別會自動檢查空指標異常,並提供預設值。
若表達式 @{user.name} 若 user 為 null 則提供預設值 null (字串)。
若表達式 @{user.age } 且 age 的型別為 int,若 user 為 null 則提供預設值0。

集合(Collection)


一般的集合,如 array, list, map 都可以透過 [] 運算符來操作。如下
<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]}"

Note:

也可以透過 object.key 來參考 map 的 元素。

如 android:text="@{map[key]}" 等同於 android:text="@{map.key}"

字串


可以使用單引號表示屬性值,或是使用雙引號表示字串。
android:text='@{map["firstName"]}'

也可以使用雙引號表示屬性值,但在這種情況下就必須使用 ` 表示字串
android:text="@{map[`firstName`]}"

 

資源


可以使用以下表達式來存取資源
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

格式化字串和複數可以提供參數來計算
android:text="@{@string/nameFormat(firstName, lastName)}"  

android:text="@{@plurals/banana(bananaCount)}"

若複數需要多個參數,必須全部傳遞它們
Have an orange  

Have %d oranges

android:text="@{@plurals/orange(orangeCount, orangeCount)}"

以下為資源對應的表示方式










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



 

5.Event handling(事件處理)


Data Binding 可以透過表達式處理從視圖發送的事件(如 onClick() 方法)。

事件屬性名稱由監聽器的方法名稱決定,但有一些例外。如 View.OnClickListener 有方法為 onClick(), 則該事件的屬性為 android:onClick

除了 android:onClick 以外還有一些點擊事件的特殊項目。這些特殊項目必須使用不同的屬性來避免衝突。如下






Class
Listener setter
Attribute

SearchView
setOnSearchClickListener(View.OnClickListener)
android:onSearchClick

ZoomControls
setOnZoomInClickListener(View.OnClickListener)
android:onZoomIn

ZoomControls
setOnZoomInClickListener(View.OnClickListener)
android:onZoomOut



可以使用以下機制來處理事件

1.方法引用(method references)

在表達式中可以引用符合偵聽器方法簽名的方法。

當表達式為方法引用時,Data Binding 會包裝方法引用(method reference)和所有者物件(owner object)在監聽器中並將監聽器設定到目標視圖。

若方法引用為 null 則 Data Binding 不會建立監聽器並設定 null

2.監聽器綁定

當事件發生時使用 lambda 表示式。

Data Binding 會建立監聽器並設定到目標視圖上。當處理事件時,監聽器將會執行 lambda 表示式。

 

方法引用(method reference)


事件可以直接綁定到方法,類似於 android:onClick 可以指定給 Activity 的方法。

與 View onClick 相比,主要優點是表達式在編譯時期處理,因此如果該方法不存在或其簽名不正確,會收到編譯錯誤。

方法引用和偵聽器綁定的主要區別在於偵聽器實現是在綁定數據時建立的,而不是在觸發事件時建立。

若想在事件發生時再執行表達式,選擇監聽器綁定較好。

使用普通綁定表達式(normal binding expression)將事件分配給其處理者,其值為要調用的方法名稱。如下類別
public class MyHandlers {  
public void onClickFriend(View view) { ... }
}

表達式可以綁定視圖的點擊監聽器(click listener)到 onClickFriend 方法,如下
<?xml version="1.0" encoding="utf-8"?>  
<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>

Note:
表達式方法的簽名必須和監聽器對象方法的簽名完全相同。

 

監聽器綁定(listener binding)


監聽器綁定為當事件發生時才執行綁定的表達式。類似於方法引用,但可以運行任意數據綁定表達式。該特性適用於 Android Gradle 2.0 或以上的版本。

在方法引用中,方法的參數必須和事件監聽器的參數完全相同。

監聽器綁定則是方法的回傳值和監聽器的回傳值必須相同(除非是回傳 null)。

如下類別
public class Presenter {  
public void onSaveClick(Task task){}
}

可綁定點擊事件到 onSaveClick 方法,如下
<?xml version="1.0" encoding="utf-8"?>  
<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>

在表達式使用 callback 時,Data Binding 會自動建立需要的監聽器並註冊到事件。當視圖發送事件時,Data Binding 會執行相對的表達式。當表達式執行時可以確保空指針(null)和執行緒安全。

在上面的示例中,我們未定義傳遞給 onClick(View)的視圖參數。

監聽器綁定提供了 2 個選擇來指定監聽器的參數。開發者可以忽略所有的參數或命名所有的參數。

若命名了參數,則開發者可以在表達式中使用它。如上面的範例可以改為
android:onClick="@{(view) -> presenter.onSaveClick(task)}"

若想使用表達式的參數,如下
public class Presenter {  
public void onSaveClick(View view, Task task){}
}

android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

可以使用 lambda 表達式提供超過 1 個的參數。
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)}"

如果表達式執行結果為 null,則數據綁定將返回該類型的默認值。例如,引用類型為 null,int 為 0,布林值為 false 等。

若在表達式使用三元運算符則可使用 void,如下
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

 

避免複雜的監聽器


監聽器表達式可以讓代碼非常容易了解。若表達式過於複雜也會使佈局難以閱讀和維護。

這些表達式應該像 UI 的可用數據傳遞給回調方法一樣簡單並實現業務邏輯在監聽器表達式呼叫的回調方法中。

 

6. Imports, variables, and includes


Imports 用來在 layout 中可以參考到類別,variable 用來描述在表達式使用的屬性, includes 可在整個應用中重複使用複雜的佈局。

 

Imports


可在 layout file 簡單的參考到類別,必須寫在 data 元素裡面。如下 import View 類別到 layout file 中。
<data>  
<import type="android.view.View"/>
</data>

Import View 類別之後便可在表達式中參考它。如下在表達式中使用 View 類別的  VISIBLE 和 GONE 常數。
<TextView  
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

 

 型別別名(Type aliases)


若類別名稱發生了衝突,當中的類別便可重命名為別名。如下將 com.example.real.estate 的 View 類別重新命名為 Vista。
<import type="android.view.View"/>  

<import type="com.example.real.estate.View" alias="Vista"/>

 

Import other classes


import 的型別可以在變數或表達式中用來當作型別參考,如下
<data>  
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>

注意: Android Studio 還未完全支援 import,因此自動完成的功能還無法使用在 IDE 中。

Import 的型別也可以用於轉型。如下將 connection 屬性轉型成 User
<TextView  
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

在表達式中也可以使用 import 型別來參考靜態變數或方法,如下 import MyStringUtils 並使用其 capitalize 方法。
<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"/>

注意 java.lang.* 會自動 import。

 

Variables

可以在 data 元素中使用多個 variable元素,每個 variable 元素代表可能會使用在表達式的屬性。如下宣告了 user, image, note



<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>




 

變數的型別會在編譯時期檢查,因此若變數實作了 Observable 或 observable collection,將會被反射到型別上。

若變數是基本類別或介面且未實作 Observable ,則該變數是無法被觀察(observable)

 

當不同佈局文件用於各種配置(橫向或縱向)時,這些變量會被組合。佈局文件之間不可存在互相衝突的變數名稱定義。

 

每個變數都有 setter 和 getter 方法定義在綁定類別中。變數將會使用預設數值,如整數為 0,布林為 false,參考型別則為 null。

 

有個特別變數為 context 會自動產生以便用於表達式,該變數的數值為根視圖(root view) getContext 方法的回傳值。

 

Includes

通過使用 App 的命名空間和屬性的名稱,變數可以從包含的佈局傳遞到佈局綁定中。如下從 name.xml 和 contact.xml 佈局中 include user 變數。



<?xml version="1.0" encoding="utf-8"?>

<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>




 

注意 : 不支援從 merge 元素使用 include 動作。如下不支援該用法



<?xml version="1.0" encoding="utf-8"?>

<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><!-- Doesn't work -->

<include layout="@layout/name" bind:user="@{user}"/>

<include layout="@layout/contact" bind:user="@{user}"/>

</merge>

</layout>




 

Orignal From: Layouts and binding expressions (佈局和綁定表達式)

finaldb 和 Serializable 合併使用問題

問題描述:


若有個物件想透過 finalDB 儲存但又實作了 Serializable 介面。

當去存取該物件時,會出現"serialVersionUID has type long, got null" 相關問題。

發生問題的 class 如下
@Table(name = "GoodItem")  
public class GoodItem implements Serializable{


private static final long serialVersionUID = -6588468312284378785L;

@Id
private String id;
...

 

解決方法:


將 serialVersionUID 加上  @Transient 註解來解決,如下
@Table(name = "GoodItem")  
public class GoodItem implements Serializable{

@Transient
private static final long serialVersionUID = -6588468312284378785L;

@Id
private String id;
...

 

 

Orignal From: finaldb 和 Serializable 合併使用問題

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Affiliate Network Reviews