JetPack之DataBinding的使用

前言

這篇文章主要講解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>

持續更新中…

推薦文章

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