Android ViewPager mit Fragments und der CursorLoader

Voraussetzungen:

  • Grundlegende Kenntnisse im Umgang mit Java
  • Überschreiben von Methoden
  • Grundsätzlicher Umgang mit Fragments (nicht zwingend)

Ziel:

  • Eine Activity indem man 5 Fragments nach links und rechts “wischen” kann.
  • Davon zwei Fragments aus XML Res.
  • Drei ListFragments von denen eines aus einem statischen Array gefüllt wird und zwei mit Inhalt aus dem ContentProvider. In diesem Fall die Namen der Audio Künstler und die Namen der Audio Alben welche sich auf dem Gerät befinden.
  • Dazu kommt das Einblenden des Titels des aktuell eingeblendeten Fragments über diesem.

Vorbereitung:

Als erstes wird ein Android Projekt erstellt. Die min. Api kann API 4 bzw. Android 1.6 sein, da wird die Android support.v4.lib benutzen werden, welche wie der Name schon sagt, bis API 4 kompatibel ist. Die Main Activity kann direkt miterzeugt werden.

Wenn das Projekt erstellt wurde und Eclipse damit fertig ist den “gen” Ordner zu füllen, klickt man mit der rechten Maustaste auf das Projekt.

Android Tools –> Add Suppot Library…

Nun sollte Eclipse sich daten von Google holen und das Package “Android Support, revision 7″ zur Intstallation anbieten. Dies wird mit Accept und Intstall bestätigt. Nun erscheinen im Projekt die Punkte “Android Dependencies” und “Referenced Libraries” in denen sich das JAR android-support-v4.jar befinden sollte.

XML Layout der Main Activity:

<?xml version=“1.0″ encoding=“utf-8″?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

android:layout_width=“fill_parent”

android:layout_height=“fill_parent”

android:orientation=“vertical” >

<TextView

android:layout_width=“fill_parent”

android:layout_height=“wrap_content”

android:text=“@string/main_textview_one” />

<android.support.v4.view.ViewPager

android:id=“@+id/viewpager”

android:layout_width=“fill_parent”

android:layout_height=“fill_parent” >

<android.support.v4.view.PagerTitleStrip

android:layout_width=“fill_parent”

android:layout_height=“wrap_content”

android:layout_gravity=“top” />

</android.support.v4.view.ViewPager>

</LinearLayout>

Komponenten aus der Support lib müssen im XML mit vollem Namen geschrieben werden. In unserem Fall müssen wird also schreiben android.support.v4.view.ViewPager schreiben. Dann wird eine id vergeben und wie immer die Höhe und Breite dieses Widgets. Das würde im Grunde schon reichen um einen ViewPager zu benutzen, aber das wir auch den Titel der einzelnen Fragments anzeigen wollen kommt auch der PagerTitleStrip zum Einsatz. Dieses wird auch wieder in der Höhe und Breite festgelegt und GANZ  WICHTIG mit android:layout:gravity auf “top” oder “bottom” gesetze, je nach dem ob der Titel oben oder unten stehen soll.

Dabei ist noch wichtig zu wissen, dass der PagerTitleStrip ein Kind des ViewPager Elements sein muss.

Die PageAdapter Klasse

Wenn der ViewPager benutzt wird, muss die PagerAdapter Klasse von “PagerAdapter” abgeleitet werden und in diesem Fall auch anders benannt werden. Da wir in diesem Fall jedoch mit Fragments arbeiten werden, muss unser PagerAdapter von FragmentPagerAdapter abgeleitet werden.

public class PageAdapter extends FragmentPagerAdapter {

 

Dabei müssen diese Methoden mindestens implementiert werden:

public PageAdapter(FragmentManager fm) {

super(fm);

// TODO Auto-generated constructor stub

}

@Override

public Fragment getItem(int arg0) {

// TODO Auto-generated method stub

return null;

}

@Override

public int getCount() {

// TODO Auto-generated method stub

return 0;

}

 

Im Constructor wird der FragmentManager übergeben, dazu aber später bei der Main Activity mehr.

Bei der Methode “getItem(int arg0)” wird die Position des abgerufenen Fragments übergeben und erwartet ein Fragment als Rückgabewert.

Die Methode getCound() erwartet als Rückgabewert die Anzahl der Fragmente die im ViewPager untergebracht werden sollen. Da Bei unserem Beispiel 5 Fragment dargestellt werden sollen legen wir uns eine

private final int NUM_PAGES = 5;

 

Variable an. Man kann diesen Wert nachtürlich auch über eine Methode aus einer anderen Klasse holen und so die Anzahl an Fragments “dynamisch” regeln.

Für den PagerTitleStrip müssen wir nun noch die Methode

public CharSequence getPageTitle(int position) {

 

überschreiben (Strg + 3 -> Enter). In diesem Fall legen wir eine Array mit den Titeln der Fragments an

private final String[] titles = { “Page1“, “Page2“, “Früchte“,”Artists“,”Albums“};

 

und geben

return titles[position];

zurück.

Die Erzeugung der Fragments

In diesem Beispiel habe ich zwei Arten von Fragments hergenommen. Zum einen das normale Fragment und zum anderen das ListFragment.

Fragment erzeugen:

Es wird eine Klasse erstellt welche von Fragment abgeleitet wird. Diese bekommt eine Variable

private int fragmentNR;

 

welche im Constructor mit einem Wert belegt wird.

public PageFragment(int nr) {

this.fragmentNR = nr;

Nun muss nur noch die Methode onCreateView überschrieben werden. Diese gibt einen View zurück. Man könnte entweder ein View per Code erzeugen oder wie in diesem Beispiel per XML. Dafür wird per if bzw. else if Abfrage überprüft welches Layout gefordert wird und “aufgeblasen” werden muss.

View v = new View(getActivity());

if (fragmentNR == 0)

v = inflater.inflate(R.layout.page_one, container, false);

else if (fragmentNR == 1)

v = inflater.inflate(R.layout.page_two, container, false);

return v;

Damit ist das Fragment fertig zum erzeugen. Dies geschieht in der getItem(int pos) Methode in der PageAdapter Klasse. Die ersten zwei Fragments werden von dem erklärten Typ sein.

public Fragment getItem(int pos) {

if (pos < 2) {

PageFragment f = new PageFragment(pos);

return f;}

 

ListFragment erzeugen:

Das ListFragment wird von der Gleichnamigen Klasse abgeleitet

public class PageListFragment extends ListFragment implements

LoaderCallbacks<Cursor>

und das Interface LoaderCallback<Cursor> angehängt. Dieses Interface implementiert 3 Methoden:

@Override

public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {

return loader;

}

@Override

public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {

}

@Override

public void onLoaderReset(Loader<Cursor> arg0) {

}

Die Methoden werden auch in dieser Folge aufgerufen. Dazu später mehr.

Wie schon im normalen Fragment belegen wir auch in dem ListFragment im Constructor eine Variable mit einem Zahlenwert, welcher vom FragmentPagerAdapter übergeben wird.

public PageListFragment(int nr) {

this.listNr = nr;

}

Nun überschreiben wir die onCreate(Bundle) Methode, diese ist nicht bei ListFragment zu finden sondern bei Fragment.

In der onCreate(Bundle) Methode erzeugen wir nun je nach angeforderten ListFragment den richtigen ListAdapter.

if (listNr == 0) {ArrayAdapter<String> mAdapter = new ArrayAdapter<String>(

getActivity(),android.R.layout.simple_list_item_1, fruit);

setListAdapter(mAdapter);

Wenn im Constructor die “0″ übergeben wird benutzten wir einen einfach ArrayAdapter um das Array

private final String[] fruit = { “Bananen“, “Apfle“, “Erdbeere“,

Kirschen“, “Mangos” };

in einer Liste darzustellen. Der ArrayAdapter braucht als Übergabewert:

  • Context: getActivity()
  • ein Layout für ein ListItem: Hier ein Android Standardlayout
  • ein Array des Types String da festgelegt wurde ArrayAdapter<String>

Jetzt muss nur noch der ListAdapter des ListFragments gesetzt werden mit “setListAdapter(mAdapter)”.

Wenn im Constructor jedoch 1 oder 2 angefordert werden, dann sieht alles ein wenig anders aus. Als erstes muss eine Klassenvariable erstellt werden.

private SimpleCursorAdapter mCursorAdapter;

 

Der SimpleCursorAdapter braucht folgende Übergabewerte:

  • Context: getActivity()
  • ein Layout für ein ListItem: Hier ein Android Standardlayout
  • einen Cursor, da aber noch keiner vorhanden ist, wird hier “null” übergeben. Später wird er ausgetauscht.
  • ein String Array mit den Strings aus dem Cursor welche dargestellt werden sollen.
  • ein int Array mit der id des TextViews auf den die Strings geschrieben werden sollen
  • als letzets noch “int flags”: Das wird nicht gebraucht und mit 0 belegt.

Jetzt wird wieder mit setListAdapter(mCursorAdapter); der Adapter für das ListFragment gesetzt und der Loader Aufgerufen.

Man holt sich zuerste den LoaderManager mit getLoaderManager() und ruft die Methode initLoader(0, null, this) auf.  Der LoaderManager ruft jetzt die Methode

public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1)

 

auf und übergibt in unserem Beispeil die int “0″ und das Bundle “null”. Zurückgegeben wird ein Loader des Typs Cursor. Nun erstellen wir also diesen Loader:

Loader<Cursor> loader = new CursorLoader(getActivity(), mMediaSource[listNr],

null, null, null, null);

Der CursorLoader braucht folgende Übergabewerte:

  • Context
  • Uri der Quelle
    private Uri[] mMediaSource = {null, MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI};
  • Ein String Array welches Festlegt welche Strings aus dem ContentProvider benötigt werden. Bei null werden Alle im Cursor vorhanden sein. Dies kann bei viel Content zu schlechter Performance führen.
  • Die nächsten zwei Werte dienen zum Filtern der Ergebnisse. Werd ich hier nicht näher erläutern.
  • Der letzte Wert entscheidet über die Sortierung. Es kann z.B.

String sortOrder = MediaStore.Audio.Media.ARTIST + ” COLLATE LOCALIZED ASC“;

 

Wenn der LoaderManager fertig ist und der Loader erstellt wurde, wird die Methode onLoadFinished(Loader<Cursor> loader, Cursor cursor) aufgerufen. Nun muss man nur noch den Cursor des SimpleCursorAdapter mit .swapCursor(cursor) austauschen gegen den neuen und schon wird die Liste angezeigt.

Abschließend muss man noch in der Methode onLoaderReset(..) den Cursor im SimpleCursorAdapter gegen den Wert “null” austauschen um den Cursor wieder löschen zu können.

Jetzt muss nur noch im PageAdapter eine Instanz dieses Fragments erstellt werden

else if (pos == 2) {

PageListFragment lf = new PageListFragment(0);

return lf;

} else if (pos == 3) {

PageListFragment lf = new PageListFragment(1);

return lf;

} else if (pos == 4) {

PageListFragment lf = new PageListFragment(2);

return lf;

} else {

PageFragment f = new PageFragment(pos);

return f;

}

Die Main Activity:

Da wir hier mit einem FragmentPageAdapter arbeiten, brauchen wir eine Activity welche einen FragmentManager hat. Dies ist in der Klasse Activity nicht der Fall sondern in der Klasse FragmentActivity. Dem entsprechend müssen wir die Main Klasse von FragmentActivity ableiten und nicht von Activity wie sonst.

public class Main extends FragmentActivity {

Anschließend muss wie gewohnt per setContentView() das entsprechende Layout geladen werden.

Jetzt folgt noch die Instanziierung unserer PageAdapter Klasse per

PageAdapter mPageAdapter = new PageAdapter(getSupportFragmentManager());

 

wie man sehen kann wird der FragmentManager an unseren Adapter übergeben.

HINWEIS: Wenn mit der support.v4.lib gearbeitet wird, wird der FragmentManager per getSupportFragmentManager() aufgerufen. Falls jedoch die FragmentActivity ab API11 verwendet wird, wird der FragmentManager per getFragmentManager() aufgerufen.

Nun noch der ViewPager. Dieser wird erstmal per id gefunden

ViewPager mViewPager = (ViewPager) findViewById(R.id.viewpager);

und dann wird unser Adapter zugewiesen

mViewPager.setAdapter(mPageAdapter);

Jetzt sind wir fertig und es sollte alles wie gewünscht funktionieren.

 

Das ganze Project könnt Ihr euch hier downloaden: ViewPagerTutorial

 

About the author:

Ich bin der Michael, 27 Jahre alt und studiere Technische Redaktion an der HS Merseburg. In diesem Studiengang lernt man technische Sachverhalte einfach und verständlich wiederzugeben. Als kleine Übung verfasse ich ab und an Artikel. Ich programmiere seit ca. einem halben Jahr für die Android Plattform.

You can leave a response, or trackback from your own site.

2 Kommentare zu “Android ViewPager mit Fragments und der CursorLoader”

  1. mephisto-online sagt:

    Gratuliere! Funktioniert prächtig und ist eigentlich genau das, was ich gerade brauche (1. Seite Textview mit HTML, 2.Seite SurfaceView mit ein wenig drawings, 3. Seite Liste mit Elementen, jeweils ein Bitmap und einen kurzen Text pro Zeile, 4. Seite Config). Es ist mir aber absolut schleierhaft, wo ich da was einbauen oder modifizieren kann (z.B. HTML-setText oder die Attribute der Liste).
    Schade! Bin vielleicht noch nicht lange genug im Android-Geschäft…

  2. Sascha sagt:

    Und wie würde man nun Funktionalität bei einem Fragment einbauen, z.B. einen Button welcher auf Klick eine Activity startet?

NEUEN KOMMENTAR HINZUFÜGEN