Data binding custom view bidirectional binding for Android developers

Author: BuildF

preface

The default reader of this article has a certain understanding of databinding. If you don't know much, you can see Dongge's Full instructions for DataBinding , many people reject databinding, but others like it. Although it is controversial, it does not prevent us from learning and understanding.
This article describes how to bind data in both directions with a custom view to realize the effect of app:customvalue="@={userName}" using customization in xml

app:xx = "@{userName}" one-way binding
app:xx = "@ = {userName}" bidirectional binding

Introducing databinding

1. First step
Introduce kapt plug-in

2. Step two
Open data binding (here is the difference between the new version of gradle and the old version, and tell me which version it is in the comment area where you know it) the new version of gradle

buildFeatures.dataBinding = true

Old gradle

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

Implementation code

The implementation method provided here is certainly not the optimal solution, or the best, but it is more suitable as a small piece of knowledge

1. Let me show you the catalogue of the whole project first

2. First, create a CustomView with a TextView and EditText as a demonstration.

class CustomView(mContext: Context, attributeSet: AttributeSet?) :
    LinearLayout(mContext, attributeSet) {
    private var onChangeListener: InverseBindingListener? = null
    private var onInputChangeListener: InverseBindingListener? = null
    private var itemInput: EditText? = null
    private var itemText: TextView? = null
    var etInput = ""
        set(value) {
            val oldValue = field
            if (value == oldValue) {
                return;
            }
            field = value
            onInputChangeListener?.onChange()
        }

    var tvValue = ""
        set(value) {
            val oldValue = field
            if (value == oldValue) {
                return;
            }
            field = value
            onChangeListener?.onChange()
        }

    init {
        initView(mContext, attributeSet)
    }

    private fun initView(mContext: Context, attributeSet: AttributeSet?) {
        val view = inflate(mContext, R.layout.widget_custom_view, this)
        itemInput = view.findViewById(R.id.et_input)
        itemText = view.findViewById(R.id.tv_value)
        itemText?.setOnClickListener {
            tvValue = System.currentTimeMillis().toString()
            itemText?.text = tvValue
        }
        itemInput?.doOnTextChanged { text, start, before, count ->
            etInput = text.toString()
        }
    }

    internal fun setOnInputChangeListener(listener: InverseBindingListener) {
        if (onInputChangeListener == null) {
            this.onInputChangeListener = listener
        }
    }

    internal fun setOnValueChangeListener(listener: InverseBindingListener) {
        if (onChangeListener == null) {
            this.onChangeListener = listener
        }
    }
}

widget_custom_view.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="100dp"
  android:orientation="vertical">

  <TextView
    android:id="@+id/tv_value"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@color/purple_200"
    android:gravity="center"
    android:hint="Please select"
    android:textColor="@color/white"
    android:textColorHint="@color/white"
    android:textSize="16dp" />

  <EditText
    android:id="@+id/et_input"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@color/teal_700"
    android:gravity="center"
    android:hint="Please enter"
    android:textColor="@color/white"
    android:textColorHint="@color/white"
    android:textSize="16dp" />
</LinearLayout>

3. Create a BindAdapter management class DatabindComponent class to manage related usage, or write the code in CustomView through BindMethod

object DataBindComponent {

    @BindingAdapter("itemInput")
    @JvmStatic
    fun CustomView.setItemInputParams(value: String) {
        etInput = value
    }

    @InverseBindingAdapter(attribute = "itemInput", event = "itemInputAttrChanged")
    @JvmStatic
    fun getItemInputParams(view: CustomView): String {
        return view.etInput
    }

    @BindingAdapter(value = ["itemInputAttrChanged"], requireAll = false)
    @JvmStatic
    fun CustomView.itemPutChange(textAttrChanged: InverseBindingListener) {
        setOnInputChangeListener(textAttrChanged)
    }

    @BindingAdapter("itemValue")
    @JvmStatic
    fun CustomView.setItemValueParams(value: String) {
        tvValue = value
    }

    @InverseBindingAdapter(attribute = "itemValue", event = "itemValueAttrChanged")
    @JvmStatic
    fun getItemValueParams(view: CustomView): String {
        return view.tvValue
    }

    @BindingAdapter(value = ["itemValueAttrChanged"], requireAll = false)
    @JvmStatic
    fun CustomView.itemValueChange(textAttrChanged: InverseBindingListener) {
        setOnValueChangeListener(textAttrChanged)
    }
}

4. Add CustomView in xml and two observablefields to listen and bind data

summary

The reason why GitHub code warehouse is not posted is that these sample codes are written in the project through Moudle, and some of them are not suitable for release. All the key codes are released, and only some used codes are displayed in the form of pictures. If you just need to bind the custom view in two directions, the above example can help you. If you have a better writing method, you can tell me in the comment area and let me learn

Tags: Design Pattern Android jetpack compose

Posted by TheTitans on Thu, 14 Apr 2022 15:58:58 +0930