Восстановление докладчиков для фрагментов ViewPager (MVP)
Я пытаюсь реорганизовать существующее приложение для использования архитектуры MVP. Одно из мероприятий имеет ViewPager
с тремя фрагментами. Каждый фрагмент связан с докладчиком. Чтобы быть точным - каждый докладчик, когда он создан, получает View
работать с, то есть Fragment
, Пока я создаю этих докладчиков внутри ViewPager
Адаптер - конкретно в getItem(int position)
метод.
Fragment fragment = FirstFragment.newInstance();
FirstPresenter presenter = new FirstPresenter(repo, (FirstContract.View) fragment, projectId, userId);
Проблема, с которой я сталкиваюсь, заключается в том, что если процесс завершается, а затем перезапускается, ViewPager
имеет свой жизненный цикл и, следовательно, getItem
больше не вызывается - фрагменты воссоздаются автоматически без предъявителей.
Есть ли известное решение этой проблемы?
4 ответа
Поскольку до сих пор нет идеального ответа на этот вопрос, я подумал, что было бы неплохо поделиться своим временным решением.
Как я уже упоминал в одном из комментариев, цель здесь - восстановить ViewPager
от процесса уничтожения и в идеале держите инициализацию Presenter отделенной от View. На данный момент мое решение состоит в том, чтобы переопределить restoreState(Parcelable state, ClassLoader loader)
внутри FragmentStatePagerAdapter
, осмотреть государство Parcelable
аналогично фактической реализации restoreState
метод, то для каждого фрагмента определенного класса, я могу инициализировать докладчик и назначить ему представление.
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle)state;
bundle.setClassLoader(loader);
Iterable<String> keys = bundle.keySet();
for (String key: keys) {
if (key.startsWith("f")) {
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
if (f instanceof FirstFragment) {
new FirstPresenter(repo, (FirstContract.View) f, projectId, userId);
}
} else {
Log.w(TAG, ".restoreState() - bad fragment at key " + key);
}
}
}
}
super.restoreState(state, loader);
}
Прежде всего, мое решение включает в себя FragmentManager.FragmentLifecycleCallbacks
, который является
Интерфейс обратного вызова для прослушивания изменений состояния фрагмента, которые происходят в данном FragmentManager
и придерживается разделения интересов, как я бы сказал, в схеме Android Architecture Blueprints.
Activity
создаетPresenter
, проходя вдольView
/Fragment
, чтобыPresenter
знает егоView
и кроме того устанавливает себя егоPresenter
В Activity
"s onCreate
Я регистрирую FragmentLifecycleCallbacks
слушатель, позвонив это
private void registerFragmentsLifecycleListener() {
// All registered callbacks will be automatically unregistered when
// this FragmentManager is destroyed.
getSupportFragmentManager.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
// Called after the fragment has returned from its onActivityCreated
@Override
public void onFragmentActivityCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
createPresenter(f);
}
}, false); // true to register callback for all child FragmentManagers
}
Слушатель получает уведомление после Fragment
вернулся из своего onActivityCreated
чтобы убедиться, что только для каждого нового Fragment
экземпляр добавлен ViewPager
новый Presenter
будет создан. Фрагмент может быть прикреплен / отсоединен, его вид может быть создан / уничтожен пару раз, ничего не нужно было делать, но все же получил его Presenter
,
Потому что в случае отдыха (например, поочередно) Fragment
s' onCreate
называется до Activity
один (где FragmentLifecycleCallbacks
слушатель зарегистрирован!), слушатель не может реализовать onFragmentCreated
, должно быть onFragmentActivityCreated
,
Для данного нового Fragment
Например, мы можем определить, какие Presenter
нужно:
private void createPresenter(Fragment fragment) {
if (fragment instanceof WhateverContract.View) {
WhateverContract.Presenter whateverPresenter =
new WhateverPresenter((WhateverContract.View) fragment);
} else if (...){}
}
Presenter
соединяется с его View
/Fragment
в конструкторе
private final WhateverContract.View mView;
public WhateverPresenter(@NonNull WhateverContract.View view) {
mView = checkNotNull(view, "view cannot be null!");
mView.setPresenter(this);
}
а затем может быть запущен в Fragment
s onResume
,
Если что-то не так или улучшается, пожалуйста, дайте мне знать:)
Предложенный ответ не работал для меня с тех пор mFragmentManager
является частным членом FragmentStatePagerAdapter
, Понятия не имею, как это работает для вкислицинов. Вместо этого я просто позвонил получил родительский класс, чтобы сделать restoreState
затем схватил фрагменты с помощью "instantiateItem". Например:
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
// this will load all the fragments again
super.restoreState(state, loader);
// since the fragments are now loaded, instantiate can be used because it just returns them
MyFragmentClass tab1 = (MyFragmentClass) instantiateItem(null, 0);
tab1Presenter.setView(tab1);
tab1.setPresenter(tab1Presenter);
// then just do the same for the other fragments
...
}
Чувствует себя немного хакером, но это работает.
Как упоминалось в комментариях - Presenter должен быть присоединен (и отключен) в методах жизненного цикла Activity/Fragment. Не во внешних классах, потому что только View может подключить-отключить Presenter в соответствующее время. Но хорошей практикой является инициализация Presenter в отдельном классе (или среде внедрения зависимостей), чтобы отделить его от View.