TextWatcher not working in DialogFragment - android

I use TextInputLayout from this library:
implementation 'com.google.android.material:material:1.0.0'
I have following part in the layout:
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/phoneNumberLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/phoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/label_phone_number"/>
</com.google.android.material.textfield.TextInputLayout>
I have Kotlin Android Extensions is enabled and I try to assign TextWatcher to phoneNumber as follows:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
phoneNumber.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
Log.e("DialogFragment", "phoneNumber::onTextChanged()")
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
Log.e("DialogFragment", "phoneNumber current text: " + s)
}
})
}
Nothing happens when I start typing , hours wasted... Any help would be appreciated.

You are adding the textwatcher in onActivityCreated which does not hold your root View. You should reference your phone(TextInputEditText) from View inflated in onCreateView or inside onViewCreated as suggested by #Miha_x64. Your code is correct though but the problem is with the view reference.

Related

How to use Inverse Binding Adapter in Kotlin for converting Lowercase text to Uppercase?

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" />

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!
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

Android Kotlin handling EditText change event

Here is a text box
<EditText
android:id="#+id/textBox"
android:width="150px"
android:layout_width="128dp"
android:layout_margin="12dp"/>
Here's some code.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main) // Unresolved reference activity_main
val tb = findViewById(R.id.textBox) as EditText
tb .addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
Toast.makeText(
this#MainActivity,
"Text changed.",
Toast.LENGTH_SHORT
).show()
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
}) }
}
The findViewById doesn't work because Unresolved reference textBox.
What's going on?
None of the examples I've found online work.
This is rubbish!
Why is Android development so exceptionally difficult?
you have to add this back : setContentView(R.layout.activity_main)
why ? setContentView(R.layout.yourLayoutName) is how you associate a layout file to an activity. think of it as connecting the xml you made in the layout to the activity. when you leave out the setContentView, your activity has no layout at all.
then, when it says it's unresolved, you have use the import:
import your.package.name.R (you can usually use alt+enter to bring up these types of suggestions for you)
just start typing setContentView(R.layout.activity_main) then, there should be a line underneath the letter R, just click to the right of it and use alt+ enter to bring up suggestions for imports.
Additional info :
The R class is generated automatically from the application's resources, It contains the ids for the majority of resources created underneath the res folder
Entirely irrlevant note :
Why is Android development so exceptionally difficult?
What's normal for the spider is chaos for the fly :)
Please uncomment setContentView(R.layout.activity_main)from your activity. In kotlin you do not need to explicitly call findViewById. You can simply do this.
textBox.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
Toast.makeText(
this#MainActivity,
"Text changed.",
Toast.LENGTH_SHORT
).show()
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
}) }

How can I replace one note with a different note as the user is typing (replace “space” with “-”

I am using a tag system similar to SO, and instead of forcing the user to find the dash in their keyboard I want space to automatically be translated to a dash.
I am trying to implement it with this textWatcher, but the app doesn't let me type the space bar (it kinda flashes but nothing happens.
imageTagsInput.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
imageTagsInput.removeTextChangedListener(this)
imageTagsInput.setText(imageTagsInput.text.toString().replace(" ","-"))
imageTagsInput.addTextChangedListener(this)
imageTagsInput.setSelection(imageTagsInput.length())
}
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
})
And this is the xml of the EditText:
android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-  "
Add something like this in afterTextChanged
String result = s.toString().replaceAll(" ", "-");
if (!s.toString().equals(result)) {
ed.setText(result);
ed.setSelection(result.length());
// alert the user
}
For more info see this answer

Kotlin addTextChangeListener lambda?

How do you build a lambda expression for the EditText addTextChangeListener in Kotlin? Below gives an error:
passwordEditText.addTextChangedListener { charSequence ->
try {
password = charSequence.toString()
} catch (error: Throwable) {
raise(error)
}
}
addTextChangedListener() takes a TextWatcher which is an interface with 3 methods. What you wrote would only work if TextWatcher had only 1 method. I'm going to guess the error you're getting relates to your lambda not implementing the other 2 methods. You have 2 options going forward.
Ditch the lambda and just use an anonymous inner class
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) {
}
})
Create an extension method so you can use a lambda expression:
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.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(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
})
}
And then use the extension like so:
editText.afterTextChanged { doSomethingWithText(it) }
Add this core ktx dependence
implementation 'androidx.core:core-ktx:1.0.0'
You simply have to do
passwordEditText.doAfterTextChanged{ }
A bit old, but using Kotlin Android extensions you can do something like that:
editTextRequest.textChangedListener {
afterTextChanged {
// Do something here...
}
}
No extra code needed, just add:
implementation 'androidx.core:core-ktx:1.0.0'
Sorry for being late!
If you add implementation 'androidx.core:core-ktx:1.1.0' to your module's build.gradle file then you can use
etPlayer1.doOnTextChanged { text, start, count, after -> // Do stuff }
Test it :
passwordEditText.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) { }
})
hope this Kotlin sample help making it clear:
class MainFragment : Fragment() {
private lateinit var viewModel: MainViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.main_fragment, container, false)
view.user.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) {
userLayout.error =
if (s.length > userLayout.counterMaxLength) {
"Max character length is: ${userLayout.counterMaxLength}"
} else null
}
})
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
// TODO: Use the ViewModel
}
}
With this XML layout:
<android.support.design.widget.TextInputLayout
android:id="#+id/userLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterMaxLength="5"
app:counterEnabled="true"
android:hint="user_name">
<android.support.design.widget.TextInputEditText
android:id="#+id/user"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
And this Gradle:
android {
compileSdkVersion 'android-P'
...
}
api 'com.android.support:design:28.0.0-alpha1'
implementation 'com.android.support:appcompat-v7:28.0.0-alpha1' // appcompat library
In case you're using Material Filled text field or Outlined text field, attempt to respond to input text change as mentioned by documentation, respectively:
filledTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
// Respond to input text change
}
and
outlinedTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
// Respond to input text change
}
if you use implementation 'androidx.core:core-ktx:1.1.0-alpha05' you can use
For android.widget.TextView
TextWatcher
TextView.doBeforeTextChanged(crossinline action: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)
Add an action which will be invoked before the text changed.
TextWatcher
TextView.doOnTextChanged(crossinline action: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)
Add an action which will be invoked when the text is changing.
TextWatcher
TextView.doAfterTextChanged(crossinline action: (text: Editable?) -> Unit)
https://developer.android.com/reference/kotlin/androidx/core/widget/package-summary#extension-functions
Add the core ktx dependency
implementation 'androidx.core:core-ktx:1.3.0'
And you can simply implement like this
edit_text.addTextChangedListener { it: Editable? ->
// Do your stuff here
}
Another alternative is the KAndroid library -
implementation 'com.pawegio.kandroid:kandroid:0.8.7#aar'
Then you could do something like this...
editText.textWatcher { afterTextChanged { doSomething() } }
Obviously it is excessive to use an entire library to solve your problem, but it also comes with a range of other useful extensions that eliminate boilerplate code in the Android SDK.
You can make use of kotlin's named parameters:
private val beforeTextChangedStub: (CharSequence, Int, Int, Int) -> Unit = { _, _, _, _ -> }
private val onTextChangedStub: (CharSequence, Int, Int, Int) -> Unit = { _, _, _, _ -> }
private val afterTextChangedStub: (Editable) -> Unit = {}
fun EditText.addChangedListener(
beforeTextChanged: (CharSequence, Int, Int, Int) -> Unit = beforeTextChangedStub,
onTextChanged: (CharSequence, Int, Int, Int) -> Unit = onTextChangedStub,
afterTextChanged: (Editable) -> Unit = afterTextChangedStub
) = addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
beforeTextChanged(charSequence, i, i1, i2)
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
onTextChanged(charSequence, i, i1, i2)
}
override fun afterTextChanged(editable: Editable) {
afterTextChanged(editable)
}
})
This is the lambda function with edit text field with TextWatcher
searchField.addTextChangedListener(
afterTextChanged = {
},
onTextChanged = {s, start, before, count->
TODO("DO your code")
},
beforeTextChanged = {s, start, before, count->
TODO("DO your code")
}
)
This looks neat:
passwordEditText.setOnEditorActionListener {
textView, keyCode, keyEvent ->
val DONE = 6
if (keyCode == DONE) {
// your code here
}
false
}

Categories

Resources