Tìm hiểu Data Binding Library - Part 1

Xin chào các bạn, hôm nay tôi xin giới thiệu 1 library mới của android.

Khi lập trình Android có những lúc bạn mong ước rằng có cách nào đó có thể code Java ngay trên layout để có thể giảm tối thiểu các dòng code không cần thiết. Đặc biệt khi bạn cần hiển thị hàng trăm trường thông tin trên giao diện và bạn chán ngấy khi cứ phải findViewById() rồi mới có thể gán giá trị cho chúng.

Data Binding Library là 1 thư viện hỗ trợ đắc lực cho vấn đề trên. Đây là 1 support library nên có thể sử dụng từ Andorid 2.1 (API 7+) trở lên.

Tôi xin cung cấp thêm là Library này hiện khá là mới với bản release đầu tiên vào ngày 28/5/2015 và phiên bản gần đây nhất 9/10/2015 vậy nên bạn cần phải thường xuyên cập nhật các version mới nhất để tránh các bug không đáng có.

Cấu hình môi trường

Để có thể bắt đầu sử dụng với Data Binding bạn hãy tải library Support repository trong Android SDK manager. Data Binding yêu cầu plugin Gradle từ bản 1.3.0-beta4 trở lên và phiên bản Android Studio từ 1.3 trở lên.

Hiện tại đã có phiên bản gradle 1.3.1 và databinding 1.0-rc4 nên trong bài viết này tôi sẽ sử dụng 2 phiên bản này

Tham khảo thêm các version dưới đây:

  1. Gradle : http://developer.android.com/tools/revisions/gradle-plugin.html
  2. DataBinding : https://bintray.com/android/android-tools/com.android.databinding.dataBinder/view

Cấu hình project Thêm đoạn code dưới đây vào build.gradle Capture.PNG

  dependencies {
       classpath "com.android.tools.build:gradle:1.3.1"
       classpath "com.android.databinding:dataBinder:1.0-rc4"
   }

và trong repositories

allprojects {
   repositories {
       jcenter()
   }
}

Trong mỗi module bạn muốn dùng data binding hãy thêm đoạn code sau Capture2.PNG

apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'

**Khai báo Data Binding **

Data-binding layout có cấu trúc như sau:

<layout>
   <data>
   .......
   </data>
   <ViewRoot>
       <View />
       ......
   </ViewRoot>
</layout>

Trong đó:

Thẻ <layout> là thẻ gốc dưới đó thẻ data và View Root Ví dụ:

<?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="com.nghiatt.demobinding.model.User"
        />
  </data>
  <LinearLayout
      android:gravity="center"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      >
    <TextView
        android:text="@{user.name}"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
  </LinearLayout>
</layout>

Biến user trong data mô tả các thuộc tính hay đối tượng bạn sẽ sử dụng trong layout

<variable
        name="user"
        type="com.nghiatt.demobinding.model.User"
        />

Cú pháp để sử dụng trong attribute properties là " @{} ". Ví dụ:

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

Data Object

Khai báo Object

Cách 1

public class User {
  public String name;
  public boolean isHappy;

  public User(String name, boolean isHappy) {
    this.name = name;
    this.isHappy = isHappy;
  }

}

Cách 2

public class User {
  private String name;
  private boolean isHappy;

  public User(String name) {
    this.name = name;
    this.isHappy = true;
  }

  public User(String name, boolean isHappy) {
    this.name = name;
    this.isHappy = isHappy;
  }

  public boolean isHappy() {
    return isHappy;
  }

  public void setIsHappy(boolean isHappy) {
    this.isHappy = isHappy;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

Sử dụng Data Binding thì 2 cách trên là như nhau. Với expression ở đây @{user.name} được dùng trong đoạn xml dưới đây thì sẽ truy cập vào trường name trong cách 1 và gọi method getName() trong cách 2

 <TextView
       android:text="@{user.name}"
        ......
        />

Binding Data

Theo mặc định, Binding class sẽ được tạo ra dựa theo tên file layout. Tên file layout sẽ được chuyển thành Pascal case và thêm hậu tố "Binding".

Ví dụ: Tên file layout là main_activity.xml sẽ tạo ra class MainActivityBinding. Tương tự activity_main.xml -> ActivityMainBinding

Class Binding này sẽ quản lý các liên kết từ các thuộc tính layout (Ví dụ: biến user) với các thành phần View của layout và biết cách để gán giá trị cho các binding expression (ví dụ: @{user.name}). Khai báo như sau:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        user = new User("User test text");
        binding.setUser(user);
    }

Vậy là xong. Giờ bạn có thể chạy thử để xem kết quả thế nào

Ngoài cách trên bạn có thể get Views bằng cách:

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

Nếu như bạn sử dụng Data Binding Items trong ListView hay RecyclerView Adapter thì bạn sử dụng:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

Binding Events

Một cách đơn giản là bạn có thể sử dung attr android:onClick để chỉ định cho 1 method trong Activity

Bạn có thể xem ví dụ dưới đây để hiểu rõ hơn:

Nếu bạn có 1 object và 2 method

public class MainBindingActivity{
     public void onClickHappy(View view) {
     ...
     }

     public void onClickUnHappy(View view) {
     ...
     }
}

Biểu thức để chỉ định Event cho View:

<?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="com.nghiatt.demobinding.model.User"
            />
        <variable
            name="handlers"
            type="com.nghiatt.demobinding.MainBindingActivity"
            />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        tools:context=".MainActivity"
        >

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

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{user.isHappy ? handlers.onClickHappy : handlers.onClickUnHappy}"
            android:text="Click"
            />
    </LinearLayout>
</layout>

Và dưới đây là khởi tạo:

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setHandlers(this);

Layout Details


Imports

Có thể khai báo nhiều thẻ import trong thẻ data. Nó cho phép tham chiếu đến Classes trong layout giống như JAVA

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

Và đây là biểu thức sử dụng:

<ImageView
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:scaleType="fitCenter"
     android:src="@drawable/happy_face"
     android:visibility="@{user.isHappy ? View.VISIBLE : View.GONE}"
     />

Khi xảy ra trường hợp conflicts, 1 trong số đó sửa tên thành “alias:”:

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

Giờ Vista sẽ tham chiếu đến com.example.real.estate.ViewView sẽ tham chiếu đến android.view.View trong file layout

Hoặc thẻ import để tham chiếu đến static field và static method trong biểu thức

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

Variables

Mỗi 1 thẻ variable sẽ mô tả thuộc tính cho biến đó để có thẻ gán giá trị vào biểu thức.

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

Các class Binding được tạo ra sẽ có method settergetter cho mỗi biến. Các biến sẽ được khởi tạo giá trị mặc định của Java bằng cách gọi thông qua setter

Khi có các file layout cho nhiều cấu hình (Ví dụ landscape hoặc portrait) thì các biến sẽ được kết hợp lại với nhau và không được phép conflict giữa các biến

Custom Binding Class Names

Theo mặc định, Binding class sẽ được tạo ra dựa theo tên file layout. Tên file layout sẽ được chuyển thành Pascal case và thêm hậu tố "Binding". Lớp được sinh ra này sẽ được đặt dưới package của module Nếu module nằm trong com.nghiatt.demobinding thì class Binding sẽ được đặt trong com.nghiatt.demobinding.databinding

Binding class có thể đổi tên hay đặt trong gói khác bằng cách chỉ định trong class attribute trong thẻ data Ví dụ:

<data class="MainBinding">
...
</data>

Class MainBinding sẽ được đặt trong package .databinding trong package module

Ví dụ: Package module là com.nghiatt.demobinding thì MainBinding sẽ được đặt trong com.nghiatt.demobinding.databinding.MainBinding

Nếu muốn đặt cùng với package module thì thêm tiền tố dấu "."

<data class=".MainBinding">
...
</data>

Thì lúc này sẽ là com.nghiatt.demobinding.MainBinding

Còn trong trường hợp muốn chỉ định rõ nằm trong package nào thì code như sau:

<data class="com.example.MainBinding">
    ...
</data>

Để hiểu rõ hơn về bài biết bạn có thể xem example dưới đây: device-2015-10-28-021534.png

Xem source code tại đây GitHub

Tiếp theo

Trong phần tiếp theo tôi sẽ đi sâu hơn về Data Binding Libray và sẽ có ví dụ kết hợp với ListView, GridView, RecyclerView, ... thể hiện được sức mạnh của library mới này.

Tài liệu tham khảo