Uvod

fragment

Fragment je modularni deo aktivnosti, koji ima svoj životni ciklus, prima sopstvene ulazne događaje, možete ga dodati ili ukloniti dok se aktivnost pokreće. Fragment objedinje View i logiku tako da se može jednostavno višekratno koristiti unutar jedne ili više različitih aktivnosti. U aktivnostima može biti više od jednog fragmenta tako da svaki fragment može da predstavlja neki View unutar jedne aktivnosti.
Prednost arhitekture koja koristi fragmenate je što nam fragmenti omogućavaju ponovnu upotrebu koda, sa jednostavnim postupkom pravljenja različitih prikaza za tablete (landscape) i mobilne uredjaje.
Komunikacija izmedju dva fragmenata je prilično komplikovana i može izvesti na dva načina:

Fragmenat vs. Aktivnost

U aplikacijama koje koriste fragmente deo zaduženja aktivnosti se delegira u fragmente, pa bi prema toj podeli zaduženja koja ostaju u aktivnosti bi bila sledeća:

  • Da sadrži navigaciju do drugih aktivnosti putem intent-a ili navigacijske komponente (“NavigationDrawer”,“ViewPager”…)
  • Da skriva i prikazuje fragmenate (pomoću menadžera fragmenata)
  • Da prima podatake iz drugih aktivnosti (intent)
  • Da kumunicira sa fragmentima i posreduje komunikaciji između njih

Dok fragmenti preuzimaju obavezu da:

  • Prikazuju odgovarajući sadržaj
  • Event handling
  • Pokretanje network request-a
  • Preuzimanje i čuvanje podataka

Životni ciklus fragmenta direktno je pod uticajem životnog ciklusa aktivnosti. Kada je aktivnost pauzirana, onda su i svi su fragmenti u njoj pauzirani, a kada je aktivnost uništena, onda su i svi njeni fragmenti uništeni. Međutim, dok je aktivnost “živa”, možemo manipulisati sa svakim fragmentom nezavisno. Detaljan prikaz lifecycle fragmenta i aktivnosti možete pogledati ovde.

fragment lifecycle

NAPOMENA:
Kada implementirate neku metodu životnog ciklusa fragmenta uvek treba da pozovete nadklasu (npr. super.onStart();):

Primer

Kreiranje fragmenta

Extendovanje fragment klase

Kreiranje fragmenta se sastoji iz kreiranja odgovarajuće klase zadužene za logiku i pridodajući joj odgovarajući layout. Klasa zadužena za logiku mora da extenduje neku od sledećih klasa:

  • Fragment je glavna klasa dok su ostale njegove podklase (pogledajte kako izgleda boilerplate kod koji generiše android studio).
  • DialogFragment – Fragment koji prikazuje prozor za dijalog, koji lebdi na vrhu prozora njegove aktivnosti. Ovo se obično koristi za prikazivanje dijaloga upozorenja, dijaloga za potvrdu ili za traženje informacija od korisnika u okviru bez potrebe za prebacivanjem na drugu aktivnost, dozvoljavajući korisniku da se vrati na prethodni fragment.
  • PreferenceFragmentCompat se koristi za kreiranje settings liste za našu aplikaciju odkle korisnici imaju mogućnost da promene funkcionalnost i ponašanje aplikacije. (pročitajte više u dokumentaciji).
  • ListFragment se koristi za prikazivanje liste nekih podataka i ima već implementiran event listener na clik nekog člana iz liste, tako da je potrebno samo da definišemo metod onListItemClick() (primer).

Povezivanje fragmenta sa njegovim layout-om

Da biste obezbedili layout za fragment, morate da implementirate “onCreateView()” metodu, koju Android poziva kada dođe vreme da fragment iscrta svoj layout. Implementacija ovog metoda mora da vrati prikaz koji je root layout vašeg fragmenta. Povezivanje fragment klase i njenog view-a se vrši korišćenjenm inflate() metode u sklopu lifecycle metode onCreateViewa:

Pogledajte više o postupku ubacivanja view-a iz xml-a u klasu “Layout inflation” u članku “Konvertovanje XML resursa u View objekat”.

Pored ovog načina možemo da kreiramo fragment na osnovu androidovog template-a pod nazivom Fragment(Blank) u čijem sklopu dolazi dosta pripremljeno boilerplate koda. Više o ovome pogledajte ovde.

Targetiranje elemenata u okviru layout-a

Da bi u okviru fragmenta mogli da koristimo metodu findViewById() potrebno je da prvo targetiramo njegov layout. To možemo uraditi na dva načina u zavisnosti gde nam u kodu treba:

  1. U okviru metoda onCreate() i onCreateView() prvo je potrebno da inflate-ujemo layout, nakon čega možemo da koristimo metodu findViewById():

    Fragment za razliku od aktivnosti nije podklasa klase Context, što znači da nema pristup globalnim informacijama o okruženju aplikacije. To znači da fragment ne može da koristi this za dobijanje context-a. Iz tog razloga u sklopu onCreateView() metode je kao parametar prosledjen objekat LayoutInflater koji ima pristup kontekstu, te stoga ako nam je potreban context možemo njega da iskoristimo inflater.getContext().

  2. U okviru metode onViewCreated() targetiramo fragmentov layout sa metodom getView(). Ova metoda je dostupna kada ekstendujemo našu klasu sa klasom Fragment i može da se pozove samo nakon kreiranja view-a, te je stoga ne možemo koristiti unutar onCreate() ili onCreateView() metode.

Embendovanje fragmenta u aktivnostima

Aktvnost koja sadrži fragment mora da ekstenduje ili FragmentActivity ili njenu potklasu AppCompatActivity. Fragmente u aktivnost možemo da dodamo na dva načina:

a) Statično ubacivanje fragmenta direktno u layout aktivnosti

Statično ubacivanje fragmenta u aktivnost podrazumeva da se fragment ubaci u layout aktivnosti kao view.

Fragment ubačen na ovaj način mora da ima definisan id u okviru XML-a, jer se preko njega targetira u okviru aktivnosti koristeći metodu findFragmentById():

Kada imamo referencu na fragment onda možemo da pozivamo njegove public metode ili properties-e.

b) Programirano ubacivanje fragmenta u aktivnost

Za programirano ubacivanje fragmenta u aktivnost je postupak sledeći:

1.) Kreiranje fragment kontejnera u XML-u

Ako vaša aktivnost dozvoljava da se fragmenti uklone i zamene, trebalo bi da dodate početni fragment (tzv. fragmentKontejner) u “onCreate()”. Taj kontejner se koristi da u njega možemo kasnije da ubacimo neki drugi fragment.

2.) Kreiranje fragmentManager-a

Fragment manager se instancira pozivajući metodu getSupportFragmentManager()

Fragment manager

Fragment manager je objekat koji zadužen za rad sa fragmentima za navigaciju izmedju njih kao i referenciranje fragmenta u okviru aktivnosti:

Metod Opis
addOnBackStackChangedListener Dodaje listener kada dodje do promene back stack-a (više o ovome pogledajte ovde)
beginTransaction() Kreira novu transakciju.
findFragmentById(int id) Nalazi fragment koji je inflated-ovan direktno u XML layout aktivnosti.
findFragmentByTag(String tag) Nalazi fragment preko tag-a
popBackStack() Uklanja fragment sa backstack-a.
executePendingTransactions() Forsira izvršenje transakcije.
3.) Kreiranje instance fragmenta

Kreiranje instance fragmenta je standardno korišćenjem new operatora:

Ukoliko želimo da prosledimo i neke podatke pri instanciranju onda oni moraju biti definisani u okviru konstruktorske metode:

Pa ih onda prosledjujemo kao parametar:

Preporuka je da se koristi tzv. newInstance pristup, kada se u okviru fragmenta definiše factory metoda koja služi za instaciranje fragmenta:

Kasnije u fragmentu okviru metode onCreate() možemo da zatražimo podatke generisane pri instanciranju fragmenta upravo preko naziva argumenta:

Drugi argument u get-eru je defaultna vrednost u slučaju da ne nadje argument.

4.) Izvršavanje neke od transakcija sa fragmentima (add, remove, replace)

Potoji API za rad sa fragmentima u aktivnosti (add, remove, or replace a fragment) i zove se FragmentTransaction. Instancu FragmentTransaction dobijamo koristeći fragmentManager.

Jedna od metoda koju može da izvrši FragmentTransaction je i add(). Ova metoda služi za ubacivanje našeg fragmenta u sklop nekog ViewGroup-a (kontejner). Taj ViewGroup se definiše kroz prvi parametar, dok se kroz drugi parametar definiše fragment koji dodajemo, a kroz treći parametar definišemo TAG koji će da obeleži dodati fragment (na osnovu taga kasnije možemo da ga targetiramo sa fragmentManager.findFragmentByTag(“TAGFRAGMENTA”);).

Da bi se klikom na “back” dugme vratili na prethodno stanje (tj. da ne bi smo zatvorili aktivnost što je podrazumevano) potrebno je da dadamo naš fragment na stack tzv. “backStack” sa metodom addToBackStack().

Po definisanju svih naredbi (može da ih bude više od jedne) je potrebno potvrditi akciju (commit-ovati), nakon čega će one biti i stvarno izvršene:

Akcije koje smo commit-ovali se ne izvršavaju odmah već ih stavljaju na čekanje za izvršavanje na glavnoj niti (main thread). Akcije će se izvršiti tek kada nit bude spremna. Pogledajte primere transakcija ovde.

Targetiranje fragmenta iz aktivnosti

Za targetiranje fragmenta u okviru aktivnost koji je ubačen na ovaj način se koristi metoda findFragmentByTag() kojoj se prosledjuje parametar TAG (definisan kroz treći parametar metode replace()).

Navigacija izmedju fragmenata

Navigacija izmedju fragmenata može biti definisana u sklopu aktivnosti koristeći samo FragmentManager, medjutim ništa nas ne sprečava da koristimo neki od sledećih pristupa:

  • TabLayout (tabovi, pogledajte više u članku)
  • Fragment Navigation Drawer (meni sa strane pogledajte više u članaku)
  • ViewPager (prebacivanje slajdovanjem izmedju fragmenata, pogledajte više u članku)

×

Kreiranje od pripremljnog boilerplate “Fragment(Blank)”

Factory statička metoda newInstance() se koristi pri instanciranju fragmenta u okviru aktivnosi i omogućava jednostavno prosledjivanje parametara pri instanciranju fragmenta:

Sada u okviru fragmenta možemo da koristimo prosledjene parametre koristeći promenjive mParam1 i mParam2 a instanciranje Fragmenta u aktivnosti se vrši sa sledećim kodom:

NAPOMENA:
Ako je fragment podklasa ListFragment-a, kod njega se po default-u vraća ListView u metodi onCreateView(), tako da ne mora da se implementira deo vezan za povezivanje logike sa layout-om osim ako ne koristimo custom layout.

U sklopu boilerplate koda dolazi deo vezan za CustomListener koji se koriste za prosledjivanje podataka iz fragmenta u aktvinost (drugi fragment).

Više o custom listener-ima pogledaj te u članku “Kreiranje custom listenera”.

×

Primer – ListFragment

×

Primer

U ovome primeru je prikazano definisanje početnog fragmenta u aktivnost. U prazan tag kontejnera se dodajemo novi view naš fragment.

Primer

U ovome primeru pozivajući metodu showFragmentA dolazi do zamene fragmenta

×

full fragment lifecycle

×

×

Back Stack

Stack je tip memorije u koji se smeštaju elementi jedan na drugi, tako da poslednji dodati element je na vrhu (analogija sa gomilom tanjira). Elementi se uklanjaju sa stacka obrnutim redosledom, tako što se poslednje dodati element uklanja prvi. U okviru Androida postoji stack u koji se smeštaju sve aktivnosti prema redosledu pozivanja. Pritiskanjem “back” dugmenta poslednja aktivnost sa stacka se briše. Medjutim u slučaju korišćenja fragmenta to nije defaultno ponašanje pa kada korisnik pritisne back sa stacka ne ukloni poslednje dodati fragment već cela aktivnost. To nije očekivano ponašanje te je potrebno je da tu funkcionalnost programer “ručno” doda.

backstack

addToBackStack

Da bi se i fragmenti ubacili na backStack potrebno je da se dodaju koristeći addToBackStack() metodu. Ovu metodu je potrebno dodati pre svakog commit-a. Tekst koji se prosledjuje je opcioni i koristi se ako kasnije želimo da prepoznamo tu transakciju, medjutim sasvim je ok da se prosledi i null kao parametar.

FragmentManager.OnBackStackChangedListener

Ako aplikacija sa promenom fragmenata treba da ažurira i druge elemente korisničkog interfejsa (npr. actionBar) to znači da bi trebalo se reaguje nakon promene backStack-a. U tom slučaju je potrebno da se iskoristi interfejs addOnBackStackChangedListener i da se definiše callback metoda onBackStackChanged() koja treba da izvrši akciju nakon okidanja dogadjaja

Primer kada aktivnost implementira interfejs

Primer kada se callback metoda onBackStackChanged() definie “on the fly”:

Podelite:

Ostavite komentar