android - 避免 EditText TextWatcher 在每次字符更改时触发事件

标签 android android-edittext textwatcher android-textwatcher

我使用带有 TextWatcher 的 EditText 文本字段。所有回调方法都会在我输入的每个字符上触发事件。就我而言,我在更改的文本上调用 api,将响应放入同一 fragment 的标签中。我无法检查 EditText 的某种格式,因为它将是自由文本。

示例: 你叫什么名字?

E -> api call 
R -> api call
I _> api call
K -> api call

如何避免这种情况?我可以使用计时器(例如 2 秒内没有文本更改 -> api 调用)吗?也许有一种优雅的方法可以做到这一点。

最佳答案

如果您使用 Kotlin,则可以使用协程来完成此操作。例如,如果您想在文本停止更改后 2 秒运行 API 调用,则如下所示:

在具有 ViewModel 的 Activity 或 Fragment 中

val model: MainViewModel by viewModels()

binding.textInput.doAfterTextChanged { e ->
    e?.let { model.registerChange( it.toString() ) }
}

在 View 模型中

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 runnable,但流程是相同的(在文本更改时,取消先前的任务并延迟启动一个新任务)。

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);
            }
        }
    });
}

关于android - 避免 EditText TextWatcher 在每次字符更改时触发事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70632237/

相关文章:

android - 单击外部关闭上下文菜单

java - Android Studio - 添加 Google Play 服务/Android 支持后出现错误

java - 将数据数组保存到数据库

android - AdMob 广告在软键盘弹出后消失

android - 清除 TextWatcher 的 beforeTextChanged() 中的 EditText 数据

java - Android MediaPlayer 在 Android 6.0 Marshmallow 上崩溃

java - Android java if 语句不适用于 OnClickListener

android - 使用 AppCompat 的 Material 设计反向微调器/编辑文本主题

java - TextWatcher不过滤ListView?

android - 可在 EditText 中绘制的可单击复合图,单击时弹出 View