I want to set up the two-way binding for Edit Text. But getting an error so far.
Could not find event 'textChangeAttrChanged' on View type 'android.widget.EditText'
This is a pretty straightforward scenario but never seen a good working example.
Part of binding adapters:
#BindingAdapter("textChange")
#JvmStatic fun setText(view: EditText, value: String) {
if (view.text.toString() != value) {
view.setText(value)
}
}
#BindingAdapter("textChangeAttrChanged")
#JvmStatic fun setTextListener(view: EditText, onTextChange: ITextChange) {
val textWatcher : TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
onTextChange.onTextChanged(view.text.toString())
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
}
view.addTextChangedListener(textWatcher)
}
#InverseBindingAdapter(attribute = "textChange")
#JvmStatic fun getText(editText: EditText): String {
return editText.text.toString()
}
And from XML:
<EditText
android:id="#+id/et_title_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/input_address"
android:inputType="textPersonName"
android:textColorHint="#color/colorGray"
textChange="#={viewModel.searchKeyword}"
textChangeAttrChanged="#{(text) -> viewModel.onSearchEntered(text)" // adding or removing this line doesn't give a thing
tools:ignore="Autofill" />
This is not the correct way to use inverse databinding for a view model.
First, EditText already supports two-way databinding via the databinding library, so you don't have to do this on your own.
Second, you only need the inverse binding adapter stuff if you want to set up a custom view for databinding. In your case, you just want an existing view that is already set up for databinding to update your view moodel.
By using the "#={viewModel.searchKeyword}" notation, you are indicating that you have a property called "searchKeyword" that has both a getter and a setter and that you want the databinding library to invoke the setter with the value from the view when it changes.
Thus, all you should need to do is implement your logic in your property setter. Something like this:
#Bindable var searchKeyord : String? = null
set(value) {
if (field != value) {
field = value
onSearchEntered(value)
notifyPropertyChanged(BR.searchKeyword)
}
}
Please review the databinding documentation for more info.
Hope that helps!
Related
Am trying to do something when my EditText content is changed.
Here is 3 ways i find to do it.
1.
edittext.addTextChangedListener {
//my code
}
edittext.doAfterTextChanged {
//my code
}
edittext.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
//my code
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
})
All works perfectly for my purpose. Can somebody explain if there is any difference between each of them, any advantage or disadvantage using a particular way or simply all of them same?
1 and 3 are basically same thing. In kotling object : TextWatcher is not needed as it can wrap the implementation with lambda using just {}
edittext.addTextChangedListener {
//my code
}
What you did in the 3 with object : TextWatcher is same thing but the implemented functions afterTextChanged, beforeTextChanged and onTextChanged are visible.
And for 2, android in kotlin gives an inline function edittext.doAfterTextChanged which just does everything for you what you did on 3 using object : TextWatcher under the hood and gives access to only afterTextChanged functions implementation. This code is from androidx.core.widget package which shows what it's doing under the hood:
inline fun TextView.doAfterTextChanged(
crossinline action: (text: Editable?) -> Unit
) = addTextChangedListener(afterTextChanged = action)
I am creating an android application where I want to use a feature in which a text that we have entered into an editText field can be converted into uppercase at runtime in that particular editText field only.
I have tried with this code
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
this.text.toString().uppercase()
}
})
But it can be easily done by the concept of Inverse Binding Adapter in android. I have tried to implement it with reference of
https://developer.android.com/reference/android/databinding/InverseBindingAdapter
It is not working for me in my project. Can You explain me with step by step explanation?
Yess This Method of addTextChangedListener is available but we have to implement this method for each and every Edittext we want to convert to Upper case. So you heard correct about the InverseBinding Adapter. In InverserBinding Adapter we have to create this method for one time and you can use it any number of time.
I have implemented this using BindingAdapter and InverseBinding Adapter.In one Kotlin File, Write this two functions as follows.
Function Code
#BindingAdapter(value = ["setText", "custom:AttrChanged"], requireAll = false)
fun EditText.updateText(text: String?, listener: InverseBindingListener) {
if (this.text.toString() != text) {
this.setText(text)
}
this.doOnTextChanged { _: CharSequence?, _: Int?, _: Int?, _: Int? ->
listener.onChange()
}
}
#InverseBindingAdapter(attribute = "setText", event = "custom:AttrChanged")
fun EditText.getUpdatedText(): String? {
return this.text.toString().uppercase()
}
For Upper Case I have created one uppercase variable of MutableLiveData of type String
var uppercase = MutableLiveData("")
Now in XML i have set that property as follow:
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/edit_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="30dp"
setText="#={viewModels.uppercase}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/btn_login_data_binding" />
Just to be clear, I want to change the color of the textField to be clear once text is entered and for it to go back to its old color, which in my case is gray when it has no text. I looked through stackoverflow and found some answers, however it was in Java and I am still a beginner with Kotlin, but I am writing this in Kotlin for my work.
Essentially, I want to know how can you change the background of the textField once text is entered?
I want to have the background color of that to change once text is entered using Kotlin.
This is my XML EditText that I am referring to:
<EditText
android:id="#+id/description"
android:layout_width="350dp"
android:layout_height="200dp"
android:layout_below="#id/addImage"
android:layout_alignBottom="#id/addImage"
android:layout_marginStart="50dp"
android:layout_marginTop="125dp"
android:layout_marginEnd="50dp"
android:layout_marginBottom="75dp"
android:background="#color/lightGray"
android:gravity="center"
android:hint="#string/description"
android:inputType="text"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Where do I go from here now?
class PostActivity : AppCompatActivity() {
private lateinit var button: Button
private lateinit var imageView: ImageView
private lateinit var editText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post)
editText = findViewById(R.id.description)
}
}
This is just what I have on my activity. I don't have any method really on how to do this.
Add a TextWatcher to react when text has been changed.
val originalColor = (editText.background as ColorDrawable).color // or Color.LIGHT_GRAY
val haveTextColor = Color.TRANSPARENT // or whatever you want
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) {
editText.setBackgroundColor(if (s.isEmpty()) originalColor else haveTextColor)
}
})
You can use the doAfterTextChanged Kotlin extension function here. It can be used to perform some action every time the text changes.
editText.doAfterTextChanged { text ->
editText.setBackgroundColor(if (text.isEmpty()) color1 else color2) // Choose whatever colors you want
}
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!
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
I'm newbie in Kotlin and Android.
I confused when see syntax when use TextWatcher like this:
editTextSample.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int,
count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
txtView1.setText("Text in EditText : "+s)
}
})
Can you explain it for me. Thank you
EditText class extends TextView class which contains a method called addTextChangedListener().
Here you are creating the object of EditText class and calling that method. Where you need to pass the object of TextWatcher interface as argument.
[Hold on.. but we can not create the object of interface. So here we are
using concept of anonymous class for that, check this ].
and as the interface contains three methods we have to override them all. That's it.
Notation object : TextWatcher is just creating an anonymous class here. It's just kotlin way of creating it. Kotlin gives you more methods to implement TextWatchers.
You can import androidx.core:core-ktx dependency which provides a lot of nice features. One of them is extensions for textwatchers. With using this you can simplify your code to:
editTExt.doOnTextChanged { text, start, count, after ->
//Do something here
}