5

Krótka wersja:onResume() nie wezwał fragmentu ViewPager przy użyciu niestandardowego Loader

Mam fragment, który utrzymuje ViewPager do wyświetlania dwóch innych fragmentów, niech je FragmentOne i FragmentTwo zadzwonić. Po uruchomieniu aplikacja jest widoczna, a FragmentTwo jest poza ekranem, staje się widoczna tylko jako przesuwa widok w lewo.

Normalnie onStart() i onResume() są natychmiast wywoływane dla obu fragmentów od razu po uruchomieniu aplikacji.

Mam problem jest, gdy FragmentOne rozpoczyna zwyczaj Loader następnie onResume() nie sprawdzony na FragmentTwo aż stanie się w pełni widoczne.

Screenshot showing the problem

Pytania:

Czy to problem z moim kodu lub błąd w Bibliotece Wsparcia Android? (Problem nie pojawił się w wersji 12 biblioteki, zaczęło się od wersji 13.)

Jeśli jest to błąd z rewionu 13 i 18, czy istnieje obejście problemu?

Czy jest coś nie tak z moim niestandardowym Loader?

Długa wersja:

I zbudowali przykładowej aplikacji, który pokazuje problem. Próbowałem zredukować kod do absolutnego minimum, ale wciąż jest dużo, więc proszę o zachowanie mnie.

Mam MainActivity, który ładuje MainFragment, który tworzy ViewPager. Dla mojej aplikacji ważne jest, aby ViewPager był obsługiwany przez Fragment zamiast działania.

MainFragment tworzy FragmentPagerAdapter, który z kolei tworzy fragmenty FragmentOne i FragmentTwo.

Zacznijmy ciekawy bit, dwa fragmenty:

FragmentOne jest ListFragment że używa zwyczaj Loader załadować zawartość:

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"); 
     } 
    } 
} 

Jest to Loader, który wydaje się być przyczyną problemu . Komentowanie linii initLoader powoduje, że cykl życia fragmentu działa zgodnie z oczekiwaniami.

FragmentTwo zmienia jego treści na podstawie tego, czy został wywołany onResume() czy nie:

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"); 
    } 
} 

I tu jest nudny reszta kodu.

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(); 
    } 
} 

Układ 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(); 
     } 
    } 
} 

Układ 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" /> 

Odpowiedz

11

Wygląda na to, że jest to błąd w bibliotece pomocy technicznej. Poniższa zmiana rozwiązuje problem.

// 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

Mam dokładnie taki sam problem jak jesteś, prace rozwiązanie, ale z wydajność - cena opóźnienia. Jak znalazłeś rozwiązanie? –

+0

dlaczego umieszczasz go w programie obsługi? – Xenione

+2

@Xenione Jest to tylko obejście, ale można go bezpiecznie używać. Oto moje myśli. Widzimy, że wywołanie funkcji initLoader() w pierwszym fragmencie uniemożliwia wywołanie metody onResume() na innych fragmentach (najprawdopodobniej z powodu błędu w bibliotece). W przypadku braku takiego wywołania wszystkie metody onResume() są wywoływane w ramach pojedynczego wykonania. Logicznie, jeśli chcemy, aby framework wywoływał metody onResume() we wszystkich Fragmentach, musimy unikać wywoływania metody initLoader(). Logicznie, aby zainicjować program ładujący natychmiast po onResume(), musimy to zrobić w następnej ramce wykonania. Tutaj bardzo pomaga handler.post(). –

3

Innym obejście że pracował dla mnie było wykorzystanie MainFragment menedżer ładowarka:

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

Szukałem rozwiązania dla mojego pytania http://stackoverflow.com/q/34503845/2277631, kiedy znalazłem podobny http://stackoverflow.com/q/17885053/2277631, który doprowadził mnie tutaj. To wydaje się po prostu działać. Czy znalazłeś jakąkolwiek wadę używania fragmentu rodzica do zainicjowania programu ładującego? –

Powiązane problemy