Versione corta:onResume() non ha invitato ViewPager frammento quando si utilizza Loader personalizzato
Ho un frammento che mantiene un ViewPager
per la visualizzazione di altri due frammenti, diamo loro FragmentOne
e FragmentTwo
chiamano. Quando si avvia l'app FragmentOne
è visibile e FragmentTwo
è fuori schermo, diventando visibile solo quando si passa la vista a sinistra.
Normalmente onStart()
e onResume()
vengono richiamati immediatamente per entrambi i frammenti non appena l'app viene avviata.
Il problema che ho è quando FragmentOne
inizia una consuetudine Loader
poi onResume()
non viene chiamato FragmentTwo
fino a quando diventa completamente visibile.
Domande:
Si tratta di un problema con il mio codice o un bug nel libreria di supporto di Android? (Il problema non si è verificato con la revisione 12 della libreria, è iniziato con la revisione 13.)
Se si tratta di un bug nelle revisioni 13 e 18, c'è una soluzione?
C'è qualcosa di sbagliato nella mia consueta Loader
?
Versione lunga:
Ho creato un'applicazione di esempio che illustra il problema. Ho provato a ridurre il codice al minimo, ma è ancora molto, quindi per favore portami dietro.
Ho un MainActivity
che carica uno MainFragment
che crea un ViewPager
. Per la mia app è importante che il ViewPager sia gestito da un frammento anziché da un'attività.
MainFragment
crea una FragmentPagerAdapter
che a sua volta crea i frammenti FragmentOne
e FragmentTwo
.
Cominciamo con il bit interessante, i due frammenti:
FragmentOne
è un ListFragment
che utilizza un personalizzato Loader
per caricare il contenuto:
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");
}
}
}
È che Loader
che sembra causare il problema . Commentando la riga initLoader, il ciclo di vita dei frammenti funziona come previsto.
FragmentTwo
cambia il suo contenuto in base al fatto onResume()
è stato invocato o no:
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");
}
}
E qui è il resto noioso del codice.
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();
}
}
layout 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();
}
}
}
layout 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" />
Sto avendo esattamente lo stesso problema come siete, lavori soluzione, ma con le prestazioni - prezzo ritardo. Come hai trovato la soluzione? –
perché lo metti nel gestore? – Xenione
@Xenione Questa è solo una soluzione, ma è sicura da usare. Ecco i miei pensieri. Vediamo che chiamare initLoader() nel primo Fragment impedisce di chiamare onResume() su altri frammenti (molto probabilmente a causa di un bug nella libreria). In questa chiamata non esiste alcuna chiamata, tutti i metodi onResume() vengono richiamati in un singolo frame di esecuzione. Logicamente, se vogliamo che framework invochi i metodi onResume() in tutti i Fragments, dobbiamo evitare di chiamare initLoader(). Logicamente, per inizializzare il loader immediatamente dopo onResume() dobbiamo farlo nel prossimo frame di esecuzione. Questo è dove handler.post() aiuta molto. –