Search code examples
androidandroid-databinding

How can I iterate over all views known to the data binder?


I have three TextInputEditText views in my layout where the user can type in specific information.
On the click of a Button this information is stored in my database.

After the user clicks this Button, I want to clear all TextInputEditText fields.
Right now, I am doing this by hardcoding:

private fun clearAllEditTextFields() {
        
    Timber.d("clearAllEditTextFields: called")
        
    binding.bookTitleEditText.text = null
    binding.bookAuthorEditText.text = null
    binding.bookPageCountEditText.text = null
        
}

Since this is bad, I would like to use a dynamic for each loop to identify all views of type TextInputEditText known to binding and clear their content:

private fun clearAllEditTextFields() {
        
    Timber.d("clearAllEditTextFields: called")
        
    for (view in binding.views) {

        if (view is TextInputEditText) {

            view.text = null
        
        }

}

Unfortunately, there is no such field binding.views.
Is there still a way to achieve this or something with the same properties?

What I have tried so far

I have used a BindingAdapter. In my Util class, where all my extension functions go, I have created an EditText extension function clearText annotated as BindingAdapter and JvmStatic:

@JvmStatic
@BindingAdapter("clearText")
fun EditText.clearText(@NotNull shouldClear: Boolean) {
        
    Timber.d("clearText: called")
        
    if (shouldClear) text = null
        
}

In XML:

<com.google.android.material.textfield.TextInputEditText
    android:id="@+id/book_title_edit_text"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:imeActionId="100"
    android:imeOptions="actionNext"
    android:inputType="text"
    android:text="@={viewModel.bookTitle}"
    app:clearText="@{viewModel.clearAllEditTextFields}"
/>

In my ViewModel class, I have created a var clearAllEditTextFields = false which is modified in the clearAllEditTextFields() function which gets called inside my ViewModel:

...
var clearAllEditTextFields = false
clearAllEditTextFields()
...

private fun clearAllEditTextFields() {
        
    Timber.d("clearAllEditTextFields: called")
        
    clearAllEditTextFields = true
        
}

According to Logcat, my extension function is called when my ViewModel is initialized. However, when clearAllEditTextFields() gets called, it does not trigger a new call to the extension function.


Solution

  • A simple for loop doesn't exist to loop over the views in the binding object and you can try the following to keep your code conscice.

    Scope Functions

        binding.apply{
           bookTitleEditText.text = null
           bookAuthorEditText.text = null
           bookPageCountEditText.text = null
        }
    

    scope functions are a good go iff there are few views and we end up with quite a boiler-plate code if the number of views is large, in which cases I think Binding-Adapter would be a good choice

    @BindingAdapter("clear_text")
    fun EditText.clearText(shouldClear : Boolean?){
       shouldClear?.apply{
           if(shouldClear)
              text = null
       }
    }
    

    ViewModel

    private val _shouldClear = MutableLiveData<Boolean>()
    val shouldClear : LiveData<Boolean>
    get() = _shouldClear
    
    fun setClearStatus(status : Boolean){
       _shouldClear.value = status
    }
    
    //since clearing a text is an event and not state, reset the clear_status once it's done
    fun resetClearStatus(){
      _shouldClear.value = nul
    }
    

    XML

    <EditText 
      ......
      app:clear_text = "@{yourViewModel.shouldClear}"
      ...... />
    

    ActivityClass

    ...
    binding.lifecycleOwner = this
    ...
    
    
    private fun clearAllEditTextFields() {
       yourViewModel.setClearStatus(true)
       yourViewModel.resetClearStatus()        
    }
    
    

    Edit:

    add binding.lifecycleOwner = this in your activity class and its used for observing LiveData with data binding. The view will observe for text changes at runtime.