Android DataBinding的簡單使用

 官方文檔地址:https://developer.android.google.cn/topic/libraries/data-binding       

        先說說開發環境,本人使用的Android Studio 版本爲3.5,gradle版本是5.4.1,在此之下的版本不確保能用。

        要使用DataBinding需要在項目對應的gradle文件中添加以下的設置

android {
    ........
    //介紹網址https://developer.android.google.cn/jetpack/androidx/releases/databinding
    dataBinding {
        enabled = true
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'//包含大部分androidx的類
}

        創建數據實體類User.java和類型轉換工具類Converter.java,看不懂這兩個類沒關係,後面會用到,看完後面再回頭看這兩個類的作用。(ViewModel的介紹可以看我另外一篇文章Android ViewModel的使用

import androidx.databinding.ObservableField;
import androidx.lifecycle.ViewModel;

public class User extends ViewModel {
    public String name;
    public int age;
    public final ObservableField<String> observableName = new ObservableField<>();//可觀察的數據對象
    public final ObservableField<Integer> observableAge = new ObservableField<>();
    public User(){
        name = "name";
        age = 18;
        observableName.set("observableName");
        observableAge.set(8);
    }
}
import android.util.Log;
import androidx.databinding.InverseMethod;
/**
 * 數據類型裝換工具類
 * 每種類型裝換必須有兩個方法相互對應
 * 有@InverseMethod註解的是在佈局文件中雙向綁定時可以調用的方法
 * 沒有@InverseMethod註解的方法看起來沒有用到,但實際在雙向綁定時會被自動調用
 */
public class Converter {
    @InverseMethod("stringToInt")//對應下面stringToInt方法
    public static String intToString(int value) {
        Log.d("JohnLiu","intToString:"+ value);
        return  String.valueOf(value);
    }

    public static int stringToInt(String value) {
        Log.d("JohnLiu","stringToInt:"+ value);
        if (Long.valueOf(value) > Integer.MAX_VALUE){
            throw new RuntimeException("數值超出");
        }
        return  Integer.valueOf(value);
    }
}

  Activity和layout文件如下(tips,如果遇到項目編譯失敗,檢查一下Activity文件所在的文件目錄是否符合規範,文件夾名稱爲小寫。還有一種情況,通過File-》Invalidate Caches/Restart 清緩存並重啓)。

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

public class DataBindingActivity extends AppCompatActivity {
    //系統會爲每個佈局文件生成一個綁定類。默認情況下,類名稱基於佈局文件的名稱,它會轉換爲 Pascal 大小寫形式並在末尾添加 Binding 後綴。
    // 本Activity佈局文件名爲 activity_data_binding,因此生成的對應類爲 ActivityDataBindingBinding
    private ActivityDataBindingBinding mBinding;
    private User user;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        user = new User();//等價於user = ViewModelUtils.getPrivateViewModel(this,User.class,this);
//        user = ViewModelUtils.getViewModel(this,User.class);//全局唯一,即在app退出之前,下次重新進來這個頁面會加載上一次的數據
        //DataBindingUtil輸入的時候AS有可能不會智能提示,需要手動導入一下androidx.databinding.DataBindingUtil
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_data_binding);
        //根據綁定的layout中的variable標籤裏name設置的值變化,比如我設置的是data,這裏方法就是setData,如果設置成user,這裏就是setUser,如此類推
        mBinding.setData(user);
        findViewById(R.id.bt_set).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //值變化,但屏幕沒刷新,需要手動調用一次 mBinding.setData(user);
                user.name="JohnLiu";
                user.age =  20;
                //只要值變化,數據就會同步顯示到屏幕上
                user.observableName.set("JohnLiu");
                user.observableAge.set(20);
            }
        });
        findViewById(R.id.bt_refresh).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //調用一次 mBinding.setData(user);後可以看到前兩行的值也刷新了
                mBinding.setData(user);
            }
        });

        findViewById(R.id.bt_toast).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String msg = "name:" + user.name + "\n"
                        + "age:" + user.age + "\n"
                        + "observableName:" + user.observableName.get() + "\n"
                        + "observableAge:" + user.observableAge.get();
                //顯示當前實體類中所有變量的值
                Toast.makeText(DataBindingActivity.this,msg,Toast.LENGTH_SHORT).show();
            }
        });

    }
}

其中activity_data_binding.xml的內容如下

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="com.jetpackdemo.databinding.Converter"/>
        <!--假如要導入兩個同名的類,可以通過alias設置別名-->
        <!-- <import type="com.jetpackdemo.Converter" alias="Converter2"/>-->
        
        <!--data實體類,type字段的值是User類的具體路徑-->
        <variable
            name="data"
            type="com.jetpackdemo.databinding.User" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/layout"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="賦值"
                android:id="@+id/bt_set"/>
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="刷新"
                android:id="@+id/bt_refresh"/>
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="當前值"

                android:id="@+id/bt_toast"/>
        </LinearLayout>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.name}"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(data.age)}"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="單向綁定,修改實體類的值,實時刷新屏幕顯示的值"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.observableName}"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(data.observableAge)}"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="雙向綁定,在輸入框修改值也會自動更新實體類的值"/>
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@={data.observableName}"
            />
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="number"
            android:text="@={Converter.intToString(data.observableAge)}"
            />
    </LinearLayout>

</layout>

剛啓動的頁面如下:

點擊賦值&當前值,可以看到雖然實體類中的值變了,但是前兩行文本沒有同步刷新

                

點擊刷新,可以看到前兩行文本也刷新了,這就是用了可觀察對象ObservableField與沒用的區別。ObservableField會監聽值的變化,實時刷新屏幕。

修改最後兩行的文本,可以看到修改視圖中的文本,同時會修改實體類中相應的可觀察對象的值,實現數據雙向綁定。是否實現雙向綁定,區別在於 android:text="@={Converter.intToString(data.observableAge)}" 裏面的“@”後面有無“=”,如果沒有“=”,修改實體類中的值,會刷新視圖,但修改視圖,不會同時修改實體類的值。

 

小結

        從效果看來,使用DataBinding好像減少很多代碼,大部分的取/賦值操作自動完成 ,不需要關注值的變化去更新視圖。但實際業務大多數要對數據的格式或取值進行校驗並做一些處理,並不僅僅只是把值賦給實體對象。還有,可以看到DataBinding的賦值邏輯是寫在XML的佈局文件上的,但實際上有時候我們需要動態地往佈局添加一些組件,這個時候又怎麼處理呢?或許只是我對它的瞭解還不夠深吧,但目前它給我的感覺是比較難運用到實際業務當中去。

發佈了111 篇原創文章 · 獲贊 21 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章