Curs 6 Fragments Un fragment reprezintă o porțiune dintr-un Activity. Într-un activity se pot combina mai multe fragmente ți un fragment se poate reutiliza în activități multiple. Un fragment poate fi privit ca secțiunea modulară a unei activități care are propriul său ciclu de viață, primește propriile evenimente de intrare și care poate fi adăugată sau eliminată în timp ce activitatea se execută. Un fragment trebuie să fie întotdeauna încorporat într-o activitate și ciclul de viață al fragmentului este afectat în mod direct de ciclul de viață al activității gazdă. De exemplu, atunci când activitatea este întreruptă sunt întrerupte toate fragmentele sale, iar atunci când activitatea este distrusă sunt distruse toate fragmentele. Când o activitate rulează, fragmentele sale pot fi manipulate independent, putând fi adăugate sau să eliminate. Acest lucru este deosebit de important, deoarece un fragment modular permite diverse combinații de fragmente pentru diferite dimensiuni de ecran. (sursa: android.com) Un fragment poate fi adăugat unui Activity direct în fișierul xml (layout) cu tagul <fragment>, sau din cod prin adăugarea acestuia la un ViewGroup existent. Fragmentele se utilizează cel mai des pentru: Reutilizarea unor părți ale ecranului, inclusiv vizualizarea și logica evenimentelor, în moduri diferite, în activități diferite (ex.: utilizarea aceleiași liste în diferite surse de date dintr-o aplicație). 1 M. Apetrii
Adaptarea conținutului la dimensiunile ecranului (ex.: versiunea de tabletă a unei activități are un aspect mult diferit față de versiunea pentru telefon, diferit față de de versiunea pentru TV). Adaptarea conținutului la orientarea ecranului (ex.: versiunea portret a unei activități are un aspect mult diferit față de versiunea peisaj). Fragmentele permit ambelor orientări să reutilizeze elementele partajate. Un fragment, la fel cu o activitate, are un fișier XML (de aspect) și o clasă Java care trebuie să moștenească clasa Fragment. Pentru a putea încorpora un fragment, activitatea trebuie să moștenească clasa FragmentActivity sau AppCompatActivity, care adaugă suport pentru managerul de fragmente. Exemplu: ex_fragment.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/textview1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="textview" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button" /> </LinearLayout> FragmentulMeu.java public class FragmentulMeu extends Fragment { public View oncreateview(layoutinflater inflater, ViewGroup parent, Bundle savedinstancestate) { // Definim fisierul xml al fragmentului return inflater.inflate(r.layout.ex_fragment, parent, false); // se apeleaza imediat dupa oncreateview(). public void onviewcreated(view view, Bundle savedinstancestate) { //orice configurare pentru elemente de tip View trebuie sa apara aici // Parametrul parent trecut la oncreateview() este părintele ViewGroup (de la aspectul activității), în care va fi inserat fragmentul. Parametrul savedinstancestate este un Bundle care furnizează date referitoare la instanța anterioară a fragmentului, dacă fragmentul 2 M. Apetrii
este reluat. Metoda inflate() are trei argumente: ID-ul de resursei (layout-ului) Elementul de tip ViewGroup care va fi parintele layout-ului fragmentului. O variabilă booleană care indică dacă layout-ul fragmentului sa fie sau nu atașat la ViewGroup-ul parinte (al doilea parametru) Există două moduri de a adăuga un fragment unei activități: dinamic folosind Java și static folosind XML. Modul static direct in XML <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <fragment android:name="mape3.ex_fragments.fragmentulmeu" android:id="@+id/fragmentulmeu" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> Modul dinamic- în codul Java <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/id_fragment" android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout> </LinearLayout> FragmentTransaction ft = getsupportfragmentmanager().begintransaction(); ft.add(r.id.your_placeholder, new FragmentulMeu()); ft.commit(); În metoda add() primul argument este ViewGroup-ul în care trebuie plasat fragmentul specificat prin ID, iar al doilea parametru este fragmentul de adăugat. La final trebuie apelată metoda commit(), pentru ca modificările să aibă efect. Managementul fragmentelor într-un activity este un element de tip FragmentManager, care poate fi obținut prin metoda getfragmentmanager(). Printre altele, acesta dă fragmente care există în activitate, cu findfragmentbyid()sau findfragmentbytag(), elimina 3 M. Apetrii
fragmente din stiva cu popbackstack(), înregistrează un ascultător pentru modificări ale stivei, cu addonbackstackchangedlistener (). Ca răspuns la interacțiunea cu utilizatorul, un fragment poate fi adăugat (add()), eliminat (remove()), înlocuit (replace()) etc. O asemenea operație se numește o tranzacție și poate fi efectuată folosind FragmentTransaction. O asemenea instanță se poate obține din FragmentManager cu begintransaction(). Exemplu de înlocuire a unui fragment cu altul (cu păstrarea stării anterioare în stivă): // Cream un fragment si o tranzactie Fragment newfragment = new ExempluFragment(); FragmentTransaction transaction = getfragmentmanager().begintransaction(); // Inlocuim continutul vechi din fragment_container cu acest // fragment si adaugam tranzactia in stiva transaction.replace(r.id.fragment_container, newfragment); transaction.addtobackstack(null); // Se executa tranzactia (operatia) transaction.commit(); Prin apelarea addtobackstack(), tranzacția este salvată în stivă, astfel încât utilizatorul poate să aducă înapoi fragmentul anterior prin apăsarea butonului Back. Pentru a prelua fragmente din stiva, trebuie să suprascriem metoda onbackpressed () în activitatea principală: public void onbackpressed() { if (getfragmentmanager().getbackstackentrycount() > 0) { getfragmentmanager().popbackstack(); else { super.onbackpressed(); Dacă nu se suprascrie metoda onbackpressed() și există o activitate anterioară în stivă, apăsarea butonului Back va face ca aplicația să revină la această activitate. Dacă nu există o activitate anterioară în stivă, apăsarea butonului Back va face ca aplicația să se închidă. Un fragment conține metode similare cu o activitate, cum ar fi oncreate(), onstart(), onpause() și onstop(). Ciclul de viață al unui fragment este prezentat în figura de mai jos. 4 M. Apetrii
(sursa: android.com) Principalele metode care ar trebui suprascrie: oncreate() Sistemul invocă această metodă la crearea fragmentului. De obicei aici se inițializează componentele esențiale ale fragmentului. oncreateview() Sistemul invocă această metodă atunci când se desenează interfața cu utilizatorul a fragmentului. Această metodă trebuie să întoarcă un element de tipul View. Se poate returna null dacă fragmentul nu oferă o interfață de utilizare. Elementul de tip View returnat de oncreateview() se poate obține dintr-un fisier xml definit în folderul layout. onattach() Sistemul invocă această metodă la atașarea fragmentului la o activitate. onpause() Sistemul invocă această metodă atunci când utilizatorul da primul semn că utilizatorul părăsește fragmentul (aceasta nu înseamnă întotdeauna că fragmentul este distrus). Se folosește de obicei pentru a salvarea datelor în afara sesiunii utilizatorului curent (persistența datelor). 5 M. Apetrii
Comunicarea cu fragmentele În general fragmentele comunică numai cu activitatea părinte, permițând activității să gestioneze intrările și ieșirile de date din fiecare fragment. Excepție de la această regulă fac fragmentele de dialog dintr-un alt fragment sau fragmentele copii imbricate. Ambele cazuri sunt situații în care un fragment a imbricat fragmente copil și, prin urmare, li se permite să comunice în sus către părintele lor (care este un fragment). Există trei moduri în care un fragment și o activitate pot comunica: Bundle - Activitatea poate construi un fragment și poate crea argumente Metode - Activitatea poate apela metode ale unui fragment Listener - Fragmentul poate declanșa evenimente ascultător la o activitate prin intermediul unei interfețe Comunicarea cu un fragment este, în general, astfel: activitățile pot crea fragmente cu date în timpul execuției activitățile pot transmite date către fragmente existente utilizând metodele ale acestuia fragmentele pot comunica cu activitatea părinte folosind o interfață și ascultători fragmentele care trebuie să transmită date altor fragmente fac acest lucru prin intermediul activității părinte fragmentele pot transmite date către și din fragmente de dialog, fragmente copil imbricate Comunicarea prin intermediul argumentelor: Deoarece clasa Fragment are doar un constructor fără argumente, se va declara o metodă statică newinstance care va crea un Fragment cu argumente, utilizând metoda setarguments. Exemplu: public class DemoFragment extends Fragment { public static DemoFragment newinstance(int argint, String argstr) { DemoFragment fragmentdemo = new DemoFragment(); Bundle args = new Bundle(); args.putint("numar", argint); args.putstring("titlu", argstr); fragmentdemo.setarguments(args); return fragmentdemo; Argumentele vor putea fi accesate în metoda oncreate. public class DemoFragment extends Fragment { public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); // preluam argumentele int nr = getarguments().getint("numar", 0); String titlu = getarguments().getstring("titlu", ""); În activitatea parinte, fragmentul se poate încărca dinamic astfel: 6 M. Apetrii
FragmentTransaction ft = getsupportfragmentmanager().begintransaction(); DemoFragment fragmentdemo = DemoFragment.newInstance(10, "Un titlu"); ft.replace(r.id.fragmentulmeu, fragmentdemo); ft.commit(); Comunicarea prin intermediul metodelor: Dacă activitatea părinte dorește să transmită date către un fragment, poate face lucrul acesta invocând o metodă a acestuia prin intermediul managerului de fragmente. Exemplu: public class DemoFragment extends Fragment { public void metoda_de_executat(string arg) { public class MainActivity extends AppCompatActivity { protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); DemoFragment fragmentdemo = (DemoFragment) getsupportfragmentmanager().findfragmentbyid(r.id.fragmentulmeu); fragmentdemo.metoda_de_executat("lista parametri"); Comunicarea prin intermediul ascultătorilor: Dacă fragmentul trebuie să comunice cu activitatea atunci acesta poate defini o interfață care va fi implementată de către activitate. Exemplu: (android.com) public class MyListFragment extends Fragment { // Definim ascultatorul interfetei private OnItemSelectedListener listener; // Definim evenimentul folosit de fragment pentru comunicare public interface OnItemSelectedListener { // pot fi oricate public void onrssitemselected(string link); // Pastram ascultatorul (activitatii) public void onattach(context context) { super.onattach(context); if (context instanceof OnItemSelectedListener) { listener = (OnItemSelectedListener) context; else { throw new ClassCastException(context.toString() + " clasa trebuie sa implementeze MyListFragment.OnItemSelectedListener"); 7 M. Apetrii
// Putem apela metoda asociata evenimentului, pentru transmiterea valorilor catre activitatea parinte public void onsomeclick(view v) { listener.onrssitemselected("some link"); // Activitatea va implementa interfata ascultator a fragmentului public class RssfeedActivity extends AppCompatActivity implements MyListFragment.OnItemSelectedListener { // Un fragment caruia dorim sa-i trimitem datele DetailFragment fragment; protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_rssfeed); // identificam fragmentul dupa id fragment = (DetailFragment) getsupportfragmentmanager().findfragmentbyid(r.id.detailfragment); // Definim metoda ce se va apela cand fragmentul solicita // Este implementarea pentru metoda interfetei `OnItemSelectedListener` public void onrssitemselected(string link) { if (fragment!= null && fragment.isinlayout()) { fragment.settext(link); FragmentManager este responsabil pentru gestionarea în timp a fragmentelor, inclusiv adăugarea, eliminarea, ascunderea, afișarea sau navigarea în alt mod între fragmente. Acesta este de asemenea responsabil pentru găsirea fragmentelor în cadrul unei activități. Metodele importante disponibile sunt: Metodă Descriere addonbackstackchangedlistener begintransaction() findfragmentbyid(int id) Adăugați un nou ascultător pentru modificarea stivei de fragmente. Creează o nouă tranzacție pentru a schimba fragmente în timpul rulării. Găsește un fragment după id, de obicei dat în structura XML. 8 M. Apetrii
Metodă Descriere findfragmentbytag(string tag) popbackstack() executependingtransactions() Găsește un fragment după etichetă, de obicei, pentru un fragment adăugat în execuție. Elimină un fragment din stivă. Forțează aplicarea tranzacției angajate. ActionBar Menu Items Deseori, in programele noastre avem nevoie de meniuri specifice fragmentelor, care apar numai pentru fragmentul respectiv. Acest lucru se poate face prin adăugarea directă a metodei oncreateoptionsmenu la fragment. Aceasta funcționează la fel ca cea pentru activitate: public void oncreateoptionsmenu(menu menu, MenuInflater inflater) { inflater.inflate(r.menu.fragment_menu, menu); Trebuie apoi notificat fragmentul că elementele meniului trebuie încărcate odata cu fragmentul: public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); sethasoptionsmenu(true); Un click pe un element din meniu poate fi captat prin proprietatea onclick sau prin intermediul metodei onoptionsitemselected din fragment: public boolean onoptionsitemselected(menuitem item) { // handle item selection switch (item.getitemid()) { case R.id.edit_item: // do s.th. return true; default: return super.onoptionsitemselected(item); (sursa: android.com) 9 M. Apetrii
Subclase ale clasei Fragment care pot fi moștenite: DialogFragment Folosită pentru crearea casetelor de dialog ListFragment Afișează o listă de elemente care sunt gestionate de un adapter, similar cu ListActivity. Acesta oferă mai multe metode, una din cele mai des folosite fiind onlistitemclick() (se apelează atunci când se selectează un element din listă). 10 M. Apetrii