android databinding 的三種自定義屬性方式

1、概述

通過半年的使用發現,databinding 有三種自定義屬性的方式。分別是:

  • 1、xml 引入自定義屬性標籤。代碼通過 @BindingAdapter 註解完成綁定
  • 2、直接在代碼裏使用:@BindingMethods 註解引入自定義屬性,再用@BindingAdapter 註解完成綁定
  • 3、自定義控件屬性時,提供set的方法 可以自動完成支持 databinding 屬性。 屬性名字=控件的自定義屬性名字 (所以有時候,發現沒有特定使用@BindingAdapter 也支持了 databinding)

2、實例

2.1、xml 引入自定義屬性標籤。代碼通過 @BindingAdapter 註解完成綁定

xml 文件:

<declare-styleable name="AdapterView">
  <!--只能設置在 checkbox 中-->
  <attr name="setCheckValue" format="boolean"/>
</declare-styleable>

代碼文件:

public class ViewAdapter {
    @BindingAdapter({"setCheckValue"})
    public static void setCheckValue(final CheckBox checkBox, final ObservableField<Boolean> tags){
        tags.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable sender, int propertyId) {
                checkBox.setChecked(tags.get());
            }
        });
    }
}

注意: xml 裏定義attr name的時候, 如果沒有加上 format 標籤, 在xml佈局中使用的時候沒法跳轉到 自定義的這個屬性。加了纔可以跳轉

這裏代碼使用的java的代碼, java代碼對應是static 靜態函數。 對應kotlin代碼 要用靜態的方式 有幾種方式:

2.1.1、比如最外層用kotlin的單例寫, 方法前加 @JvmStatic 註解:

比如 :

object LinearLayoutViewAdapter {
 
    @JvmStatic
    @Suppress("UNCHECKED_CAST")
    @BindingAdapter(value = ["itemBinding", "items"], requireAll = false)
    fun <T> setAdapter(linearLayout: LinearLayout,
                       itemBinding: ItemBinding<T>?,
                       items: List<T>?) {
        requireNotNull(itemBinding) { "itemBinding == null" }
        val oldAdapter: BindingLinearLayoutAdapter<T>? = linearLayout.getAdapter() as? BindingLinearLayoutAdapter<T>
        val adapter = oldAdapter ?: BindingLinearLayoutAdapter()
        adapter.itemBinding = itemBinding
        adapter.setItems(items)
        if (adapter != oldAdapter) {
            linearLayout.setAdapter(adapter)
        }
    }
}
2.1.2、用 kotin的擴展函數來寫:

比如 下文的代碼

@BindingAdapter(value = ["labelCheckChecked"], requireAll = false)
fun LabelCheckBoxView.setEnable(labelCheckChecked: Boolean) {
    this.getCheckBox().isChecked = labelCheckChecked
}
2.1.3、kotlin 還有一種寫靜態的方法,是以函數式編程的方式,不套類,直接寫一個函數。

比如:谷歌官網的demo寫法

@BindingAdapter("android:paddingLeft")
    fun setPaddingLeft(view: View, padding: Int) {
        view.setPadding(padding,
                    view.getPaddingTop(),
                    view.getPaddingRight(),
                    view.getPaddingBottom())
    }

2.2、@BindingMethods + @BindingAdapter 的方式

不需要在xml中寫屬性,直接用代碼。使用的時候 也會提示補全

@RestrictTo(RestrictTo.Scope.LIBRARY)
@BindingMethods(BindingMethod(type = LabelCheckBoxView::class, attribute = "onCheckedChangedCommand", method = "setCheckedChanged"),
        BindingMethod(type = LabelCheckBoxView::class, attribute = "labelCheckChecked", method = "setEnable"))
class ViewAdapter
 
 
/**
 * 綁定改變事件
 * Desc:
 * <p>
 * Author: LiWei
 * Date: 2019-12-27
 * @receiver LabelCheckBoxView
 * @param bindingCommand BindingCommand<Boolean>
 */
@BindingAdapter(value = ["onCheckedChangedCommand"], requireAll = false)
fun LabelCheckBoxView.setCheckedChanged(bindingCommand: BindingCommand<Boolean>) {
    this.getCheckBox().setOnCheckedChangeListener { compoundButton, b -> bindingCommand.execute(b) }
}
 
@BindingAdapter(value = ["labelCheckChecked"], requireAll = false)
fun LabelCheckBoxView.setEnable(labelCheckChecked: Boolean) {
    this.getCheckBox().isChecked = labelCheckChecked
}

2.3、自定義控件屬性時,提供set的方法 直接支持databinding
自定義屬性:

<declare-styleable name="LabelCheckBoxView">
    <attr name="labCheckTip" format="string" />
    <attr name="labCheckSelect" format="boolean" />
</declare-styleable>
 
class LabelCheckBoxView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
 
    /**
     * 左邊Tv
     */
    private var tipStr: String? = ""
 
 
    init {
        orientation = HORIZONTAL
        View.inflate(context, R.layout.widget_label_checkbox, this)
        if (attrs != null) {
            val a = getContext().obtainStyledAttributes(attrs, R.styleable.LabelCheckBoxView)
            if (a.hasValue(R.styleable.LabelCheckBoxView_labCheckTip)) {
                tipStr = a.getString(R.styleable.LabelCheckBoxView_labCheckTip)
            }
 
            //設置延展模式, 左右對齊方式
            if (a.hasValue(R.styleable.LabelCheckBoxView_labCheckSelect)) {
                switchLabel.isSelected = a.getBoolean(R.styleable.LabelCheckBoxView_labCheckSelect, false)
            }
 
            a.recycle()
        }
        tvLabelTip.text = tipStr
    }
 
...
...
 
    /**
     * 這個需要留着,用自定義屬性支持 dataBinding
     * Desc:
     * <p>
     * Author: xx
     * Date: 2019-12-27
     * @param str String
     */
    fun setLabCheckTip(str: String) {
        tvLabelTip.text = str
    }
 
    /**
     * 這個需要留着,用自定義屬性支持 dataBinding
     * Desc:
     * <p>
     * Author: xx
     * Date: 2019-12-27
     * @param select Boolean
     */
    fun setLabCheckSelect(select: Boolean) {
        switchLabel.isSelected = select
    }
}

3、參考
【1】、官網https://developer.android.com/topic/libraries/data-binding/binding-adapters#kotlin

【2】、androidx.databinding.adapters 源碼

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