避免 EditText TextWatcher 在每次字符更改时触发事件
Avoid EditText TextWatcher firing events on every character change
我使用附加了 TextWatcher 的 EditText 文本字段。所有回调方法都会在我输入的每个字符上触发事件。在我的例子中,我调用 api on text changed,将响应放入同一片段的标签中。我无法检查特定格式的 EditText,因为它将是自由文本。
示例:
你叫什么名字?
E -> api call
R -> api call
I _> api call
K -> api call
如何避免这种情况?我可以使用计时器吗(例如 2 秒没有文本更改 -> api 调用)?也许有一种优雅的方式可以做到这一点。
如果您使用的是 Kotlin,则可以使用协程来完成此操作。例如,如果您想 运行 在文本停止更改后 2 秒调用 API,它看起来像这样:
在 Activity 或带有 ViewModel 的片段中
val model: MainViewModel by viewModels()
binding.textInput.doAfterTextChanged { e ->
e?.let { model.registerChange( it.toString() ) }
}
在视图模型中
private var job: Job? = null
fun registerChange(s: String) {
job?.cancel() // cancel prior job on a new change in text
job = viewModelScope.launch {
delay(2_000)
println("Do API call with $s")
}
}
或者不使用 ViewModel
private var job: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textInput.doAfterTextChanged { e ->
e?.let {
job?.cancel()
job = lifecycleScope.launch {
delay(1_000)
println("Do API call with ${it}")
}
}
}
}
使用 Jetpack Compose
使用 Jetpack Compose 的解决方案是相同的,只是添加到 onValueChange
lambda 而不是 doAfterTextChanged
(就地,或者与之前相同的 ViewModel 调用)。
private var job: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val textState = remember { mutableStateOf("") }
TextField(
value = textState.value,
onValueChange = {
textState.value = it
job?.cancel()
job = lifecycleScope.launch {
delay(2_000)
println("TEST: Do API call with $it")
}
}
)
}
}
Java解决方案
如果您正在使用 Java,您可以使用处理程序和 postDelayed 运行nable,但流程是相同的(在文本更改时,取消先前的任务并启动新的有延迟)。
private final Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText textInput = findViewById(R.id.text_input);
textInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
if( s != null ) {
handler.removeCallbacksAndMessages(null);
handler.postDelayed(() -> {
System.out.println("Call API with " + s.toString());
}, 2000);
}
}
});
}
我使用附加了 TextWatcher 的 EditText 文本字段。所有回调方法都会在我输入的每个字符上触发事件。在我的例子中,我调用 api on text changed,将响应放入同一片段的标签中。我无法检查特定格式的 EditText,因为它将是自由文本。
示例: 你叫什么名字?
E -> api call
R -> api call
I _> api call
K -> api call
如何避免这种情况?我可以使用计时器吗(例如 2 秒没有文本更改 -> api 调用)?也许有一种优雅的方式可以做到这一点。
如果您使用的是 Kotlin,则可以使用协程来完成此操作。例如,如果您想 运行 在文本停止更改后 2 秒调用 API,它看起来像这样:
在 Activity 或带有 ViewModel 的片段中
val model: MainViewModel by viewModels()
binding.textInput.doAfterTextChanged { e ->
e?.let { model.registerChange( it.toString() ) }
}
在视图模型中
private var job: Job? = null
fun registerChange(s: String) {
job?.cancel() // cancel prior job on a new change in text
job = viewModelScope.launch {
delay(2_000)
println("Do API call with $s")
}
}
或者不使用 ViewModel
private var job: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textInput.doAfterTextChanged { e ->
e?.let {
job?.cancel()
job = lifecycleScope.launch {
delay(1_000)
println("Do API call with ${it}")
}
}
}
}
使用 Jetpack Compose
使用 Jetpack Compose 的解决方案是相同的,只是添加到 onValueChange
lambda 而不是 doAfterTextChanged
(就地,或者与之前相同的 ViewModel 调用)。
private var job: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val textState = remember { mutableStateOf("") }
TextField(
value = textState.value,
onValueChange = {
textState.value = it
job?.cancel()
job = lifecycleScope.launch {
delay(2_000)
println("TEST: Do API call with $it")
}
}
)
}
}
Java解决方案
如果您正在使用 Java,您可以使用处理程序和 postDelayed 运行nable,但流程是相同的(在文本更改时,取消先前的任务并启动新的有延迟)。
private final Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText textInput = findViewById(R.id.text_input);
textInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
if( s != null ) {
handler.removeCallbacksAndMessages(null);
handler.postDelayed(() -> {
System.out.println("Call API with " + s.toString());
}, 2000);
}
}
});
}