Android Jetpack(2):DataBinding的基本使用
DataBinding 介紹
DataBinding 是谷歌官方釋出的一個框架,顧名思義即為資料繫結,是 MVVM 模式在 Android 上的一種實現,用於降低佈局和邏輯的耦合性,使程式碼邏輯更加清晰。MVVM 相對於 MVP,其實就是將 Presenter 層替換成了 ViewModel 層。DataBinding 能夠省去我們一直以來的 findViewById() 步驟,大量減少 Activity 內的程式碼,資料能夠單向或雙向繫結到 layout 檔案中,有助於防止記憶體洩漏,而且能自動進行空檢測以避免空指標異常
DataBinding入門
1. 啟用 DataBinding
啟用 DataBinding 的方法是在對應 Model 的 build.gradle 檔案里加入以下程式碼,同步後就能引入對 DataBinding 的支援
android {
dataBinding {
enabled = true
}
}
就是這麼簡單,一個簡單的databinding配置之後,就可以開始使用資料繫結了。
2. 佈局檔案:生成 DataBinding 需要的佈局規則
我們來看看佈局檔案該怎麼寫,首先佈局檔案不再是以傳統的某一個容器作為根節點,而是使用layout作為根節點,在layout節點中我們可以通過data節點來引入我們要使用的資料來源。
開啟佈局檔案,選中根佈局的 ViewGroup,按住 Alt + Enter鍵,點選 “Convert to data binding layout”,就可以生成 DataBinding 需要的佈局規則。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
和原始佈局的區別在於多出了一個 layout 標籤將原佈局包裹了起來,data 標籤用於宣告要用到的變數以及變數型別,要實現 MVVM 的 ViewModel 就需要把資料(Model)與 UI(View)進行繫結,data 標籤的作用就像一個橋樑搭建了 View 和 Model 之間的通道。
3. 定義一個實體類
要使用資料繫結,我們得首先建立一個實體類:
data class User(val name:String,val age:Int)
4. 在佈局檔案的data 標籤裡宣告要使用到的變數名、類的全路徑,設定資料
在data中定義的variable節點,name屬性表示變數的名稱,type表示這個變數的型別,例項就是我們實體類的類的全路徑。
這裡宣告瞭一個 User 型別的變數 user,我們要做的就是使這個變數與TextView 控制元件掛鉤,通過設定 user的變數值同時使 TextView 顯示相應的文字。
通過 @{user.name} 使 TextView 引用到相關的變數,DataBinding 會將之對映到相應的 getter 方法。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.example.jetpack.bean.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@{`名字`+user.name}"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!--注意:這裡age是int型別,必須轉化為String,否則會執行時異常-->
<TextView
android:id="@+id/tvAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@{String.valueOf(user.age)}"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvName" />
<TextView
android:id="@+id/tvPhoneNum"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@{user.phoneNum==null?user.phoneNum:`17817318877`}"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvAge" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
5. Activity 中通過 DataBindingUtil 設定佈局檔案
Activity 中通過 DataBindingUtil 設定佈局檔案,省略原先 Activity 的 setContentView() 方法。
每個資料繫結佈局檔案都會生成一個繫結類,ViewDataBinding 的例項名是根據佈局檔名來生成,將之改為首字母大寫的駝峰命名法來命名,並省略佈局檔名包含的下劃線。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding=DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
binding.user= User("趙麗穎",20,"17817318859")
}
}
一個簡單的dataBinding案例就已經完成。
其他用法
在data中定義的variable節點,name屬性表示變數的名稱,type表示這個變數的型別,例項就是我們實體類的類的全路徑。
import
如果 User 型別要多處用到,也可以直接將之 import 進來,這樣就不用每次都指明整個包名路徑了,而 java.lang.* 包中的類會被自動匯入,所以可以直接使用
<data>
<import type="com.example.jetpack.bean.User"/>
<variable
name="user"
type="User" />
</data>
alias
先使用import節點將User匯入,然後直接使用即可。但是如果這樣的話又會有另外一個問題,假如我有兩個類都是User,這兩個UserBean分屬於不同的包中,又該如何?這時候就要用到alias了。
在import節點中還有一個屬性叫做alias,這個屬性表示我可以給該類取一個別名,我給User這個實體類取一個別名叫做Lenve,這樣我就可以在variable節點中直接寫Lenve了。
<data>
<import type="com.example.jetpack.bean.User" alias="Lenve"/>
<variable
name="user"
type="Lenve" />
</data>
設定預設值
由於 TextView在佈局檔案中並沒有明確的值,所以在預覽檢視中什麼都不會顯示,不便於觀察文字的大小和字型顏色等屬性,此時可以為之設定預設值(文字內容或者是字型大小等屬性都適用),預設值將只在預覽檢視中顯示,且預設值不能包含引號。
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@{`名字`+user.name,default=morenzhi}"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
自定義 ViewDataBinding 的例項名
可以通過如下方式自定義 ViewDataBinding 的例項名:
<data class="CustomBinding">
</data>
字串拼接
如activity_main中如下屬性
android:text="@{`名字`+user.name}"
三目運算
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@{user.phone==null?user.phone:`147522444`}"
android:textSize="18sp" />
程式碼中使用控制元件
使用binding物件可獲取佈局檔案中的各個物件,根據控制元件設定的id來獲取。如
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="按鈕"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPhoneNum" />
binding.btn.setOnClickListener {
Toast.makeText(this,"點選了按鈕",Toast.LENGTH_SHORT).show()
}
BindingAdapter
dataBinding 提供了 BindingAdapter 這個註解用於支援自定義屬性,或者是修改原有屬性。註解值可以是已有的 xml 屬性,例如 android:src、android:text等,也可以自定義屬性然後在 xml 中使用。
對於一個 ImageView ,我們希望在某個變數值發生變化時,可以動態改變顯示的圖片,此時就可以通過 BindingAdapter 來實現。
通過 BindingAdapter 來實現載入圖片
在java中使用:
在java中使用dataBinding展示圖片很簡單,只需要配置一個靜態的BindingAdapter就可以了。
需要先定義一個靜態方法,為之新增 BindingAdapter 註解,註解值是為 ImageView 控制元件自定義的屬性名,而該靜態方法的兩個引數可以這樣來理解:
當 ImageView 控制元件的 url 屬性值發生變化時,dataBinding 就會將 ImageView 例項以及新的 url 值傳遞給 loadImage() 方法,從而可以在此動態改變 ImageView 的相關屬性。
public class DataBindingUtils {
@BindingAdapter("imageUrl") //imageUrl:控制元件的屬性名
public static void loadImage(ImageView imageView, String url) {
Glide.with(imageView.getContext())
.load(url)
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.into(imageView);
}
}
在kotlin中使用:
首先:kotlin中沒有static關鍵字,但是提供了companion object{}程式碼塊和使用object關鍵字。
object關鍵字宣告一種特殊的類,這個類只有一個例項,因此看起來整個類就好像是一個物件一樣,這裡把類宣告時的class關鍵字改成了object,這個類裡面的成員預設都是static的。
@JvmStatic註解:與伴生物件搭配使用,將變數和函式宣告為真正的JVM靜態成員。
要加上kapt外掛:
apply plugin: 'kotlin-kapt'
第一種方式:使用 companion object
class DataBindingUtils {
companion object {
@BindingAdapter("imageUrl")
@JvmStatic
fun loadImage(view: ImageView, url: String) {
Glide.with(view.context).load(url).into(view)
}
}
}
第二種方式:使用object
object DataBindingUtils {
@BindingAdapter("imageUrl")
@JvmStatic
fun loadImage(view: ImageView, url: String) {
Glide.with(view.context).load(url).into(view)
}
}
val url="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3796319594,2802761532&fm=26&gp=0.jpg"
binding.user= User("趙麗穎",20,"17817318859",url)
在 xml 檔案中關聯變數值,當中, app 這個名稱可以自定義:
<ImageView
android:id="@+id/ivNet"
android:layout_width="match_parent"
android:layout_height="300dp"
app:imageUrl="@{user.url}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn" />
BindingAdapter 更為強大的一點是可以覆蓋 Android 原先的控制元件屬性
例如,可以設定每一個 Button 的文字都要加上字尾:“-Button”
@BindingAdapter("android:text")
public static void setText(TextView view, String text) {
view.setText(text + "趙麗穎");
}
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@{`名字`+user.name,default=morenzhi}"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
這樣,整個工程中使用到了 “android:text” 這個屬性的控制元件,其顯示的文字就會多出一個字尾-趙麗穎。
可以用這個屬性來載入本地圖片:
public class ImageViewAdapter {
@BindingAdapter("android:src")
public static void setSrc(ImageView view, Bitmap bitmap) {
view.setImageBitmap(bitmap);
}
@BindingAdapter("android:src")
public static void setSrc(ImageView view, int resId) {
view.setImageResource(resId);
}
@BindingAdapter("imageUrl")
public static void setSrc(ImageView imageView, String url) {
Glide.with(imageView.getContext()).load(url)
.placeholder(R.mipmap.ic_launcher)
.into(imageView);
}
@BindingAdapter({"app:imageUrl", "app:placeHolder", "app:error"})
public static void loadImage(ImageView imageView, String url, Drawable holderDrawable, Drawable errorDrawable) {
Glide.with(imageView.getContext())
.load(url)
.placeholder(holderDrawable)
.error(errorDrawable)
.into(imageView);
}
}
Databinding 同樣是支援在 Fragment
class DemoFrgament : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = DataBindingUtil.inflate<FragmentDemoBinding>(
inflater,
R.layout.fragment_demo,
container,
false
)
return binding.root
}
}
DataBinding事件繫結
嚴格意義上來說,事件繫結也是一種變數繫結,只不過設定的變數是回撥介面而已,事件繫結可用於以下多種回撥事件:
- android:onClick
- android:onLongClick
- android:afterTextChanged
- android:onTextChanged
- …
Databinding事件繫結,分兩種方式:方法引用和監聽繫結,下面分別用案例介紹兩種事件繫結的異同。
方式1:直接獲取控制元件設定點選事件
binding.btn1.setOnClickListener {
Toast.makeText(this,"點選了按鈕1",Toast.LENGTH_SHORT).show()
}
方式2:方法引用
傳入OnClickListener的變數:
<variable
name="listener"
type="android.view.View.OnClickListener" />
在Button中給android:onClick設定listener的變數:
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{listener}"
android:text="按鈕"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPhoneNum" />
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{listener}"
android:text="按鈕"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn1" />
通過setListener傳入點選監聽給listener物件:
binding.setListener {
when (it.id) {
R.id.btn1 -> Toast.makeText(this, "點選了按鈕1", Toast.LENGTH_SHORT).show()
R.id.btn2 -> Toast.makeText(this, "點選了按鈕2", Toast.LENGTH_SHORT).show()
}
}
方式3:方法引用
<variable
name="handlers"
type="com.example.jetpack.MainActivity" />
呼叫語法可以是@{handlers::onClickFriend}或者@{handlers.onClickFriend}:
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{handlers::onClickFriend}"
android:text="按鈕"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPhoneNum" />
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{handlers::onClickFriend}"
android:text="按鈕"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn1" />
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val url =
"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3796319594,2802761532&fm=26&gp=0.jpg"
binding.user = User("趙麗穎", 20, "17817318859", url)
binding.handlers = this
}
fun onClickFriend(view: View) {
when (view.id) {
R.id.btn1 -> Toast.makeText(this, "點選了按鈕1", Toast.LENGTH_SHORT).show()
R.id.btn2 -> Toast.makeText(this, "點選了按鈕2", Toast.LENGTH_SHORT).show()
}
}
}
方式4:方法引用
<data>
<variable
name="user"
type="com.example.jetpack.bean.User" />
<variable
name="handlers"
type="com.example.jetpack.MainActivity.MyClickHandlers" />
</data>
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="改變name屬性"
android:onClick="@{handlers.onClickChangeName}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPhoneNum" />
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{handlers::onClickChangAage}"
android:text="改變age屬性"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn1" />
在 Activity 內部新建一個類來宣告 onClickChangeName() 和 onClickChangAage() 事件相應的回撥方法:
class MainActivity : AppCompatActivity() {
lateinit var user: User
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val url =
"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3796319594,2802761532&fm=26&gp=0.jpg"
user = User("趙麗穎", 20, "17817318859", url)
binding.user = user
binding.handlers = MyClickHandlers()
}
inner class MyClickHandlers {
fun onClickChangeName(v: View?) {
user.name = "趙麗穎2"
binding.user=user
}
fun onClickChangAage(v: View?) {
user.age = 18
binding.user=user
}
}
}
方式5:方法引用
把回撥方法單獨寫到一個介面。
<variable
name="handlers"
type="com.example.jetpack.UserClickListener" />
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="按鈕"
android:onClick="@{handlers.userClicked}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPhoneNum" />
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{handlers::userClicked}"
android:text="按鈕"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn1" />
interface UserClickListener {
fun userClicked(view: View?)
}
class MainActivity : AppCompatActivity(), UserClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val url =
"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3796319594,2802761532&fm=26&gp=0.jpg"
val user = User("趙麗穎", 20, "17817318859", url)
binding.user = user
binding.handlers = this
}
override fun userClicked(view: View?) {
Toast.makeText(this, "方法引用",Toast.LENGTH_SHORT).show();
}
}
方式6:監聽繫結(重要)
將物件直接傳回點選方法中。
<variable
name="handlers"
type="com.example.jetpack.MainActivity.MyClickHandlers" />
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{()->handlers.showUser(user)}"
android:text="按鈕"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPhoneNum" />
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val url =
"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3796319594,2802761532&fm=26&gp=0.jpg"
var user = User("趙麗穎", 20, "17817318859", url)
binding.user = user
binding.handlers = MyClickHandlers()
}
inner class MyClickHandlers {
fun showUser(user: User) {
Toast.makeText(this@MainActivity, user.name, Toast.LENGTH_SHORT).show()
}
}
}
相關文章
- Android Jetpack - DataBindingAndroidJetpack
- Android Jetpack Navigation基本使用AndroidJetpackNavigation
- Android JetPack~ DataBinding(資料繫結)(一) 整合與使用AndroidJetpack
- Android架構元件-DataBinding的使用Android架構元件
- JetPack 學習筆記:Databinding 與響應式Jetpack筆記
- Android DataBinding 從入門到進階(2)Android
- Android:DataBinding的一二事Android
- Android開發教程-使用DataBinding(六)RecyclerViewAdapter中的使用AndroidViewAPT
- Jetpack Compose學習(2)——文字(Text)的使用Jetpack
- Android_Jetpack:Paging元件之BoundaryCallback的使用AndroidJetpack元件
- Android Studio: Kotlin使用DataBinding異常AndroidKotlin
- MVVM模式--DataBinding的使用MVVM模式
- Android Jetpack – 使用 WorkManager 管理後臺任務AndroidJetpack
- Android JetPack~ LiveData (一) 介紹與使用AndroidJetpackLiveData
- Android JetPack~ ViewModel (一) 介紹與使用AndroidJetpackView
- Android Jetpack - 使用 WorkManager 管理後臺任務AndroidJetpack
- Android Button的基本使用Android
- DataBinding資料繫結基本講解
- Android DataBinding之初體驗Android
- Android Jetpack - 使用 Navigation 管理頁面跳轉AndroidJetpackNavigation
- Android Jetpack 之Navigation Architecture Component使用AndroidJetpackNavigation
- Android Jetpack 之 LiveDataAndroidJetpackLiveData
- Android Jetpack 之 LifecycleAndroidJetpack
- Android Jetpack 之 ViewModelAndroidJetpackView
- Android Jetpack之ViewModelAndroidJetpackView
- Android開發教程-使用DataBinding(五)資料繫結Android
- Android JNI的基本使用(CMake)Android
- Android Jetpack元件 - ViewModel,LiveData使用以及原理AndroidJetpack元件ViewLiveData
- Android PopUpWindow基本使用Android
- Struts2框架的基本使用框架
- Android Jetpack - Fragment官方說明AndroidJetpackFragment
- DataBinding基礎使用一
- DataBinding基礎使用二
- DataBinding基礎使用三
- 怎麼學習谷歌最新的元件--Jetpack?一文介紹paging的基本使用谷歌元件Jetpack
- DataBinding繫結android:onClick出錯Android
- 2章 RxJava基本使用RxJava
- Android Volley 基本使用Android