Prowtons McBishop
Prowtons McBishop

Reputation: 49

How to make a proper binding adapter for 2 way data binding in Android

I am attempting to make a page in my Android app that allows the user to enter a Float value in an EditText and binds that value to my view model. Here is my code for my current binding adapter:

import android.text.SpannableStringBuilder
import android.widget.EditText
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter

@BindingAdapter("android:text")
fun setText(view: EditText, value: Float?) {
    view.text = value?.let { SpannableStringBuilder(value.toString()) } ?: SpannableStringBuilder("")
}

@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
fun getTextString(view: EditText): Float? {
    val editTextString: String? = view.text?.toString()
    return if(editTextString == null || editTextString == "") 0.0f else editTextString.toString().toFloat()
}

Here is the code that I have for my EditText in my layout file:

<com.google.android.material.textfield.TextInputLayout
            android:id="@+id/amount_edit_text_outer"
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_width="@dimen/edit_text_width"
            android:layout_height="@dimen/edit_text_height"
            android:layout_gravity="center"
            android:hint="@string/edit_text_hint"
            app:layout_constraintBottom_toTopOf="@+id/fab"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/amount_edit_text_inner"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="@={viewmodel.liveAmount$app_debug}"
                android:inputType="numberDecimal" />

        </com.google.android.material.textfield.TextInputLayout>

Finally, my view model live data variable looks like this:

internal val liveAmount = MutableLiveData<Float>(0.0f)

I should also add that when I run this code on my phone, it allows me to enter the first number in the EditText and nothing else. Furthermore, the keyboard does not go away unless I kill the app. Any questions or comments are welcome. Thank you all for any help!

Upvotes: 0

Views: 1629

Answers (1)

Gavin Wright
Gavin Wright

Reputation: 3212

Try changing this:

@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")

to:

@InverseBindingAdapter(attribute = "android:text")

And add this other method:

// This notifies the data binding system that the attribute value has changed.
@BindingAdapter("android:textAttrChanged")
fun setTextChangedListener(editText: TextInputEditText, listener: InverseBindingListener) {
    editText.addTextChangedListener(object: TextWatcher{

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        }

        override fun afterTextChanged(s: Editable?) {
            listener.onChange()
        }
    })
}

This is not tested, but it should work or at least be close. See here for other similar examples:

https://github.com/gavingt/upcoming-games/blob/adc8b7e67b1e05d7bc9ac6247b82cfa93a43a26f/app/src/main/java/com/gavinsappcreations/upcominggames/utilities/BindingAdapters.kt#L324

Upvotes: 1

Related Questions