Получить родительскую активность в подклассе DialogFragment перед show()

Я работаю над приложением для Android со многими диалогами, каждый из которых расширяет пользовательский класс под названием «DialogFragmentBase», который расширяет «DialogFragment» библиотеки. Все действия используют метод show(), переопределенный в DialogFragmentBase.

Я хочу предотвратить отображение диалогов, если родительская активность находится в фоновом режиме (например, при получении телефонного звонка), поскольку это приводит к недопустимому StatEException, но в то же время я не хочу охранять каждый вызов show() в приложении с помощью:

if(getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED))

Итак, я хотел сделать что-то подобное в DialogFragmentBase:

@Override
public void show(FragmentManager manager, String tag){

    List<Fragment> fragments = manager.getFragments();
    if(fragments != null && fragments.size() > 0){
        FragmentActivity activity = fragments.get(fragments.size()-1).getActivity();
        if(activity != null && activity.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)){
            super.show(manager, tag);
        }
    }
}

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

Примечание. Я попробовал решение onSaveInstanceState, описанное здесь, но, поскольку DialogFragment еще не показан, onSaveInstanceState не призывал к этому в тот момент. Также getActivity() возвращает null, так как onAttach еще не вызывался в этот момент.

Спасибо!


person Menna Mostafa    schedule 09.08.2018    source источник


Ответы (2)


библиотека поддержки FragmentManager имеет isStateSaved()метод. В зависимости от того, каковы ваши требования, вы можете использовать это, чтобы проверить, «безопасно» ли показывать ваши диалоги:

@Override
public void show(FragmentManager manager, String tag) {
    if (!manager.isStateSaved()) {
        super.show(manager, tag);
    }
}

Обратите внимание, что приведенная выше реализация относительно похожа на использование commitAllowingStateLoss() в том смысле, что в некоторых случаях вы не сможете показать свой диалог. Но, возможно, это хорошо для ваших требований.

person Ben P.    schedule 09.08.2018
comment
isStateSaved() возвращает false, даже если родительская активность уже находится в фоновом режиме. Я начинаю думать, что, возможно, commitAllowingStateLoss() не такой уж плохой подход (по крайней мере, в моем случае, поскольку диалог, который я хочу показать пользователю, бесполезен, если приложение все равно находится в фоновом режиме). - person Menna Mostafa; 09.08.2018
comment
Как правило, если родительская активность находится в фоновом режиме, но состояние еще не сохранено, самое время показать диалоги. Когда пользователь вернется в приложение, диалоговое окно будет видно. - person Ben P.; 09.08.2018
comment
Действительно? это не то, что я вижу, я получаю illegalStateException Может ли это быть из-за того, что я использую show(), а не commitNow(), поэтому к тому времени, когда запланировано шоу, состояние уже было сохранено? - person Menna Mostafa; 09.08.2018
comment
Предположим, что вы получаете исключение can not be performed after onSaveInstanceState(), тогда да. Важно то, сохранил ли диспетчер фрагментов свое состояние; если это не так, то любая транзакция безопасна. Но вы правы, я могу поверить, что есть сценарии, в которых можно добавить транзакцию в очередь, а затем заставить диспетчер фрагментов сохранить свое состояние до того, как эта транзакция будет обработана. - person Ben P.; 09.08.2018
comment
Итак, мне удалось заставить его работать, выполнив следующие действия: if(!manager.isStateSaved() && !savedInstanceStateDone) { transaction.commitNow(); } Это выглядит нормально? - person Menna Mostafa; 10.08.2018
comment
Мне кажется разумным - person Ben P.; 10.08.2018

Возможно, вы могли бы создать новый метод в своем базовом классе диалога:

public void showIfResumed(FragmentActivity activity, String tag) {
    if (/* your check here, e.g. activity.getLifecycle()...  */) {
        show(activity.getSupportFragmentManager(), tag);
    }
}

И затем, в своей деятельности, вместо того, чтобы звонить show(getSupportFragmentManager(), TAG), вы могли звонить showIfResumed(this, TAG). Вам по-прежнему придется заменять каждый вызов show() вызовом showIfResumed(), но это значительно уменьшит дублирование кода.

person Ben P.    schedule 09.08.2018
comment
спасибо, это был мой следующий вариант, но является ли решение доступа к списку фрагментов плохой практикой? потому что это позволяет мне ничего не менять из DialogFragmentBase. В то время как с вашим решением мне пришлось бы многое изменить, и я надеюсь на решение, которое можно легко реализовать, вместо того, чтобы зависеть от разработчиков, вызывающих showIfResumed, что не является интуитивно понятным. - person Menna Mostafa; 09.08.2018
comment
@MennaMostafa Я бы сказал, что то, что вы разместили в своем вопросе, является плохой практикой. Почему ваш фрагмент опрашивает менеджера фрагментов о других фрагментах, которыми он управляет? Что произойдет, если нет предыдущего фрагмента? Что произойдет, если предыдущий фрагмент не присоединен к активности? И т. д. Вы могли бы потенциально изменить свой существующий метод show() в базовом классе, чтобы генерировать UnsupportedOperationException, чтобы любой, кто его использовал, сразу увидел, что они не должны. - person Ben P.; 09.08.2018