Восстановление фокуса выполняется в onRestoreInstanceState()
действия, что делается отдельно от того, когда Фрагмент восстанавливает свое собственное состояние просмотра (что было бы в onViewStateRestored()
Фрагмента).
Согласно документации onRestoreInstanceState()
, он вызывается между onStart()
и onPostCreate()
действия (который выполняется до onResume()
и onPostResume()
- onPostResume()
, когда Fragment получает свои onResume()
callbacks).
Это означает, что вы правы в том, что обратного вызова на уровне фрагмента не существует до onResume()
, когда фокус установлен правильно до вызова этого метода.
При этом пользователи могут взаимодействовать с фрагментом до того, как он достигнет возобновленного состояния. Например, ViewPager2
(а также ViewPager 1 при использовании BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
) сохраняют невыбранные фрагменты (т. Е. Не в середине экрана) в STARTED
гос. С помощью мультитач пользователи могут слегка перетаскивать страницу, а затем другим пальцем нажимать на частично видимый вид. Вы бы увидели то же поведение, если бы вы использовали setMaxLifecycle()
с Lifecycle.State.STARTED
самостоятельно (что и делают те, кто находится под капотом) - фрагмент является интерактивным, но не RESUMED.
Однако в большинстве общих случаев, если вы не используете какой-либо из перечисленных выше API, жизненный цикл фрагмента обычно соответствует жизненному циклу действия. И действие, согласно исходному коду ActivityThread , выполняет все свои обновления одним и тем же методом handleStartActivity()
.
Следует отметить, что каждый раз, когда действие уничтожается, вы будете получите обратный вызов к вашему OnFocusChangeListener
с hasFocus
из false
, поскольку представление удаляется из действия (которое всегда теряет фокус представления). Это происходит после того, как состояние сохранено, состояние фокуса просмотра фактически не потеряно, это просто то, что вам уже нужно обработать в обратном вызове, обычно проверяя isStateSaved()
и игнорируя потерю фокуса после состояния сохраняется и проверяется isRemoving()
, если вы вручную удаляете / заменяете фрагмент (то есть, выполняя операцию replace()
).
Учитывая, что у вас уже должна быть логика c в вашем слушателе, чтобы избежать обработки hasFocus
ложных событий после уничтожения, 100% правильный случай для обработки получения фокусировки будет включать сохранение вашего собственного состояния фокуса (т. Е. Истинного или ложного для определенного c представления) в вашем сохраненное состояние экземпляра и запуск вашей логики c, только если hasFocus
меняется с того, что вы уже сохранили. Это означает, что вы восстановите свое сохраненное состояние экземпляра ранее в жизненном цикле фрагмента (скажем, в методе onViewStateRestored()
, который предоставляют фрагменты) и добавите туда свой слушатель. Тогда ваша логика c может безопасно игнорировать обратные вызовы с тем же фокусом:
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
}
}
}