英文:
Android Kotlin editText Listener Question
问题
I'm trying to program something where a user inputs a number into an editText field and it automatically has decimal places appended. I'm using editText.setText
inside the listener, but find that the code crashes every time. Upon logging the value that I'm trying to edit, I found that the moment I change the editText field, editText.setText
runs non-stop, which causes the program to crash. Below is my code:
val etTesting = findViewById<EditText>(R.id.etTesting)
etTesting.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) {
if (!s.isNullOrEmpty() && s.toString() != ".") {
val value = s.toString().toDouble()
val formatted = String.format("%.2f", value)
Log.i(TAG, "onTextChanged $formatted")
etTesting.setText(formatted)
etTesting.setSelection(formatted.length)
}
}
})
I tried to use setText
outside of the listener and it doesn't trigger a crash. It's only inside the listener. I'm guessing it has to do with the listener writing an updated value, seeing that it's updated again, and then goes through this sequence infinitely?
How is one supposed to use the listener to do what I'm trying to do?
Thanks for the help.
英文:
I'm trying to program something where a user inputs a number into an editText field and it automatically has decimal places appended. I'm using editText.setText
inside the listener, but find that the code crashes every time. Upon logging the value that I'm trying to edit, I found that the moment I change the editText field, editText.setText
runs non-stop, which causes the program to crash. Below is my code:
val etTesting = findViewById<EditText>(R.id.etTesting)
etTesting.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) {
if (!s.isNullOrEmpty() && s.toString() != ".") {
val value = s.toString().toDouble()
val formatted = String.format("%.2f", value)
Log.i(TAG, "onTextChanged $formatted")
etTesting.setText(formatted)
etTesting.setSelection(formatted.length)
}
}
})
I tried to use setText outside of the listener and it doesn't trigger a crash. It's only inside the listener. I'm guessing it has to do with the listener writing an updated value, seeing that it's updated again, and then goes through this sequence infinitely?
How is one supposed to use the listener to do what I'm trying to do?
Thanks for the help.
答案1
得分: 0
This will crash, because as soon as you format and set text, onTextChanged
is called with a new value and this is repeated infinitely, so the best option here would be to use filters
and validate and return the text as you need, an example of how to use
etTesting.filters = arrayOf(object : InputFilter {
override fun filter(
input: CharSequence?,
p1: Int,
p2: Int,
p3: Spanned?,
p4: Int,
p5: Int
): CharSequence =
if (!input.isNullOrEmpty() && input.toString() != ".") {
val value = input.toString().toDouble()
String.format("%.2f", value)
} else input ?: ""
})
英文:
This will crash, because as soon as you format and set text, onTextChanged
is called with new value and this is repeated infintely, so best option here would be to use filters
and validate and return the text as you need, an example of how to use
etTesting.filters = arrayOf(object : InputFilter {
override fun filter(
input: CharSequence?,
p1: Int,
p2: Int,
p3: Spanned?,
p4: Int,
p5: Int
): CharSequence =
if (!input.isNullOrEmpty() && input.toString() != ".") {
val value = input.toString().toDouble()
String.format("%.2f", value)
} else input?:""
})
答案2
得分: 0
您的应用将崩溃,因为当您在EditText中键入内容时,addTextChangedListener将触发,如果您在监听器中还使用setText,它将一遍又一遍地触发,导致应用崩溃,因为它将无限重复,就像使用while循环一样。
使用InputFilter接口来实现自己的过滤CharSequence。像这样。
val inputFilter = object : InputFilter {
override fun filter(
input: CharSequence?, p1: Int, p2: Int, p3: Spanned?, p4: Int, p5: Int
): CharSequence {
return if (!input.isNullOrEmpty() && input.isDigitsOnly() && input.toString() != ".") {
val value = input.toString().toDouble()
String.format("%.2f", value)
} else {
""
}
}
}
etTesting.setFilters(arrayOf<InputFilter>(inputFilter))
希望对您有帮助。
英文:
Your app will crash, because when you type something in the EditText, the addTextChangedListener will trigger and if you also use setText in your listener and it will trigger again and again, this will lead your app to crash, because it will repeat forever, Like you use a while loop.
Use the InputFilter interface to implement your own filtered CharSequence. Like this.
val inputFilter = object : InputFilter {
override fun filter(
input: CharSequence?, p1: Int, p2: Int, p3: Spanned?, p4: Int, p5: Int
): CharSequence {
return if (!input.isNullOrEmpty() && input.isDigitsOnly() && input.toString() != ".") {
val value = input.toString().toDouble()
String.format("%.2f", value)
} else {
""
}
}
}
etTesting.setFilters(arrayOf<InputFilter>(inputFilter))
Hope this would be helpful for you
答案3
得分: 0
作为更一般的方法(而不是依赖于过滤器,因为它们不一定适用于你想要做的事情),你可以在你的TextWatcher
中创建一种updating
标志:
object : TextWatcher {
var updating = false
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) {
// 如果由内部更新引起的文本更改,则跳过对文本更改的反应
if (!updating) {
updating = true
// 对文本内容进行更改
updating = false
}
}
}
基本上,当onTextChanged
触发时,它会检查是否正在进行内部更新,只有在没有内部更新时才会继续。首先它设置了updating
标志,然后更新文本,这立即触发了TextWatcher
的回调函数。
这些调用是递归的,因此你的初始onTextChanged
函数不会完成,直到触发的onTextChanged
返回。如果这引发了另一个onTextChanged
,你最终会得到无限嵌套的函数调用,直到发生堆栈溢出。
通过使用这个标志,你可以短路这种行为。因为第一次调用设置了updating
标志,文本更改会触发第二次调用,第二次调用会看到updating
为true,并且在不进行任何更改的情况下返回。然后你的第一个onTextChanged
调用将完成。最后它会将updating
设置为false,因为更新过程已经完成,所以它准备好处理下一次更改。
英文:
As a more general approach (instead of relying on filters, which aren't always enough for what you want to do) you could create some kind of updating
flag in your TextWatcher
:
object : TextWatcher {
var updating = false
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) {
// skip reacting to the text change if it's caused by an internal update
if (!updating) {
updating = true
// make changes to the text contents
updating = false
}
}
}
Basically when onTextChanged
fires, it checks to see if there's an internal update happening, and only proceeds if there isn't. First it sets that updating
flag, and then updates the text, which immediately triggers the TextWatcher
callbacks again.
Those calls are recursive, so your initial onTextChanged
function won't complete until the triggered onTextChanged
returns. And if that fires another onTextChanged
, you end up with infinite nested function calls until you get a (heyo) stack overflow.
By using this flag, you short-circuit that behaviour. Because the first call sets that updating
flag, the text change will trigger a second call, which sees that updating
is true, and returns without making any changes. Then your first onTextChanged
call will complete. And the last thing it does is set updating
to false since the update process has finished, so it's ready to handle the next change.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论