用户交互能否在 OnResume 完成之前触发事件?

Can a user interaction trigger events before OnResume has finished?

在我的一个 Fragment 中,我正在为 onResume() 中的 EditText 注册一个 OnFocusChangeListener

override fun onResume() {
    super.onResume()
    editText.setOnFocusChangeListener { 
        // do something here
    }
}

我在 onResume() 中注册侦听器,因为如果我在较早的生命周期方法中设置它,它会在每次配置更改时触发。在 onResume() 中设置它可确保在注册侦听器之前已恢复配置更改之前存在的焦点,因此侦听器不会在配置更改/焦点恢复后自动触发。

现在我担心我可能注册这个侦听器太晚了。所以我的问题是:在执行 onResume() 之前或期间,用户交互是否已经导致对元素的关注? (这意味着,我会松开这个焦点事件,因为我在 onResume() 期间设置监听器)。或者更笼统:在执行 onResume() 时是否已经可以进行用户交互?片段 documentation 说到 onResume()

Called when the fragment is visible to the user and actively running.

"visible to the user"的意思很清楚,但是"actively running"到底是什么意思呢?这是否已经意味着接受用户输入?还是在 onResume() 完成后首先接受用户输入?

查看 FragmentManager 源代码,performResume 触发 onResume 的调用在片段启动后立即执行(并调用 onStart): https://android.googlesource.com/platform/frameworks/support/+/84448d71fda0a24ba5d60fe9368ac47b97564c88/fragment/src/main/java/androidx/fragment/app/FragmentManagerImpl.java#926

用户交互需要启动片段,onStartonResume 调用之间不会发生交互,因为它们只能在同一个主线程上执行。

所以,是的,在 onResume 之前无法进行用户输入。

焦点恢复在 Activity 的 onRestoreInstanceState(), which is done separately from when Fragment's restore their own View's state (that would be in the Fragment's onViewStateRestored()) 中完成。

根据 onRestoreInstanceState() 文档,它在 Activity 的 onStart()onPostCreate() 之间调用(运行 在 onResume()onPostResume() - onPostResume() 是片段获得 onResume() 回调的时间)。

这意味着您是正确的,因为在 onResume() 之前的片段级别没有可用的回调,其中在调用该方法之前正确设置了焦点。

也就是说,是的,用户可以在 Fragment 达到恢复状态之前与它进行交互。例如,ViewPager2 (as well as ViewPager 1 when using BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) keep the non-selected fragments (i.e., those not in the middle of the screen) in the STARTED state. Through the power of multi-touch, users could drag a page over slightly and then use another finger to tap on a view that is partially visible. You'd see the same behavior if you use setMaxLifecycle()Lifecycle.State.STARTED 你自己(这就是那些在幕后所做的事情)——片段是可交互的,但不是 RESUMED。

然而,在大多数一般情况下,如果您不使用上述任何 API,片段生命周期通常与 Activity 生命周期相匹配。 activity,根据 ActivityThread source code,运行 其更新全部采用相同的 handleStartActivity() 方法。

需要注意的是,每次 activity 被销毁时,当视图被移除时,你会得到一个回调到你的 OnFocusChangeListenerfalsehasFocus来自 Activity(它总是失去 View 的焦点)。这发生在状态被保存之后,视图的焦点状态实际上并没有丢失,这只是您已经需要在回调中处理的事情,通常通过检查 isStateSaved() and ignoring focus loss after the state is saved and checking isRemoving() 如果您手动删除/替换片段(即,通过执行 replace() 操作)。

鉴于您已经必须在侦听器中具有逻辑以避免处理 hasFocus 错误事件 post 破坏,处理获得焦点的 100% 正确情况将涉及保存您自己的在您保存的实例状态中关注状态(即特定视图的 true 或 false),并且如果 hasFocus 正在从您已经保存的内容更改,则仅 运行 调整您的逻辑。这意味着您将在 Fragment 的生命周期早期恢复您保存的实例状态(例如,在 Fragment 提供的 onViewStateRestored() 方法中)并在那里添加您的侦听器。然后,您的逻辑可以安全地忽略具有相同焦点的回调:

override fun onViewStateRestored(savedInstanceState: Bundle?) {
    super.onViewStateRestored(savedInstanceState)
    // Restore your member variable of focus
    focused = savedInstanceState?.getBoolean("HAS_FOCUS", false) ?: false
    editText.setOnFocusChangeListener { _, hasFocus ->
        if (focused == hasFocus) {
            // ignore
            return
        }
        focused = hasFocus
        if (hasFocus) {
            // We gained focus
        } else if (!isStateSaved() && !isRemoving()) {
            // We lost focus
        }
    }
}