前言
這篇文章主要講解JetPack中的DataBinding組件的使用。
簡單實用
解決問題:當界面被改寫的時候,Activity中的控件代碼也必須被改寫。爲了避免這個問題的發生,使用了databinding。
- Build.gradle配置項目使用dataBinding功能
defaultConfig{
...
dataBinding{
enabled true
}
...
}
- 將佈局文件轉換成dataBinding適配的佈局文件
- 反向綁定(數據回綁到界面上)
- 在xml中的標籤聲明變化的數據。
- 通過@{}綁定變量和函數@{()->函數名稱}
<layout>
<data>
<!-- 聲明變量
name:變量名稱
type:變量類型-->
<variable
name="jetViewModel"
type="com.martin.jetmo.jetpack.JetViewModel" />
<!-- 導包-->
<import type="com.dustess.baselib.common.assist.Check" />
</data>
<!--
使用時需要將文件語法
字符串:@{字符串內容},在引用時變量時,必須要有getXXX的函數,否則編譯報錯。
調用函數:@{()->調用的函數}
-->
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(jetViewModel.likeNumber)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.196" />
<Button
android:id="@+id/button"
android:layout_width="87dp"
android:layout_height="36dp"
android:text="+1"
android:onClick="@{()->jetViewModel.add()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<layout>
- 正向綁定
setContentView
改寫爲dataBindingUtil.setContentView(R.layout.xxxx)
返回一個AndroidDataBinding類型,這裏變量起爲databinding。findViewbyId()
被替換爲databinding.textView(AndroidDataBinding類型.id名稱)。- 在Activitiy中使用databinding.setData()和databinding.setLifecyclerOwner()開啓雙向綁定。
代碼示例
public class JetPackActivity extends AppCompatActivity {
/**
* 創建JetModel
*/
private JetViewModel mJetViewModel;
/**
* 當將佈局自動轉換爲databinding的xml就會自動創建此類
*/
private ActivityMainBinding mainBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mJetViewModel = ViewModelProviders.of(this).get(JetViewModel.class);
//根據在佈局中聲明變量自動生成
mainBinding.setJetViewModel(mJetViewModel);
mainBinding.setLifecycleOwner(this);
}
}
雙向綁定
官網中《雙向綁定》文章中提到。若用戶在使用EditText
輸入文本時,通過dataBinding
可以將直接將監聽器直接在XML中進行綁定。
AppCompatEditText
<layout>
<data>
<variable
name="afterChangeListener"
type="androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged" />
</data>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/idSearchEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
android:afterTextChanged="@={afterChangeListener}"/>
</layout>
@={} 表示法接收屬性的數據更改並同時監聽用戶更新,其中重要的是包含“=”符號。
上面的TextViewBindingAdapter爲Android中內置的。
接下來我們來看下代碼的編寫。
//將xml申明的afterChangeListener對象在代碼中進行復制。
binding.afterChangeListener = TextViewBindingAdapter.AfterTextChanged {
//do something....
}
這樣就達到了,我們想要的效果了。
DataBindingUtil的常用的代碼片段
在項目中我們會集成一些常用的代碼片段,提升開發效率:
BaseBindingActivity
abstract class BaseBindingMVPActivity<T : ViewDataBinding> : BaseInputMVPActivity() {
lateinit var binding: T
override fun getLayoutId(): Int {
binding = DataBindingUtil.setContentView(this, bindingLayoutId) as T
return 0
}
abstract val bindingLayoutId: Int
}
BaseBindingFragment
abstract class BaseBindingMVPFragment<TDB : ViewDataBinding> : BaseMVPLoadFragment(){
lateinit var binding: TDB
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false)
return binding.root
}
}
BaseBindingViewHolder
class BaseBindViewHolder(view: View) : BaseViewHolder(view) {
var binding: ViewDataBinding? = null
var presenter: BasePresenter<*>? = null
init {
try {
binding = DataBindingUtil.bind(view)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
佈局綁定資源字符串的方法
關鍵方法@{@string/bu_cost_learning_time(time)}
使用用例
資源字符串
<string name="bu_cost_learning_time">累計學習時長:%1$s</string>
資源佈局
<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="time"
type="com.lang.String" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@{@string/bu_cost_learning_time(time)}"
android:textColor="@color/textColor595959"
android:textSize="13sp"
app:layout_constraintStart_toStartOf="@+id/idDescriptionTv"
app:layout_constraintTop_toBottomOf="@+id/idDescriptionTv"
tools:text="@string/bu_cost_learning_time"/>
</layout>
進階點:佈局控件使用自定義屬性
使用案例1:自定義防止多次點擊屬性app:singleClick
databing可以將自定義屬性,並且使用在對應的佈局中,如:點擊事件我們可以在佈局中導入,並且綁定到按鈕中進行觸發。下面講解使用案例
創建擴展類DataBindingAdapter.kt
/**
* 短時間內響應一次點擊
*/
//定義佈局中引用的屬性名稱
@BindingAdapter("singleClick")
//實現細節
fun bindSingleClick(view: View, onClickListener: View.OnClickListener) {
//OnSingleClickListener:是自己再想買裏面實現的防雙點擊的按鈕
view.setOnClickListener(object : OnSingleClickListener() {
override fun onSingleClick(v: View) {
onClickListener.onClick(v)
}
})
}
在佈局中使用app:singleClick屬性
<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="listener"
type="android.view.View.OnClickListener" />
</data>
<TextView
android:id="@+id/idTitle"
android:layout_width="match_parent"
android:layout_height="@dimen/def_tab_height"
android:background="@color/white"
android:gravity="center_vertical"
android:text="雙點擊測試"
android:textSize="@dimen/font_size_def_title"
android:textStyle="bold"
app:singleClick="@{listener}"
app:layout_constraintTop_toTopOf="parent" />
</layout>
使用案例2:自定義圖片加載屬性app:imageUrl
我們自定義app:imageUrl
屬性,在xml中使用後,可以直接執行bindImageUrl
函數代碼段
擴展工具類:ImageDataBindingAdapter.kt
//自定義屬性:app:imageUrl、app:roundRadius、app:placeImage
@BindingAdapter(value = ["app:imageUrl", "app:roundRadius", "app:placeImage"], requireAll = false)
//具體實現細節
fun bindImageUrl(imageView: ImageView, url: String?, roundRadius: Int = 0, placeImage: Drawable?) {
val drawable = placeImage?:imageView.context.getDrawable(R.drawable.def_place)
GlideUtils.getInstance().loadContextRoundBitmap(imageView.context, Check.checkReplace(url),
imageView, drawable, drawable, DensityUtils.dip2px(imageView.context, roundRadius.toFloat()))
}
使用擴展屬性app:imageUrl
<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="imgUrl"
type="com.lang.String" />
</data>
<ImageView
android:id="@+id/imagUrlTestIv"
android:layout_width="@dimen/dp_36"
android:layout_height="@dimen/dp_36"
android:layout_marginStart="@dimen/dp_8"
app:imageUrl="@{imgUrl}"/>
</layout>