在我的一个Fragment
中,我正在onResume()
:中为EditText
注册OnFocusChangeListener
override fun onResume() {
super.onResume()
editText.setOnFocusChangeListener {
// do something here
}
}
我在onResume()
中注册侦听器,因为如果我在早期的生命周期方法中设置它,那么在每次配置更改时都会触发它。在onResume()
中设置它可以确保在注册侦听器之前已经恢复了在配置更改之前存在的焦点,因此侦听器不会在配置更改/焦点恢复之后自动激发。
现在我担心我可能注册这个听众太晚了。所以我的问题是:在执行onResume()
之前或期间,用户交互是否已经导致对某个元素的关注?(这意味着,我将失去这个焦点事件,因为我正在onResume()
期间设置侦听器(。或者更一般地说:在执行onResume()
时,用户交互已经可能了吗?Fragment文档介绍了onResume()
:
当片段对用户可见并且正在活动运行时调用。
"对用户可见"的含义很清楚,但"主动运行"的确切含义是什么?这是否已经意味着接受用户输入?还是在onResume()
完成后首先接受用户输入?
焦点恢复是在"活动"的onRestoreInstanceState()
中完成的,这与Fragment恢复其自己的View状态(将在Fragment的onViewStateRestored()
中(时单独完成。
根据onRestoreInstanceState()
文档,它是在活动的onStart()
和onPostCreate()
之间调用的(它在onResume()
和onPostResume()
-onPostResume()
之前运行,此时Fragment获得其onResume()
回调(。
这意味着您是正确的,因为在onResume()
之前的Fragment级别上没有可用的回调,其中焦点在调用该方法之前被正确设置。
话虽如此,是的,用户可以在Fragment达到恢复状态之前与它进行交互。例如,ViewPager2
(以及当使用BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
时的ViewPager 1(将非选择片段(即,不在屏幕中间的片段(保持在STARTED
状态。通过多点触摸的功能,用户可以稍微拖动页面,然后用另一根手指点击部分可见的视图。如果你自己使用setMaxLifecycle()
和Lifecycle.State.STARTED
(这就是他们在幕后所做的(,你会看到同样的行为——片段是可交互的,但不是RESUMED。
然而,在大多数情况下,如果您不使用上述任何API,则片段生命周期通常与活动生命周期相匹配。根据ActivityThread源代码,一个活动确实在同一个handleStartActivity()
方法中运行其更新。
需要注意的是,每次活动被销毁时,由于视图从"活动"中删除(它总是失去视图的焦点(,您都会得到一个OnFocusChangeListener
的回调,其中hasFocus
为false
。这种情况发生在状态保存之后,视图的焦点状态实际上并没有丢失,这只是您已经需要在回调中处理的事情,通常是通过检查isStateSaved()
,并在状态保存后忽略焦点丢失,如果您手动删除/替换片段(即通过执行replace()
操作(,则检查isRemoving()
。
考虑到您的侦听器中已经有了逻辑,以避免在销毁后处理错误事件的hasFocus
,处理获得焦点的100%正确的情况将包括在保存的实例状态中保存您自己的焦点状态(即,对于特定视图为true或false(,并且只有在hasFocus
与您已经保存的状态发生变化时才运行您的逻辑。这意味着您将在Fragment生命周期的早期恢复已保存的实例状态(例如,在Fragments提供的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
}
}
}
查看FragmentManager
源代码,触发onResume
的performResume
调用在片段启动后立即执行(并调用onStart
(:https://android.googlesource.com/platform/frameworks/support/+/84448d71fda0a24ba5d60fe9368ac4c7b97564c88/sfragment/src/main/java/androidx/fragment/app/FragmentManagerImpl.java#926
用户交互需要启动片段,并且onStart
和onResume
调用之间不能发生交互,因为它们只能在同一个主线程上执行。
因此,是的,在onResume
之前不可能有用户输入。