Может ли взаимодействие с пользователем инициировать события до завершения OnResume? - PullRequest
5 голосов
/ 23 марта 2020

В одном из моих Fragment s я регистрирую OnFocusChangeListener для EditText в onResume():

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

Я регистрирую слушателя в onResume(), потому что если я установит его в более раннем методе жизненного цикла, он будет срабатывать при каждом изменении конфигурации. Установка его в onResume() гарантирует, что фокус, который существовал до изменения конфигурации, уже был восстановлен до регистрации слушателя, поэтому слушатель не будет автоматически срабатывать после изменения конфигурации / восстановления фокуса.

Теперь я боюсь, что я, возможно, регистрирую этого слушателя слишком поздно. Поэтому мой вопрос: может ли пользовательское взаимодействие уже привести к фокусировке элемента до или во время выполнения onResume()? (Это означало бы, что я потерял бы это событие фокуса, потому что я настраиваю слушателя во время onResume()). Или более общий: возможно ли взаимодействие с пользователем во время выполнения onResume()? Фрагмент документация говорит о onResume():

Вызывается, когда фрагмент виден пользователю и активно работает.

Понятно, что " Видимо пользователю "означает", но что именно означает "активно работает"? Это уже означает, что пользователь принимает ввод? Или пользовательский ввод впервые принят после завершения onResume()?

Ответы [ 2 ]

4 голосов
/ 31 марта 2020

Восстановление фокуса выполняется в 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
        }
    }
}
1 голос
/ 26 марта 2020

Глядя на FragmentManager исходный код, performResume вызов, вызывающий onResume, выполняется сразу после запуска фрагмента (и вызывается onStart): https://android.googlesource.com/platform/frameworks/support/+/84448d71fda0a24ba5d60fe9368ac47b97564c88/fragment/src/main/java/androidx/fragment/app/FragmentManagerImpl.java#926

Запуск фрагмента необходим для взаимодействия с пользователем, и между вызовами onStart и onResume не может происходить никаких взаимодействий, поскольку они могут выполняться только в одном и том же главном потоке.

Так что да, пользовательский ввод не выполняется возможно до onResume.

...