Короткая версия:onResume() не вызывается на ViewPager фрагмента при использовании пользовательского загрузчика
У меня есть фрагмент, который поддерживает ViewPager
для отображения двух других фрагментов, давайте назовем их FragmentOne
и FragmentTwo
. При запуске приложения отображается FragmentOne
, а FragmentTwo
- вне экрана, становится видимым только по мере того, как один просматривает вид слева.
Обычно onStart()
и onResume()
вызывается сразу для обоих фрагментов, как только приложение запускается.
У меня есть проблема, когда FragmentOne
начинается обычай Loader
тогда onResume()
не дозвонились на FragmentTwo
, пока она не станет полностью видимым.
Вопросы:
Является ли это проблемой с моим кодом или ошибка в библиотеке поддержки Android? (Проблема не возникала с ревизией 12 библиотеки, она начиналась с ревизии 13.)
Если это ошибка в версиях 13 и 18, есть ли обходной путь?
Есть что-то неправильно с моим обычным Loader
?
Длинная версия:
Я построил пример приложения, демонстрирующий эту проблему. Я пытался уменьшить код до минимального минимума, но все равно много, поэтому, пожалуйста, несите меня.
У меня есть MainActivity
, который загружает MainFragment
, который создает ViewPager
. Для моего приложения важно, чтобы ViewPager поддерживался фрагментом вместо Activity.
MainFragment
создает FragmentPagerAdapter
, который, в свою очередь, создает фрагменты FragmentOne
и FragmentTwo
.
Давайте начнем с интересной битой, два фрагментом:
FragmentOne
является ListFragment
, который использует пользовательский Loader
для загрузки содержимого:
public class FragmentOne extends ListFragment implements LoaderCallbacks<List<String>> {
private ArrayAdapter<String> adapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1);
setListAdapter(adapter);
setEmptyText("Empty");
}
@Override
public void onResume() {
super.onResume();
// initializing the loader seems to cause the problem!
getLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new MyLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<List<String>> loader, List<String> data) {
adapter.clear();
adapter.addAll(data);
}
@Override
public void onLoaderReset(Loader<List<String>> loader) {
adapter.clear();
}
public static class MyLoader extends AsyncTaskLoader<List<String>> {
public MyLoader(Context context) {
super(context);
}
@Override
protected void onStartLoading() {
forceLoad();
}
@Override
public List<String> loadInBackground() {
return Arrays.asList("- - - - - - - - - - - - - - - - - - - foo",
"- - - - - - - - - - - - - - - - - - - bar",
"- - - - - - - - - - - - - - - - - - - baz");
}
}
}
Это та Loader
, что, кажется, вызывает проблему , Комментирование строки initLoader делает работу жизненного цикла фрагмента, как и ожидалось, снова.
FragmentTwo
меняет свое содержание, основанное на ли onResume()
был вызван или нет:
public class FragmentTwo extends Fragment {
private TextView text;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
text = new TextView(container.getContext());
text.setText("onCreateView() called");
return text;
}
@Override
public void onResume() {
super.onResume();
Log.i("Fragment2", "onResume() called");
text.setText("onResume() called");
}
}
А вот скучный остальной код.
MainActivity
:
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment fragment = new MainFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container, fragment).commit();
}
}
Компоновка activity_main
:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
MainFragment
:
public class MainFragment extends Fragment {
private ViewPager viewPager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.frag_master, container, false);
viewPager = (ViewPager) layout.findViewById(R.id.view_pager);
return layout;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
viewPager.setAdapter(new MyPagerAdapter(getChildFragmentManager()));
}
private static final class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@Override
public int getCount() {
return 2;
}
@Override
public Fragment getItem(int position) {
if (position == 0)
return new FragmentOne();
else
return new FragmentTwo();
}
}
}
Компоновка frag_master
:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Я, имеющий точно такой же вопрос, как вы, вы решение работает, но с производительность - цена задержки. Как вы нашли решение? –
Почему вы отправляете его в обработчик? – Xenione
@Xenione Это всего лишь обходной путь, но он безопасен в использовании. Вот мои мысли. Мы видим, что вызов initLoader() в первом фрагменте предотвращает вызов onResume() на другие фрагменты (скорее всего, из-за ошибки в библиотеке). В этом нет такого вызова, все методы onResume() вызывают в одном кадре выполнения. Логически, если мы хотим, чтобы среда вызывала методы onResume() во всех фрагментах, мы должны избегать вызова initLoader(). Логически, чтобы инициализировать загрузчик сразу после onResume(), нам нужно сделать это в следующем кадре исполнения. Именно здесь handler.post() помогает много. –