Hallo,
ich arbeite gerade an einer App deren MainActivity über ActionBar-Tabs insgesamt 5 Seiten/Fragments verwaltet. Das Ganze arbeitet ähnlich wie die "Apps" Activity/Seite in der Play-Store App von Google. Dort gibt es auch eine Reihe von Tabs (Kategorien, Startseite, Bestseller, etc.) und man kann über einen Klick auf den zugehörigen Tab oder durch links/rechts Swipes zwischen den Fragments wechseln.
Wie bekomme ich es hin, dass der Wechsel zwischen den Tabs/Fragments ebenso flüssig und reibungslos läuft wie in der PlayStore App?
Genau wie bei der PlayStore App haben auch die Tabs/Fragments in meiner App dynamischen Inhalt der erst von einem Server geladen werden muss bevor dieser angezeigt werden kann. Das Problem ist, dass bei meiner App der Wechsel zwischen den Tabs
extrem hakt. Wechselt man also zu einem neuen Tab dauert 1-2 Sekunden bis dieser wirklich angezeigt wird.
Dabei wird wird das Laden der Daten natürlich NICHT im UI-Thread ausgeführt sondern ist über einen AsyncTask in den Hintergrund verlagert. Dennoch bekomme ich keinen flüssigen Seitenwechsel hin.
Die Tabs sind bei mir alle gleich aufgebaut:
Wenn die View geladen wird, ist zunächst nur der ProgressBar sichtbar um zu zeigen, dass etwas passiert. Gleichzeitig wird ein AsyncTask gestartet, dass zwei Dinge übernimmt:
- Die Daten werden geladen
- Die zugehörigen UI wird erstellt, also z.B. eine List für alle Kontakte die vom Server geladen wurden, eine ImageView für jedes geladen Bild oder was auch immer. Die Elemente werden natürlich noch NICHT zur UI hinzugefügt denn ein Zugriff darauf ist aus einem Hintergrundthread nicht möglich. Statt dessen werden die Elemente nur erstellt und zu einem gemeinsamen Container Layout (z.B. einem LinearLayout) hinzugefügt
Sobald der Task fertig ist passiert im UI-Thread nur noch zwei Sachen:
- Die erzeugte ContainerView wird zum ContentRoot hinzugefügt.
- Der ProgressBar wird ausgeblendet und der ContentRoot wird eingeblendet
Der Code hierzu sieht so aus:
public class MyFragment extends Fragment {
private boolean viewLoaded = false;
private boolean dataLoaded = false;
private ViewGroup containerView;
@Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.MyFragment, container, false);
activityProgressBar = (ProgressBar)rootView.findViewById(R.id.activityProgressBar);
contentRoot = (ViewGroup)rootView.findViewById(R.id.contentRoot);
if (dataLoaded) {
showData();
} else {
showActivity(); // ProgressBar und ein- und ContentRoot ausblenden
}
viewLoaded = true;
return rootView;
}
@Override
public final void onDestroyView () {
super.onDestroy();
removeContentContainerFromParent();
viewIsLoaded = false;
}
LoadDataTask loadTask == null;
public void pageSelected() {
// Wird von onPageSelected des ViewPager.SimpleOnPageChangeListener der MainActivity abgerufen um
// das Fragment darüber zu benachrichtigen, dass dieses nun angezeigt wird
if (dataLoaded == false && loadTask == null) {
loadTask = new LoadDataTask();
loadTask.execute();
}
}
private class LoadDataTask extends AsyncTask<Void, Void, Void> {
@Override
protected void onPreExecute() {
removeContentContainerFromParent();
}
@Override
protected final Void doInBackground(Void... params) {
// Daten laden und UI Elemente vorbereiten
...
containerView = new ...
}
@Override
protected void onPostExecute(Void result) {
if (viewLoaded)
showData();
}
}
private void showData() {
contentRoot.addView(containerView);
activityProgressBar.setVisibility(View.GONE);
contentRoot.setVisibilty(View.VISIBLE);
}
}
Alles anzeigen
Obwohl alle anderen Aktionen in den Hintergrundthread verlagert wurde und die Aktivität im UI-Thread auf das absolute Minimum reduziert wurde verläuft der Tab-Wechsel nicht flüssig. Warum?
Dabei kommt es scheinbar nicht darauf an, ob die vorbereiteten UI-Elemente in der ContainerView besonders komplex sind. Auch wenn die ContainerView nur relativ wenige Elemente enthält (z.B. nur ein paar Buttons oder eine ListView mit Kontakten) kommt es bereits zu diesen Hängern.
Das Problem tritt in zwei verschiedenen Varianten auf, je nach dem ob eine Tab-Seite zum ersten Mal oder wiederholt aufgerufen wird.
- Beim ersten Besuch: onCreateView wurde ausgeführt und der LoadTask läuft noch. Die Seite wird also angezeigt und der ProgressBar ist sichtbar. Das funktioniert ohne Probleme. Ist der LoadTask aber fertig verläuft der Wechsel vom ProgressBar zum ContentRoot hakelig. Der ProgressBar bleibt für 1-2 Sekunden stehen und erst dann wird der Inhalt angezeigt.
- Beim wiederholten Besuch: Die View wurde zwischenzeitig durch onDestroyView() freigegeben, der LoadTask ist aber schon komplett. Beim erneuten Besuch der Seite stehen die Daten also direkt in onCreateView() zur Verfügung und der Task muss nicht neu gestartet werden. In diesem Fall dauert einfach der Wechsel zu dieser Seite 1-2 Sekunden.
Insgesamt ist also keine flüssige Navigation zwischen den Seiten möglich. Warum? Wie könnte ich die Aktivität im UI-Thread noch weiter reduzieren? Ist das überhaupt noch möglich?
Die PlayStore App arbeitet scheinbar genauso und ermöglicht dabei einen komplett flüssigen Wechsel zwischen den Seiten? Wie löst man das also?
Vielen Dank!