You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不依赖FocusChange检测EditText输入起止时间,计算密码输入耗时

问题分析

你现有实现的核心问题在于:

  • 输入满6位直接跳转Activity时,EditText不会触发焦点丢失回调,MyFocusChangeListener无法捕获输入结束事件
  • 仅靠焦点变化计算耗时的逻辑,覆盖不了输入完成直接跳转、用户中途停止输入未切走焦点的场景

解决方案

采用超时检测机制判定输入停止,所有逻辑封装在库内无需上层适配,覆盖所有触发场景:

  1. 设定输入停止判定阈值(默认500ms可调整),用户连续指定时长无输入即判定为输入停止
  2. 每次用户输入字符时清空之前的延时任务,重新提交新的停止检测任务
  3. 额外处理满6位直接跳转的场景,输入到6位时直接触发结束计算,无需等超时

实现代码

1. 改造MyTextWatcher

class MyTextWatcher(private val view: EditText) : TextWatcher {
    // 输入停止判定超时时间,单位ms,可按需调整
    private val inputStopDelay = 500L
    var startTypingTime: Long = 0L
    var lastInputTime: Long = 0L

    // 输入停止执行任务
    private val inputStopRunnable = Runnable {
        val inputCost = lastInputTime - startTypingTime
        // 此处添加输入停止后的处理逻辑,比如埋点上报
        handleInputStop(inputCost)
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        val now = System.currentTimeMillis()
        // 首次输入记录开始时间
        if (startTypingTime == 0L) {
            startTypingTime = now
        }
        lastInputTime = now
        // 移除之前的延时任务
        view.removeCallbacks(inputStopRunnable)
        // 输入满6位直接触发停止,无需等待超时
        if (s?.length == 6) {
            val inputCost = lastInputTime - startTypingTime
            handleInputStop(inputCost)
            return
        }
        // 提交新的延时任务
        view.postDelayed(inputStopRunnable, inputStopDelay)
    }

    override fun afterTextChanged(s: Editable?) {}

    private fun handleInputStop(costTimeMs: Long) {
        // 你的自定义处理逻辑
    }

    // 对外暴露清理方法,避免内存泄露
    fun clearTask() {
        view.removeCallbacks(inputStopRunnable)
    }
}

2. 改造焦点监听逻辑

fun MyFocusChangeListener(view: View, hasFocus: Boolean) {
    if (!hasFocus && view is EditText) {
        // 失去焦点时直接触发输入停止计算
        val watcher = view.getTag(R.id.tag_text_watcher) as? MyTextWatcher ?: return
        watcher.clearTask()
        if (watcher.startTypingTime != 0L && watcher.lastInputTime != 0L) {
            val inputCost = watcher.lastInputTime - watcher.startTypingTime
            // 调用处理逻辑
        }
    }
}

3. 绑定逻辑调整

首先在res/values/ids.xml中添加自定义id,用于存储Watcher实例:

<item type="id" name="tag_text_watcher" />

在Activity初始化时绑定:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...原有逻辑不变
    val myTextWatcher = MyTextWatcher(password)
    password.addTextChangedListener(myTextWatcher)
    // 将watcher实例存在Tag中
    password.setTag(R.id.tag_text_watcher, myTextWatcher)
    password.setOnFocusChangeListener { v, hasFocus -> MyFocusChangeListener(v, hasFocus) }
}

注意事项

  • 超时阈值可根据业务需求调整,300-1000ms都是合理范围
  • 可监听EditText的OnAttachStateChangeListener,在View销毁时调用clearTask()避免内存泄露
  • 所有逻辑均封装在库内,上层应用无需做额外适配

内容的提问来源于stack exchange,提问作者Neco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 16:06:02