Android Jetpack DataBinding入門填坑(三)

Jetpack系列

Android Jetpack WorkManager初級認識

Android Jetpack ViewModel由淺入深

附上官網地址https://developer.android.com/topic/libraries/data-binding/expressions

一、DataBinding的定義及作用

是一個支持庫,使用該庫,您可以使用聲明性格式而非編程方式將佈局中的UI組件綁定到應用程序中的數據源。不需要在使用findViewById之類的,佈局文件也不再簡單的是一個佈局文件,可以包含數據以及邏輯。

配置必要信息

Build.gradle文件添加下面信息

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

二、基本使用

#####1. 簡單展示綁定過程

  1. 新建activity_databinding.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
  // 定義數據的標籤
    <data>
        <variable
            name="title"
            type="String" />
    </data>
 // 定義佈局的標籤
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="@{title}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

上面的格式:<layout></layout>包含兩個子標籤,

  1. <data></data>定義我們的數據的地方,
  2. <ConstraintLayout></ConstraintLayout>,這個佈局就相當於我們平時的佈局。
    TextViewtext綁定了android:text="@{title}",如果第一次設置title數據變化,頁面就會跟着變化。

這裏解釋一下<variable/>標籤中的屬性,
name是定義的變量名
type是變量的類型

  1. 新建Acitivity綁定佈局
class DatabasingActivity : AppCompatActivity() , View.OnClickListener {


    lateinit var mactivityBinding: ActivityDatabindingBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //綁定佈局
        mactivityBinding = DataBindingUtil.setContentView(this,R.layout.activity_databinding)
        mactivityBinding.title = "基礎使用"
    }
}

上面的ActivityDatabindingBinding是系統自動幫我們生成的,使用DataBindingUtil.setContentView(this,R.layout.activity_databinding)便可以綁定好佈局,(不需要像以前調用setContentView()),mactivityBinding.title = "基礎使用"賦值給title,textView就會自動展示出數據了。

注意點:

  1. ActivityDatabindingBinding是根據你的佈局.xml去生成的 這裏的是activity_databinding.xml所以生成ActivityDatabindingBinding.
  2. 如果發現databinding生成的ActivityDatabindingBindingImpl(看自己的命名)中出現路徑報錯,那麼最大的可能性是你的.xml文件裏面寫錯了之類的!!!

下面看一下.xml文件支持什麼表達式。

三、支持的表達式:

  • 數學計算符 + - / * %
  • 字符連接符號 +
  • 邏輯運行符號 && ||
  • 二元運行符號 & | ^
  • 一元運行符號 + - ! ~
  • 移位 >> >>> <<
  • 比較符號 == > < >= <=(請注意,<必須轉義爲&lt;)
  • instanceof
  • Grouping ()
  • character, String, numeric, null
  • cast
  • 方法調用
  • 現場訪問
  • Array存取 []
  • 三元運算符 ?:

四、 不支持的表達式:

  • this
  • super
  • new
  • explicit generic invocation (顯式泛型調用)

五、 設置點擊事件跟導入數據類

  1. 新建數據實體類
data class Person(var Name:String,var Age:Int)
  1. 修改一下佈局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
      //關鍵點1
        <import type="testview.zhen.com.myapplication.bean.Person"/>
       //關鍵點2
        <variable
            name="peronInfo"
            type="Person" />
     //關鍵點3
        <variable
            name="onClickListener"
            type="android.view.View.OnClickListener" />
        <variable
            name="title"
            type="String" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="172dp"
        android:text="@{peronInfo.name,default=預覽}"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="-16dp" />
//關鍵點4
    <TextView
        android:id="@+id/textView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{String.valueOf(peronInfo.age),default=預覽}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_view" />
//關鍵點4
    <Button
        android:id="@+id/button7"
        android:layout_width="159dp"
        android:layout_height="46dp"
        android:layout_marginTop="56dp"
        android:onClick="@{onClickListener}"
        android:text="點擊1"
        app:layout_constraintEnd_toStartOf="@+id/button8"
        app:layout_constraintHorizontal_bias="0.451"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

    <Button
        android:id="@+id/button8"
        android:layout_width="159dp"
        android:layout_height="46dp"
        android:layout_marginTop="56dp"
        android:layout_marginEnd="36dp"
        android:onClick="@{onClickListener}"
        android:text="點擊2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

關鍵代碼1:<import type="testview.zhen.com.myapplication.bean.Person"/> 導入數據類所在的路徑包名,要導入後才能使用
關鍵代碼2:定義Person類型的變量
關鍵代碼3:定義android.view.View.OnClickListenerView的點擊事件
關鍵代碼4:在Buttonandroid:onClick="@{onClickListener}中引用關鍵代碼3定義的點擊事件
3 修改一下Activity文件

class DatabasingActivity : AppCompatActivity() , View.OnClickListener {


    lateinit var mactivityBinding: ActivityDatabindingBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //綁定佈局
        mactivityBinding = DataBindingUtil.setContentView(this,R.layout.activity_databinding)
        //賦值給佈局中定義的<data>中的面的peronInfo
        mactivityBinding.peronInfo = Person("man",18)
        //實現佈局中定義的<data>中的onClickListener變量,這裏傳入this,記得要實現接口
        mactivityBinding.onClickListener = this
    }
    //當點擊按鈕的時候就會自動調用到這裏了
    override fun onClick(v: View?) {
        when(v){
            mactivityBinding.button7 -> {
                mactivityBinding.textView.text = "這個是點擊button7產生的"
            }
            mactivityBinding.button8 ->{
                mactivityBinding.textView2.text = "這個是點擊button8產生的"
            }
        }
    }
}

直接看上面註釋就知道用法了

上面的註釋看懂就可以了,Activity中基本的DataBinding你就算是掌握了,關於點擊事件的還有很多種寫法,可能需要重新寫一篇詳細的說一下其它的細節吧。

六、在Fragment中使用

    lateinit var fragmentTestBinding:FragmentTestBindingBinding
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        fragmentTestBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_test_binding,container,false)
        return fragmentTestBinding.root
    }

Activity對比Fragment這裏就是DataBindingUtil.inflate()傳參不同,然後返回fragmentTestBinding.root。其它的操作都是一樣的

七、單向綁定數據(可觀察的數據對象)

最開始使用的時候我很不理解爲什麼還有個單向綁定。因爲我看到基本類型的數據,在xmldata定義 然後再Activity中更改,頁面會跟着一起刷新,這不就是相當於單向綁定了嗎???直到後面我在<data>節點中import入自定義的數據類bean,然後再在Activity中更改bean中的成員變量時,頁面並沒有改變。這時候我才懂了這個存在的原因。(ps:這個只是個人剛接觸的時候想法,新手有可能也有會這樣想法,我這裏先說一下)

官網定義:任何普通的舊對象都可以用於數據綁定,但是修改對象不會自動導致UI更新。數據綁定可用於使您的數據對象在數據更改時通知其他對象(稱爲偵聽器)。有三種不同類型的可觀察類: 對象,字段和集合

實現的基本方法有以下幾種

1. 直接定義可監聽變量
  • ObservableBoolean
  • ObservableByte
  • ObservableChar
  • ObservableShort
  • ObservableInt
  • ObservableLong
  • ObservableFloat
  • ObservableDouble
  • ObservableParcelable
//定義的實體bean時候
class Person {
    val Name = ObservableField<String>()
    val Age = ObservableInt()
}
//(kotlin 語法 )調用實體bean的時候
user.firstName = "Google"
val age = person.Age 
--------------------------------------------------------
//(java 語法 )調用實體bean的時候
person.firstName.set("Man");
int age = person.Age.get();

使用的時候記得使用get(),set()方法。(ps:kotlin語法看來起來是直接調用的,但實際上裏面也是實現了get()set()方法)

//可監聽變量數組類型

  • ObservableArrayMap<>()
  • ObservableArrayList<>()

----------定義xml文件-----------------------
<data>
        <import type="androidx.databinding.ObservableArrayList"/>
        <variable
            name="list"
            type="ObservableArrayList&lt;String>" />
</data>
…

<TextView
    android:text='@{list[0],defualt = "hello"}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
}
------------使用的時候----------------------------

ObservableArrayList<String>().apply {
    add("man")
}

用法跟基本類型一致,但是有兩點要注意

  1. 記得在<data>import入對應的數組的包名所在路徑
  2. 引用這些集合類型的時候,<要改爲\&lt; (ps:Android stuido會提醒你這樣子操作的 )
2. ObservableField,這個跟上面那些用法一樣,泛型傳入指定類型。
3. 繼承 BaseObservable()
-------------------------------kotlin寫法-----------------------------
class User :BaseObservable(){
    @get:Bindable
    var name:String = ""
    set(value) {
        field = value
        notifyPropertyChanged(BR.name)
    }
    @get:Bindable
    var age:Int = 0
    set(value) {
        field = value
        notifyPropertyChanged(BR.age)
    }
}
--------------------------------java寫法---------------------------------
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);
    }
}

實體類繼承BaseObservable()

  1. 在對應的字段get方法錢前面加上註解@Bindable ,kotlin@get:Bindable
  2. set方法中修改數據後調用notifyPropertyChanged(BR.name)通知更改數據
  3. 如果數據類的基類不能被改變,可觀察到的界面可以實現使用PropertyChangeRegistry對象註冊和通知監聽器。(補充)

這裏有個坑!!! BR總是報錯 找不到nameage字段,後來百度需要在對應module項目下的build.gradle上面加上插件

apply plugin: 'kotlin-kapt'

八、雙向綁定數據

在xml文件中 單向綁定我們是用@{變量名},雙向綁定則是@={變量名}
最常見的需求就是我們的頁面輸入了。輸入的數據要在代碼中可以讓我們接收到。

 <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@{title,default =標題}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="28dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="@={title}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView3" />

editText數據數據的時候android:text="@={title}"雙向綁定數據,TextView中綁定的 android:text="@{title,default =標題}"也會跟着一起變化。

九、在include中使用

在include中:
先定義include_text.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="testview.zhen.com.myapplication.bean.Person"/>
        <variable
            name="peronInfo"
            type="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:text="@{`嵌套佈局 用戶名:`+peronInfo.name}"
        />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{`嵌套佈局 年齡:`+peronInfo.age}"
        />
</LinearLayout>
</layout>

在佈局中引入

<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="peronInfo"
            type="Person" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <include
        layout="@layout/inclue_text"
        app:peronInfo = "@{peronInfo}"
        />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

include標籤中app:peronInfo將數據傳入,這裏的peronInfo是在include佈局中我們自己定義的數據。

十、在viewStub中使用

因爲viewStub是屬於懶加載的佈局,如果不調用inflate,就不會加載。屬於安卓上面也是頁面優化有一個範疇。

先定義一個viewStub佈局頁面

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="testview.zhen.com.myapplication.bean.Person"/>
        <variable
            name="peronInfo"
            type="Person" />
    </data>
<LinearLayout
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{peronInfo.name + peronInfo.age}"
        />
</LinearLayout>
</layout>

在佈局中引入

<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="peronInfo"
            type="Person" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <ViewStub
            android:id="@+id/view_stub"
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:layout="@layout/viewstub_layout"
            app:peronInfo = "@{peronInfo}"
            />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

以上的操作跟include的用法一致,不同點在於Activity中的綁定
Activity

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //綁定佈局
        mactivityBinding = DataBindingUtil.setContentView(this,R.layout.activity_databinding)
        o.add(0,"第一數據")
        mactivityBinding.peronInfo = Person("man",18,o)
        mactivityBinding.viewStub.viewStub!!.inflate()
    }

關鍵代碼mactivityBinding.viewStub.viewStub!!.inflate()這裏就是加載viewStub的地方。

十一、在Adapter中的使用

簡化統一可以使用的ViewHolder

public class BaseViewHolder extends RecyclerView.ViewHolder {
    public BaseDataBinding mbinding;

    public BaseViewHolder(BaseDataBinding binding) {
        super(binding.getRoot());
        mbinding = binding;
    }
}

你可以使用上面的ViewHolder就可以不用自己總是去寫ViewHolder了。

val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)

AdapteronCreateViewHolder中可以使用上面的方法綁定佈局

關於AdapterDataBinding使用的網上有很多文章大家可以自行查閱

這篇文章主要是記錄一下自己學習的過程,很多細節化跟封裝的話以後再出一篇文章另外記錄,有問題的也請同學們在評論區留言,希望看的同學們覺得有用的話給個贊~ 謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章