5

Короткая версия:onResume() не вызывается на ViewPager фрагмента при использовании пользовательского загрузчика

У меня есть фрагмент, который поддерживает ViewPager для отображения двух других фрагментов, давайте назовем их FragmentOne и FragmentTwo. При запуске приложения отображается FragmentOne, а FragmentTwo - вне экрана, становится видимым только по мере того, как один просматривает вид слева.

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

У меня есть проблема, когда FragmentOne начинается обычай Loader тогда onResume() не дозвонились на FragmentTwo, пока она не станет полностью видимым.

Screenshot showing the problem

Вопросы:

Является ли это проблемой с моим кодом или ошибка в библиотеке поддержки 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" /> 

ответ

11

Это, кажется, ошибка в библиотеке поддержки. Ниже приведенное изменение решает проблему.

// FragmentOne.java 

@Override 
public void onResume() { 
    super.onResume(); 
    Handler handler = getActivity().getWindow().getDecorView().getHandler(); 
    handler.post(new Runnable() { 
     @Override public void run() { 
      // initialize the loader here! 
      getLoaderManager().initLoader(0, null, FragmentOne.this); 
     } 
    }); 
} 
+0

Я, имеющий точно такой же вопрос, как вы, вы решение работает, но с производительность - цена задержки. Как вы нашли решение? –

+0

Почему вы отправляете его в обработчик? – Xenione

+2

@Xenione Это всего лишь обходной путь, но он безопасен в использовании. Вот мои мысли. Мы видим, что вызов initLoader() в первом фрагменте предотвращает вызов onResume() на другие фрагменты (скорее всего, из-за ошибки в библиотеке). В этом нет такого вызова, все методы onResume() вызывают в одном кадре выполнения. Логически, если мы хотим, чтобы среда вызывала методы onResume() во всех фрагментах, мы должны избегать вызова initLoader(). Логически, чтобы инициализировать загрузчик сразу после onResume(), нам нужно сделать это в следующем кадре исполнения. Именно здесь handler.post() помогает много. –

3

Другой обходной путь, который работал для меня было использовать менеджер в MainFragment в загрузчик:

getParentFragment().getLoaderManager().initLoader(0, null, this); 
+0

Я искал решение для своего вопроса http://stackoverflow.com/q/34503845/2277631, когда нашел такой же http://stackoverflow.com/q/17885053/2277631, который привел меня сюда. Кажется, это просто работает. Вы обнаружили какой-либо недостаток использования родительского фрагмента для запуска загрузчика? –

Смежные вопросы