5

Kurzversion:onResume() nicht aufgerufen ViewPager Fragment, wenn benutzerdefinierte Loader

ich ein Fragment haben, die für die Anzeige von zwei anderen Fragmenten eine ViewPager hält, nennen wir sie FragmentOne und FragmentTwo. Beim Start der App ist FragmentOne sichtbar und FragmentTwo ist off-screen, wird nur sichtbar, wenn man die Ansicht nach links wischt.

Normalerweise werden onStart() und onResume() sofort für beide Fragmente aufgerufen, sobald die App gestartet wird.

Das Problem, das ich habe, ist, wenn FragmentOne eine benutzerdefinierte beginnt Loader dann ist onResume() nicht auf FragmentTwo aufgerufen, bis es vollständig sichtbar wird.

Screenshot showing the problem

Fragen:

Ist das ein Problem mit meinem Code oder einen Fehler in der Bibliothek Android-Support? (Das Problem trat bei Revision 12 der Bibliothek nicht auf. Es begann mit Revision 13.)

Wenn es sich um einen Fehler in den Revisionen 13 und 18 handelt, gibt es einen Workaround?

Ist etwas falsch mit meiner benutzerdefinierten Loader?

Lange Version:

ich eine Beispielanwendung erstellt haben, die das Problem veranschaulicht. Ich habe versucht, den Code auf das Nötigste zu reduzieren, aber es ist immer noch sehr viel, bitte ertragen Sie mit mir.

Ich habe eine MainActivity, die eine MainFragment lädt, die eine ViewPager erstellt. Für meine App ist es wichtig, dass der ViewPager von einem Fragment statt einer Aktivität verwaltet wird.

MainFragment schafft eine FragmentPagerAdapter dass die Fragmente wiederum FragmentOne und FragmentTwo schafft.

die mit dem interessanten Bit Lassen Sie beginnen, die beiden Fragmente:

FragmentOne ist ein ListFragment, die eine benutzerdefinierte verwendet Loader den Inhalt zu laden:

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

Es ist das Loader, die das Problem zu verursachen scheint . Wenn Sie die Zeile initLoader auskommentieren, funktioniert der Fragmentlebenszyklus wie erwartet.

FragmentTwo ändert seinen Inhalt basierend darauf, ob onResume() angerufen worden ist oder nicht:

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

Und hier ist der langweilige Rest des Codes.

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

Antwort

11

Es scheint ein Fehler in der Support-Bibliothek zu sein. Die Änderung unten löst das 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

Ich habe genau das gleiche Problem wie du bist, du Lösung funktioniert, aber mit Leistung - Verzögerung Preis. Wie haben Sie eine Lösung gefunden? –

+0

warum postest du es in den Handler? – Xenione

+2

@Xenione Dies ist nur ein Workaround, aber es ist sicher zu verwenden. Hier sind meine Gedanken. Wir sehen, dass das Aufrufen von initLoader() im ersten Fragment verhindert, dass onResume() auf anderen Fragmenten aufgerufen wird (höchstwahrscheinlich aufgrund eines Fehlers in der Bibliothek). Wenn es keinen solchen Aufruf gibt, werden alle onResume() -Methoden in einem einzigen Ausführungsrahmen aufgerufen. Wenn wir möchten, dass das Framework onResume() -Methoden in allen Fragmenten aufruft, müssen wir logischerweise vermeiden, initLoader() aufzurufen. Um loader unmittelbar nach onResume() zu initialisieren, müssen wir es logischerweise im nächsten Ausführungsrahmen tun. Hier hilft handler.post() sehr. –

3

Eine andere Lösung, die für mich gearbeitet wurde der Lader-Verwaltung des MainFragment zu verwenden:

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

Ich suchte eine Lösung für meine Frage http://stackoverflow.com/q/34503845/2277631, als ich eine ähnliche http://stackoverflow.com/q/17885053/2277631 fand, die mich hierher führte. Das scheint einfach zu funktionieren. Haben Sie einen Nachteil beim Verwenden des Elternfragments gefunden, um den Loader zu initalisieren? –

Verwandte Themen