W androidzie można tworzyć dynamiczne interfejsy poprzez łączenie w moduły komponentów UI z aktywnościami. Moduły takie tworzy się za pomocą klasy Fragment, która zachowuje się jak zagnieżdżone aktywności definiujące własne layouty oraz zarządzające własnym cyklem życia. Kiedy fragment określa własny layout, można skonfigurować go w różne kombinacje z innymi fragmentami wewnątrz aktywności w celu zmodyfikowania konfiguracji layoutu na innych ekranach. Fragmenty są wspierane od Androida 1.6
Tworzenie fragmentu Fragmenty są częścią aktywności która posiada własny cykl życia, otrzymuje własne zdarzenia i która może być dodana i wyłączana podczas działania aktywności nadrzędnej. Support Library Support library jest zbiorem kodu który zawiera metody umożliwiające utrzymanie wstecznej kompatybilności ze starszymi wersjami Androida. Każda biblioteka jest kompatybilna ze specyficznym poziomem API. Dla przykładu nasz program będzie kompatybilny z Android 1.6, czyli API level 4 oraz w górę. Oznacza to, że musimy za pomocą SDK Managera doinstalować. W managerze wchodzimy w 'Extras', klikamy Android support library i instalujemy.
Aby móc wykorzystać tą bibliotekę w projekcie: Tworzymy folder libs/ w naszym projekcie Kopiujemy odpowiedni plik JAR z SDK (extras/android/support/v4/android-support-v4.jar) androida do folderu libs/ Prawym przyciskiem na JAR i wybieramy Build Path Add to Build Path Oczywiście możemy wykorzystać class Fragment wbudowaną w API bez wykorzystywania Suport Library odcinamy sobie wtedy jednak kompatybilność ze wszystkimi wersjami starszymi od API level 11.
Tworzenie fragmentu Aby stworzyć fragment musimy rozszerzyć klasę Fragment a potem nadpisać podstawowe metody cyklu życiowego w sposób analogiczny do tego, co robiliśmy z klasą Activity. Jedyną wymaganą metodą do uruchomienia Fragmentu jest nadpisanie klasy oncreateview(): import android.os.bundle; import android.support.v4.app.fragment; import android.view.layoutinflater; import android.view.viewgroup; public class ArticleFragment extends Fragment { @Override public View oncreateview(layoutinflater inflater, ViewGroup container, Bundle savedinstancestate) { // Zwieksz layout o dodawany fragment return inflater.inflate(r.layout.article_view, container, false);
Dodanie Fragmentu do aktywności wykorzystując XML Każda instancja klasy Fragment musi być związana z klasą rodzicem FragmentActivity co można uzyskać definiując każdy fragment aktywności w pliku XML. FragmentActivity jest aktywnością dostarczaną z Support Library dla API starszych niż 11. W przypadku docelowego API będącego na poziomie 11 lub więcej, można wykorzystać normalną klasę Activity.
Przykład layoutu który dodaje dwa fragmenty do aktywności gdy ekran jest uznawany za duży ( large ): res/layout-large/news_articles.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <fragment android:name="com.example.android.fragments.headlinesfragment" android:id="@+id/headlines_fragment" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.android.fragments.articlefragment" android:id="@+id/article_fragment" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
Następnie musimy zastosować layout w aktywności: import android.os.bundle; import android.support.v4.app.fragmentactivity; public class MainActivity extends FragmentActivity { @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.news_articles); Dodając fragment do layoutu za pomocą pliku XML nie możemy usunąć fragmentu w czasie działania aplikacji aby móc zamieniać fragmenty w czasie interakcji z użytkownikiem należy dodać wszystkie potrzebne fragmenty w momencie startowania aplikacji.
Budowanie elastycznego UI Budując aplikację wspierającą duża ilość rozmiarów ekranów możemy wykorzystywać te same fragmenty w różnych konfiguracjach layoutów. Dodawanie fragmentu do aktywności w czasie działania aplikacji Zamiast dodawać fragment do aktywności za pomocą pliku layoutu możemy dodać fragment do aktywności w czasie działania aplikacji. Wykorzystuje się to do zmiany fragmentów podczas działania aktywności.
Aby dodać/usunąć fragment musimy zastosować klasę FragmentManager, stworzyć transakcję: FragmentTransaction, która pozwala nam między innymi dodać, usunąć bądź zamienić fragmenty. Fragmenty które chcemy zastępować bądź usuwać powinniśmy dodawać gdy aktywność jest w stanie oncreate(). Fragmenty muszą posiadać pojemnik View wewnątrz layouta. W pojemniku tym znajdą się elementy fragmentu: res/layout/news_articles.xml: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" />
Aby wygenerować transakcję musimy odwołać się wewnątrz aktywności do getsupportfragmentmanager(). Otrzymując FragmentManagera możemy wywołać z niego metodę begintransaction() którą tworzymy FragmentTransaction. Następnie możemy wywołać z niego metodę add() i dodać fragment. Za pomocą FragmentTransaction możemy wykonać kilka transakcji w aktywności, kiedy ustawimy wszystko tak jak chcemy, wywołujemy commit().
import android.os.bundle; import android.support.v4.app.fragmentactivity; public class MainActivity extends FragmentActivity { @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.news_articles); // Sprawdzamy czy aktywnosc uzywa layoutu z // fragment containerem if (findviewbyid(r.id.fragment_container)!= null) { // Jesli wychodzimy z innego stanu odtwarzajacego aktywnosc // nie musimy nic robic i powinnismy wywolac return // poniewaz mogą powstac nachodzace na siebie fragmenty if (savedinstancestate!= null) { return; // Nowy fragment HeadlinesFragment firstfragment = new HeadlinesFragment(); //Pobierz dodatkowe instrukcje z Intenta i przenies je do fragmentu firstfragment.setarguments(getintent().getextras()); // Dodaj fragment do layoutu 'fragment_container' getsupportfragmentmanager().begintransaction().add(r.id.fragment_container, firstfragment).commit();
Zastępowanie fragmentu innym: Procedura zmiany jest podobna do dodawania, ale zamiast.add() stosujemy metodę replace(). Zamieniając fragmenty należy umożliwić użytkownikowi powrócić do poprzedniego stanu. Aby umożliwić taką nawigację wstecz należy wywołać addtobackstack() przed commitowaniem transakcji. W momencie usuwania bądź zastępowania fragmentu, przy wywołaniu metody addtobackstack(), fragment ten jest stopowany, a nie niszczony. W momencie nawigacji wstecz fragment restartuje się. Gdy nie dodamy tej transakcji do stosu wówczas fragment jest niszczony. AddToBackStack() przyjmuje dodatkowy parametr typu string który pozwala nadać unikatowe imię transakcji.
// Stwórz fragment i wskaz co powinien wyswietlac ArticleFragment newfragment = new ArticleFragment(); Bundle args = new Bundle(); args.putint(articlefragment.arg_position, position); newfragment.setarguments(args); FragmentTransaction transaction = getsupportfragmentmanager().begintransaction(); //Zastąp cokolwiek znajduje się w fragment_container za pomoca newfragment. Dodaj transakcje // do stosu transaction.replace(r.id.fragment_container, newfragment); transaction.addtobackstack(null); // Commit! transaction.commit();
Komunikacja z innymi fragmentami Każdy fragment powinien być tworzony jako samo-zawierający się, modularny komponent który implementuje swój layout oraz zachowanie. Po stworzeniu takich fragmentów możemy je doczepiać do aktywności i połączyć z logiką aplikacji. Aby umożliwić komunikację między fragmentami musimy zastosować komunikację przez Aktywność, fragmenty nigdy nie mogą komunikować się bezpośrednio ze sobą. Tworzenie interfejsu Aby umożliwić komunikowanie Fragmentu z Aktywnością należy zdefiniować interfejs w klasie Fragment i zaimplementować go wewnątrz Aktywności. Fragment generuje interfejs w metodzie onattach():
public class HeadlinesFragment extends ListFragment { OnHeadlineSelectedListener mcallback; // Glowna aktywnosc musi implementowac ten interfejs public interface OnHeadlineSelectedListener { public void onarticleselected(int position); @Override public void onattach(activity activity) { super.onattach(activity); // Upewniamy sie ze aktywnosc glowna implementuje interfejs // jesli nie, wyrzuca wyjatek try { mcallback = (OnHeadlineSelectedListener) activity; catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener");
Teraz fragment może wysyłać wiadomości wywołując onarticleselected() wykorzystując instancje mcallback interfejsu OnHeadLineSelectedListener. Na przykład, gdy użytkownik kliknie element listy, fragment wykorzystuje interfejs aby wysłać zdarzenie do głównej aktywności: @Override public void onlistitemclick(listview l, View v, int position, long id) { // wyslij informacje do glownej aktywnosci mcallback.onarticleselected(position);
Implementacja interfejsu: Aby otrzymać informację z fragmentu, główna aktywność musi zaimplementować interfejs zdefiniowany w klasie fragmentu, dla przykładu: public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{... public void onarticleselected(int position) { // Uzytkownik wybral jakiś element z listy, // Kod w tym miejscu powinien zrobic cos aby wyswietlic
Dostarczanie wiadomości do fragmentu: Główna aktywność może dostarczać wiadomości do fragmentu poprze z tworzenie instancji Fragmentu za pomocą findfragmentbyid(), a potem wywołując jej publiczne metody.
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{... public void onarticleselected(int position) { // Uzytkownik wybral z listy artykul w HeadlineFragment // Wyswietl artykul ArticleFragment articlefrag = (ArticleFragment) getsupportfragmentmanager().findfragmentbyid(r.id.article_fragment); if (articlefrag!= null) { // Jesli fragment artykulu jest dostepny, jestesmy w podwojnym layoucie // Zaktualizuj fragment wywolujac jego metode articlefrag.updatearticleview(position); else { // W innym przypadku jestesmy w pojedyncyzm layoucie i musimy zamieniac fragmenty // Stworz fragment i przekaz mu argumenty dla wybranego artykulu ArticleFragment newfragment = new ArticleFragment(); Bundle args = new Bundle(); args.putint(articlefragment.arg_position, position);
newfragment.setarguments(args); FragmentTransaction transaction = getsupportfragmentmanager().begintransaction(); // Zamien rzeczy z fragment_containera na newfragment, zapamietaj poprzedni stan transaction.replace(r.id.fragment_container, newfragment); transaction.addtobackstack(null); // Commit the transaction transaction.commit();