Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
laboratoare:laborator04 [2015/03/15 21:45]
Andrei Roșu-Cojocaru [Gestiunea evenimentelor cu difuzare prin intermediul intențiilor]
laboratoare:laborator04 [2016/03/19 12:36] (current)
Andrei Roșu-Cojocaru
Line 75: Line 75:
 <code xml> <code xml>
 <​activity <​activity
-  android:​name="​ro.pub.cs.systems.pdsd.lab04.MainActivity"​+  android:​name="​ro.pub.cs.systems.eim.lab04.MainActivity"​
   android:​label="​@string/​app_name"​ >   android:​label="​@string/​app_name"​ >
   <​intent-filter>​   <​intent-filter>​
-    <action android:​name="​ro.pub.cs.systems.pdsd.lab04.intent.action.MainActivity"​ />+    <action android:​name="​ro.pub.cs.systems.eim.lab04.intent.action.MainActivity"​ />
     <​category android:​name="​android.intent.category.LAUNCHER"​ />     <​category android:​name="​android.intent.category.LAUNCHER"​ />
   </​intent-filter>​   </​intent-filter>​
Line 99: Line 99:
 </​code>​ </​code>​
   * dacă se dorește rularea unei activități existente în cadrul altei aplicații, aceasta va trebui referită prin numele său complet, inclusiv denumirea pachetului care o identifică ​ <code java>   * dacă se dorește rularea unei activități existente în cadrul altei aplicații, aceasta va trebui referită prin numele său complet, inclusiv denumirea pachetului care o identifică ​ <code java>
-startActivity(new Intent("​ro.pub.cs.systems.pdsd.lab04.AnotherActivity"​));​+startActivity(new Intent("​ro.pub.cs.systems.eim.lab04.AnotherActivity"​));​
 </​code>​ </​code>​
  
Line 117: Line 117:
   * vizualizarea conținutului specificat în secțiunea ''​data''​ asociată intenției, sub forma unui URI, de către aplicații Android diferite, în funcție de schema (protocolul) utilizat (''​http''​ - navigator, ''​tel''​ - aplicația pentru formarea unui număr de telefon, ''​geo''​ - Google Maps, ''​content''​ - aplicația pentru gestiunea contactelor din agenda telefonică):​ <code java>   * vizualizarea conținutului specificat în secțiunea ''​data''​ asociată intenției, sub forma unui URI, de către aplicații Android diferite, în funcție de schema (protocolul) utilizat (''​http''​ - navigator, ''​tel''​ - aplicația pentru formarea unui număr de telefon, ''​geo''​ - Google Maps, ''​content''​ - aplicația pentru gestiunea contactelor din agenda telefonică):​ <code java>
 Intent intent = new Intent(Intent.ACTION_VIEW);​ Intent intent = new Intent(Intent.ACTION_VIEW);​
-intent.setData(Uri.parse("​http://​ocw.cs.pub.ro/​pdsd"));+intent.setData(Uri.parse("​http://​ocw.cs.pub.ro/​eim"));
 </​code>​ </​code>​
   * căutarea unor informații pe Internet folosind un motor de căutare, termenul căutat fiind indicat în secțiunea ''​extra''​ asociată intenției, fiind identificată prin cheia ''​SearchManager.QUERY'':​ <code java>   * căutarea unor informații pe Internet folosind un motor de căutare, termenul căutat fiind indicat în secțiunea ''​extra''​ asociată intenției, fiind identificată prin cheia ''​SearchManager.QUERY'':​ <code java>
Line 252: Line 252:
 <code xml> <code xml>
 <​activity <​activity
-  android:​name="​ro.pub.cs.systems.pdsd.lab04.AnotherActivity"​+  android:​name="​ro.pub.cs.systems.eim.lab04.AnotherActivity"​
   android:​label="​@string/​app_name"​ >   android:​label="​@string/​app_name"​ >
   <​intent-filter>​   <​intent-filter>​
-    <action android:​name="​ro.pub.cs.systems.pdsd.lab04.intent.action.AnotherActivity"​ />+    <action android:​name="​ro.pub.cs.systems.eim.lab04.intent.action.AnotherActivity"​ />
     <​category android:​name="​android.intent.category.DEFAULT"​ />     <​category android:​name="​android.intent.category.DEFAULT"​ />
-    <data android:​scheme="​myprotocol"​ android:​host="​mynamespace"​ />+    <data 
 +      ​android:​scheme="​myprotocol"​ 
 +      ​android:​host="​mynamespace"​ />
   </​intent-filter>​   </​intent-filter>​
 </​activity>​ </​activity>​
 </​code>​ </​code>​
 +
 +De remarcat este faptul că în structura URI-ului, partea de după ''​schemă:​%%//​%%protocol/''​ poate conține orice șir de caractere, rolul său fiind strict acela de a respecta forma unui astfel de obiect (estetic), fără a influența în vreo formă funcționalitatea acestuia. ​
  
 ==== Transmiterea de informații între componente prin intermediul intențiilor ==== ==== Transmiterea de informații între componente prin intermediul intențiilor ====
Line 288: Line 292:
   super.onCreate(state);​   super.onCreate(state);​
   setContentView(R.layout.activity_main);​   setContentView(R.layout.activity_main);​
-  Intent intent = new Intent("​ro.pub.cs.systems.pdsd.lab04.AnotherActivity"​);​ +  Intent intent = new Intent("​ro.pub.cs.systems.eim.lab04.AnotherActivity"​);​ 
-  intent.putExtra("​ro.pub.cs.systems.pdsd.lab04.someKey",​ someValue);+  intent.putExtra("​ro.pub.cs.systems.pdsd.eim.someKey",​ someValue);
   startActivityForResult(intent,​ ANOTHER_ACTIVITY_REQUEST_CODE);​   startActivityForResult(intent,​ ANOTHER_ACTIVITY_REQUEST_CODE);​
   // start another activities with their own request codes   // start another activities with their own request codes
Line 323: Line 327:
   // intent to parent   // intent to parent
   Intent intentToParent = new Intent();   Intent intentToParent = new Intent();
-  intent.putExtra("​ro.pub.cs.systems.pdsd.lab04.anotherKey",​ anotherValue);​+  intent.putExtra("​ro.pub.cs.systems.eim.lab04.anotherKey",​ anotherValue);​
   setResult(RESULT_OK,​ intentToParent);​   setResult(RESULT_OK,​ intentToParent);​
   finish();   finish();
Line 329: Line 333:
 </​code>​ </​code>​
  
-TODO +În cazul folosirii unor intenții în care activitățile sunt invocate ​prin intermediul ​unor URI-uridatele vor putea fi concatenate direct în cadrul acestuia (fără a utiliza un obiect ​de tip ''​Bundle''​), restricția constând în faptul că pot fi utilizate ​numai șiruri de caractere:
- +
-Datele vor putea fi transmise și prin intermediul URI-uluinu doar prin obiectul ​de tip ''​Bundle'',​ restricția constând în faptul că pot fi transmise ​numai șiruri de caractere:+
   * în activitatea părinte <code java>   * în activitatea părinte <code java>
-Uri uri = Uri.parse("​myprotocol://​mynamespace/​myactivity?​someKey=someValue"​);​+Uri uri = Uri.parse("​myprotocol://​mynamespace/​myactivity?​someKey=someValue&...");
 Intent intent = new Intent(Intent.ACTION_VIEW,​ uri); Intent intent = new Intent(Intent.ACTION_VIEW,​ uri);
 startActivity(intent);​ startActivity(intent);​
Line 373: Line 375:
  
 <code java> <code java>
-final public static String SOME_ACTION = "​ro.pub.cs.systems.pdsd.lab04.SomeAction.SOME_ACTION";​+final public static String SOME_ACTION = "​ro.pub.cs.systems.eim.lab04.SomeAction.SOME_ACTION";​
  
 Intent intent = new Intent(SOME_ACTION);​ Intent intent = new Intent(SOME_ACTION);​
-intent.putExtra("​ro.pub.cs.systems.pdsd.lab04.someKey",​ someValue);+intent.putExtra("​ro.pub.cs.systems.eim.lab04.someKey",​ someValue);
 sendBroadcast(intent);​ sendBroadcast(intent);​
 </​code>​ </​code>​
Line 391: Line 393:
       android:​name="​.SomeEventBroadcastReceiver">​       android:​name="​.SomeEventBroadcastReceiver">​
       <​intent-filter>​       <​intent-filter>​
-        <action android:​name="​ro.pub.cs.systems.pdsd.lab04.SomeAction.SOME_ACTION"​ />+        <action android:​name="​ro.pub.cs.systems.eim.lab04.SomeAction.SOME_ACTION"​ />
       </​intent-filter> ​       </​intent-filter> ​
     </​receiver>​     </​receiver>​
Line 448: Line 450:
  
 <code java> <code java>
-final public static String SOME_ORDERED_ACTION = "​ro.pub.cs.systems.pdsd.lab04.SomeOrderedAction.SOME_ORDERED_ACTION";​+final public static String SOME_ORDERED_ACTION = "​ro.pub.cs.systems.eim.lab04.SomeOrderedAction.SOME_ORDERED_ACTION";​
  
 Intent intent = new Intent(SOME_ORDERED_ACTION);​ Intent intent = new Intent(SOME_ORDERED_ACTION);​
-intent.putExtra("​ro.pub.cs.systems.pdsd.lab04.someKey",​ someValue);​ +intent.putExtra("​ro.pub.cs.systems.pdsd.eim.someKey",​ someValue);​ 
-sendOrderedBroadcast(intent,​ "​ro.pub.cs.systems.pdsd.lab04.SOME_PERMISSION"​);​+sendOrderedBroadcast(intent,​ "​ro.pub.cs.systems.pdsd.eim.SOME_PERMISSION"​);​
 </​code>​ </​code>​
  
Line 460: Line 462:
     <​receiver     <​receiver
       android:​name="​.SomeEventOrderedBroadcastReceiver"​       android:​name="​.SomeEventOrderedBroadcastReceiver"​
-      android:​permission="​ro.pub.cs.systems.pdsd.lab04.SOME_PERMISSION">​+      android:​permission="​ro.pub.cs.systems.eim.lab04.SOME_PERMISSION">​
       <​intent-filter       <​intent-filter
         android:​permission="​100">​         android:​permission="​100">​
-        <action android:​name="​ro.pub.cs.systems.pdsd.lab04.SomeOrderedAction.SOME_ORDERED_ACTION"​ />+        <action android:​name="​ro.pub.cs.systems.eim.lab04.SomeOrderedAction.SOME_ORDERED_ACTION"​ />
       </​intent-filter> ​       </​intent-filter> ​
     </​receiver>​     </​receiver>​
Line 477: Line 479:
  
 <code java> <code java>
-final public static String SOME_STICKY_ACTION = "​ro.pub.cs.systems.pdsd.lab04.SomeStickyAction.SOME_STICKY_ACTION";​+final public static String SOME_STICKY_ACTION = "​ro.pub.cs.systems.eim.lab04.SomeStickyAction.SOME_STICKY_ACTION";​
  
 Intent intent = new Intent(SOME_STICKY_ACTION);​ Intent intent = new Intent(SOME_STICKY_ACTION);​
-intent.putExtra("​ro.pub.cs.systems.pdsd.lab04.someKey",​ someValue);+intent.putExtra("​ro.pub.cs.systems.eim.lab04.someKey",​ someValue);
 sendStickyBroadcast(intent);​ sendStickyBroadcast(intent);​
 </​code>​ </​code>​
Line 530: Line 532:
 ===== Fragmente ===== ===== Fragmente =====
  
-Un fragment este o componentă Android ​având ​structură asemnănătoare cu a unei activitățiadică cuprinzând mai multe elemente graficeDe asemenea, o activitate poate fi formată din unul sau mai multe fragmente, în funcție de cerințele aplicației la un moment dat de timp.+Un fragment este o componentă Android ​atomică, încapsulând ​interfață grafică ce poate fi reutilizată, definind un ciclu de viață propriuactivitate poate fi formată din unul sau mai multe fragmente, în funcție de cerințele aplicației la un moment dat de timp.
  
-<​note>​Fragmentele sunt mai ales utilizate pentru a gestiona în mod eficient suprafața de afișare și pentru a se evita dezvoltarea mai multor interfețe grafice pentru diferite tipuri de rezoluții. Acestea vor cuprinde un număr de fragmente corespunzător posibilităților dispozitivului respectiv, distribuite pe una sau mai multe ferestre (activități).</​note>​+<​note>​Fragmentele sunt mai ales utilizate pentru a gestiona în mod eficient suprafața de afișare și pentru a se evita dezvoltarea mai multor interfețe grafice pentru diferite tipuri de rezoluții. Acestea vor cuprinde un număr de fragmente corespunzător posibilităților dispozitivului respectiv, distribuite pe una sau mai multe ferestre (activități). Se permite astfel dezvoltarea unor interfețe grafice flexibile în mod dinamic, asigurându-se adaptabilitatea la suprafațe de afișare diverse (ceasuri și telefoane inteligente,​ tablete, televizoare).</​note>​
  
-Fragmentele ​reprezintă ​unitatea atomică a interfeței grafice, putând fi atașate sau detașate de la o activitate în mod dinamic.+Deși reprezintă ​un modul independent,​ un fragment este string legat de activitatea din care face parte. Un fragment poate fi (re)utilizat în cadrul mai multor activități, putând fi plasat în numeroase moduri în funcție de capabilitățile ecranului dispozitivului mobil (tip, dimensiune, densitate, rezoluție). Operațiile de atașare și detașare a unui fragment la o activitate se realizează ​de regulă programatic,​ în cadrul codului sursă. 
 + 
 +<note tip>​Utilizarea de fragmente nu este obligatorie,​ însă reprezintă o recomandare pentru dezvoltarea aplicațiilor Android, întrucât ușurează foarte mult posibilitatea de a adapta experiența utilizatorului ​la noi configurații ale suprafeței de afișare.</​note>​ 
 + 
 +Fragmentele au fost introduse în Android începând cu nivelul de API 11 (Honeycomb),​ odată cu apariția tabletelor dotate cu un astfel de sistem de operare. Ulterior, au fost incluse și în bibliotecile de suport, astfel încât această funcționalitate să fie disponibilă pentru toate dispozitivele mobile echipate cu un sistem de operare având minim versiunea 1.6. 
 + 
 +==== Componentele unui Fragment ==== 
 + 
 +Un fragment este o clasă derivată din ''​android.app.Fragment'',​ care: 
 +  * definește o interfața grafică (opțional);​ 
 + 
 +<​note>​Există și fragmente care nu conțin o interfață grafică, ci furnizează un anumit comportament pentru ​o activitate</​note>​ 
 + 
 +  * implementează o anumită funcționalitate. 
 + 
 +Pentru a construi interfața grafică corespunzătoare unui fragment, în codul sursă se va suprascrie metoda ''​onCreateView()''​ ce utilizează:​ 
 +  * un obiect de tip ''​LayoutInflator''​ pentru a încărca fișierul XML; 
 +  * un obiect de tip ''​ViewGroup''​ ce referă containerul din care face parte fragmentul (de obicei, acesta aparține activității părinte);​ 
 +  * un obiect de tip ''​Bundle''​ utilizat pentru restaurarea stării în situația fragmentul este reluat (după ce anterior a fost întrerupt temporar). 
 + 
 +<file java SomeFragment.java>​ 
 +package ro.pub.cs.systems.eim.lab04;​ 
 + 
 +import android.app.Fragment;​ 
 +import android.os.Bundle;​ 
 +import android.view.LayoutInflater;​ 
 +import android.view.View;​ 
 +import android.view.ViewGroup;​ 
 + 
 +public class SomeFragment extends Fragment { 
 + 
 +  @Override 
 +  public View onCreateView(LayoutInflater layoutInflater,​ ViewGroup container, Bundle state) { 
 +    return layoutInflater.inflate(R.layout.some_fragment,​ container, false); 
 +  } 
 + 
 +
 +</​file>​ 
 + 
 +Se observă că metoda ''​inflate()''​ din clasa ''​LayoutInflater''​ primește următorii parametrii:​ 
 +  * un întreg, reprezentând identificatorul către o resursă de tip mecanism de control al conținutului;​ 
 +  * un obiect de tip ''​android.view.ViewGroup'';​ 
 +  * o valoare de tip adevărat/​fals,​ care indică dacă parametrul anterior este folosit ca părinte pentru interfața grafică încărcată sau dacă acesta este utilizat numai pentru a-i prelua parametrii de tip ''​LayoutParams'',​ spre a afișa ​în mod corect componentele conținute.
  
 Pentru fiecare fragment trebuie să existe în directorul ''/​res/​layout''​ un fișier XML corespunzător,​ descriind interfața grafică similar cu modul în care se realiza acest lucru în cadrul unei activități (exceptând atributul ''​tools:​context''​ din cadrul elementului rădăcină de tip layout): Pentru fiecare fragment trebuie să existe în directorul ''/​res/​layout''​ un fișier XML corespunzător,​ descriind interfața grafică similar cu modul în care se realiza acest lucru în cadrul unei activități (exceptând atributul ''​tools:​context''​ din cadrul elementului rădăcină de tip layout):
  
-<file xml fragment1.xml>+<file xml some_fragment.xml>
 <?xml version="​1.0"​ encoding="​UTF-8"?>​ <?xml version="​1.0"​ encoding="​UTF-8"?>​
 <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​ <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​
Line 544: Line 588:
   android:​layout_width="​match_parent"​   android:​layout_width="​match_parent"​
   android:​layout_height="​match_parent"​   android:​layout_height="​match_parent"​
-  android:​gravity="​center_horizontal"​ 
   android:​orientation="​vertical"​ >   android:​orientation="​vertical"​ >
   <​ImageView   <​ImageView
-    android:​id="​@+id/​imageView1"+    android:​id="​@+id/​some_image_view"
     android:​layout_width="​wrap_content"​     android:​layout_width="​wrap_content"​
     android:​layout_height="​wrap_content"​     android:​layout_height="​wrap_content"​
     android:​src="​@drawable/​some_image"​ />     android:​src="​@drawable/​some_image"​ />
   <​TextView   <​TextView
-    android:​id="​@+id/​textView1"+    android:​id="​@+id/​some_text_view"
     android:​layout_width="​wrap_content"​     android:​layout_width="​wrap_content"​
     android:​layout_height="​wrap_content"​     android:​layout_height="​wrap_content"​
Line 559: Line 602:
 </​file>​ </​file>​
  
-Referințla acest fragment trebuie să se realizeze ​în fișierul XML corespunzător activității care va conține:+De asemenea, există posibilitatea de defini o interfață grafică programatic, ​în codul sursă, folosind un obiect de tipul ''​android.view.ViewGroup'',​ însă o astfel de abordare nu este recomandată,​ nefiind scalabilă.
  
-<code xml>+În fișierul ''​AndroidManifest.xml''​ NU este necesar să existe nici o referință la fragmente, întrucât existența acestora este limitată la activitatea în care sunt conținute. 
 + 
 +==== Ciclul de Viață al unui Fragment ==== 
 + 
 +Ca și în cazul unei activități,​ ciclul de viață al unui fragment poate fi controlat prin intermediul unor metode care sunt apelate în mod automat. În cazul în care sunt suprascrise,​ acestea trebuie să apeleze în mod necesar metodele din clasa părinte. Pe lângă metodele apelate în momentul în care fragmentul este creat, pornit, reluat, întrerupt, oprit și distrus, există și metode specifice care semnalizează atașarea și detașarea sa în cadrul unei activități,​ crearea și distrugerea ierarhiei de controale grafice precum și crearea activității părinte. 
 + 
 +{{ :​laboratoare:​laborator04:​ciclul_de_viata_al_unui_fragment.png?​nolink&​900 }} 
 + 
 +  * la creare sunt apelate metodele: ''​onAttach()'',​ ''​onCreate()'',​ ''​onCreateView()''​ și ''​onActivityCreated()''​ 
 +  * în momentul în care devine vizibil, interacționând cu utilizatorul,​ sunt apelate ''​onStart()''​ și ''​onResume()''​ 
 +  * în momentul în care nu mai este vizibil, nemaiinteracționând cu utilizatorul,​ sunt apelate ''​onPause()''​ și ''​onStop()''​ 
 +  * la distrugere sunt apelate metodele ''​onDestroyView()'',​ ''​onDestroy()''​ și ''​onDetach()''​ 
 + 
 +Unele dintre aceste metode sunt aceleași ca în cazul activităților,​ având aceeași funcționalitate,​ în timp ce altele sunt specifice:​ 
 +  * ''​onAttach(Activity)''​ - apelată în momentul în care fragmentul este atașat la activitate; de regulă, în această metodă se reține referința către activitate;​ 
 +  * ''​onCreateView(LayoutInflator,​ ViewGroup, Bundle)''​ - apelată pentru a crea interfața grafică a fragmentului,​ fie prin încărcarea din fișierul XML corespunzător (folosind metoda ''​inflate()''​ din clasa ''​LayoutInflater''​),​ fie prin construirea programatică;​ interfața grafică (obiect de tip ''​android.view.View''​) trebuie întoarsă ca rezultat al acestei metode; pentru fragmentele care nu dețin o interfață grafică se întoarce ''​null'';​ 
 +  * ''​onActivityCreated(Bundle)''​ - apelată în momentul în care s-a terminat metoda ''​onCreate()''​ a activității care conține fragmentul; în cadrul acestei metode este finalizat procesul de inițializare al fragmentului,​ fiind executate operații pentru care este necesar ca activitatea să fie creată; 
 +  * ''​onDestroyView()''​ - apelată pentru a elibera resursele aferente controalelor din cadrul fragmentului,​ atunci când interfața grafică este descărcată;​ 
 +  * ''​onDetach()''​ - apelată în momentul în care fragmentul este detașat de la activitate 
 + 
 +<​note>​Se observă faptul că restaurarea stării se poate face în oricare din metodele ''​onCreate()'',​ ''​onCreateView()'',​ respectiv ''​onActivityCreated()''​.</​note>​ 
 + 
 +Se observă că ciclul de viață al unui fragment se desfășoară între momentele în care acesta este atașat la activitatea din care face parte, respectiv este detașat din cadrul acesteia, metodele corespunzătoare fiind ''​onAttach()''​ și ''​onDetach()''​. 
 + 
 +Metoda ''​onAttach()''​ este apelată înainte de a se crea interfața grafică a fragmentului,​ fragmentul însuși și chiar activitatea din care acesta face parte. În cadrul său, de regulă se obține o referință către activitate pentru a se realiza alte operații de inițializare. 
 + 
 +Se consideră că un fragment există între metodele ''​onCreate()''​ și ''​onDestroy()''​. 
 + 
 +În cadrul metodei ''​onCreate()''​ se recomandă să fie instanțiate orice obiecte care vor fi utilizate în cadrul fragmentului. Tot aici sunt realizate și alte operații de inițializare. 
 + 
 +<note important>​Spre diferență de activități,​ interfața grafică a unui fragment NU este încărcată în cadrul metodei ''​onCreate()''​.</​note>​ 
 + 
 +În cazul unui fragment, interfața grafică este construită,​ respectiv distrusă în cadrul metodelor ''​onCreateView()''​ și ''​onDestroyView()''​. 
 + 
 +În cadrul metodei ''​onCreateView()''​ se recomandă să se încarce interfața grafică din fișierul XML asociat (respectiv să se creeze programatic),​ obținându-se referințe către controalele grafice respective și asociindu-li-se obiecte de tip ascultător pentru diferite tipuri de evenimente din cadrul interacțiunii cu utilizatorul. Tot acum pot fi pornite diferite servicii sau cronometre. 
 + 
 +<note important>​Este obligatoriu ca metoda ''​onCreateView()''​ să întoarcă un obiect de tip ''​android.view.View''​ reprezentând interfața grafică a fragmentului respectiv.</​note>​ 
 + 
 +În cazul în care este necesar ca fragmentul să acceseze obiecte din interfața grafică a activității din care face parte, astfel de operații nu pot fi realizate decât după ce a fost apelată metoda ''​onActivityCreated()'',​ semnificând faptul că procesul de inițializare al acestuia a fost terminat. În caz contrar, este destul de probabil să se genereze o excepție de tipul ''​NullPointerException''​. 
 + 
 +Stările unui fragment sunt strâns legate de cele ale activității din care face parte. În plus, un fragment poate fi atașat sau detașat în mod dinamic unei activități,​ astfel încât acesta poate parcurge stările de creat, vizibil și activ de mai multe ori în timpul ciclului de viață al containerului din care face parte. 
 + 
 +Gestiunea tranzițiilor între diferitele stări din cadrul ciclului de viață al unui fragment este foarte importantă pentru a se asigura o experiență adecvată a utilizatorilor. Diferitele treceri între stările de inactivitate și activitate ale fragmentului trebuie să se realizeze în mod transparent pentru utilizatorii aplicației Android. De aceea, este foarte important ca să se asigure persistența controalelor grafice și a celorlalte date, salvându-se starea atunci când fragmentul este întrerupt temporar și oprit, restaurarea fiind realizată atunci când acesta este (re)pornit și reluat. 
 + 
 +<note tip>Este posibil ca după întreruperea temporară a activității / fragmentului să nu mai fie apelată nici o metodă, dacă necesarul de memorie impune ca acestea să fie distruse.</​note>​ 
 + 
 +<file java SomeFragment.java>​ 
 +package ro.pub.cs.systems.eim.lab04;​ 
 + 
 +import android.app.Activity;​ 
 +import android.app.Fragment;​ 
 +import android.os.Bundle;​ 
 +import android.view.LayoutInflater;​ 
 +import android.view.View;​ 
 +import android.view.ViewGroup;​ 
 + 
 +public class SomeFragment extends Fragment { 
 + 
 +  @Override 
 +  public void onAttach(Activity activity) { 
 +    super.onAttach(activity);​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onCreate(Bundle savedInstanceState) { 
 +    super.onCreate(savedInstanceState);​ 
 +    // ... 
 +  } 
 +   
 +  @Override 
 +  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
 +    return inflater.inflate(R.layout.some_fragment,​ container, false); 
 +  } 
 + 
 +  @Override 
 +  public void onActivityCreated(Bundle savedInstanceState) { 
 +    super.onActivityCreated(savedInstanceState);​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onStart() { 
 +    super.onStart();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onResume() { 
 +    super.onResume();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onPause(){ 
 +    super.onPause();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onSaveInstanceState(Bundle savedInstanceState) { 
 +    super.onSaveInstanceState(savedInstanceState);​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onStop() { 
 +    super.onStop();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onDestroyView() { 
 +    super.onDestroyView();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onDestroy() { 
 +    super.onDestroy();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onDetach() { 
 +    super.onDetach();​ 
 +    // ... 
 +  } 
 +
 +</​file>​ 
 + 
 +==== Gestiunea Fragmentelor din cadrul unei Activități ==== 
 + 
 +Fiecare activitate dispune de un proces de gestiune a fragmentelor conține, acesta fiind o instanță a clasei ''​FragmentManager'',​ putând fi obținut din cadrul contextului asociat și punând la dispoziția utilizatorilor metode pentru adăugarea și scoaterea, respectiv înlocuirea unui fragment din cadrul său. 
 + 
 +<code java> 
 +FragmentManager fragmentManager = getFragmentManager();​ 
 +</​code>​ 
 + 
 +=== Adăugarea unui Fragment === 
 + 
 +Operația de adăugare a unui fragment poate fi realizată:​ 
 +  - static, în cadrul fișierului XML, prin intermediul elementului ''<​fragment>'',​ în situația în care sunt definite interfețe grafice pentru fiecare dintre tipurile de suprafețe de afișare suportate; ​<code xml>
 <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​ <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​
   xmlns:​tools="​http://​schemas.android.com/​tools"​   xmlns:​tools="​http://​schemas.android.com/​tools"​
Line 573: Line 758:
   tools:​context="​.MainActivity"​ >   tools:​context="​.MainActivity"​ >
   <​fragment   <​fragment
-    android:​name="​ro.pub.cs.pdsd.laborator03.Fragment1"​+    android:​name="​ro.pub.cs.systems.eim.labo04.Fragment1"​
     android:​id="​@+id/​fragment1"​     android:​id="​@+id/​fragment1"​
     android:​layout_weight="​1"​     android:​layout_weight="​1"​
     android:​layout_width="​match_parent"​     android:​layout_width="​match_parent"​
-    android:​layout_height="​match_parent" /> +    android:​layout_height="​wrap_content" /> 
-    ​ +  <fragment 
-  <!-- add other fragments --> +    ​android:​name="​ro.pub.cs.systems.eim.labo04.Fragment2"​ 
-    ​+    android:​id="​@+id/​fragment2"​ 
 +    android:​layout_weight="​1"​ 
 +    android:​layout_width="​match_parent"​ 
 +    android:​layout_height="​wrap_content"​ />
 </​LinearLayout>​ </​LinearLayout>​
 </​code>​ </​code>​
 +  - dinamic (programatic),​ în cadrul codului sursă, prin definirea unor containere (obiecte de tip ''​FrameLayout'',​ care pot conține un sigur element vizibil) în cadrul interfețele grafice în care pot fi plasate fragmentele la diferite momente de timp. <code xml>
 +<​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​
 +  xmlns:​tools="​http://​schemas.android.com/​tools"​
 +  android:​layout_width="​match_parent"​
 +  android:​layout_height="​match_parent"​
 +  android:​paddingBottom="​@dimen/​activity_vertical_margin"​
 +  android:​paddingLeft="​@dimen/​activity_horizontal_margin"​
 +  android:​paddingRight="​@dimen/​activity_horizontal_margin"​
 +  android:​paddingTop="​@dimen/​activity_vertical_margin"​
 +  android:​orientation="​vertical"​
 +  tools:​context="​.MainActivity"​ >
 +  <​FrameLayout
 +    android:​id="​@+id/​frame1"​
 +    android:​layout_weight="​1"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​wrap_content"​ />
 +  <​FrameLayout
 +    android:​id="​@+id/​frame2"​
 +    android:​layout_weight="​1"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​wrap_content"​ />
 +</​LinearLayout>​
 +</​code>​ În metoda ''​onCreate()''​ corespunzătoare activității,​ se creează un obiect de tip ''​FragmentManager''​ pentru care se definesc operațiile care trebuie realizate (atomic) în cadrul unei tranzacții (obiectul de tip ''​FragmentTransaction''​ fiind obținut prin invocarea metodei ''​beginTransaction()''​). O tranzacție poate specifica suplimentar animațiile asociate unei tranziții prcum și dacă se dorește ca tranzacție să fie stocată și pe stivă. Realizarea propriu-zisă a operațiilor indicate se realizează doar atunci când se apelează metoda ''​commit()''​.\\ Pentru adăugarea unui fragment în cadrul unei activități,​ trebuie să se precizeze, ca parametrii ai metodei ''​add()''​ din clasa ''​FragmentTransaction''​ identificatorul containerului în care va fi plasat fragmentul, obiectul de tip fragment și (opțional) o etichetă (de tip șir de caractere) prin intermediul căreia acesta poate fi ulterior referit.<​code java>
 +@Override
 +protected void onCreate(Bundle savedInstanceState) {
 +  FragmentManager fragmentManager = getFragmentManager();​
 +  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​
 +  fragmentTransaction.add(android.R.id.frame1,​ new Fragment1());​
 +  fragmentTransaction.add(android.R.id.frame2,​ new Fragment2(),​ Constants.FRAGMENT2_TAG);​
 +  fragmentTransaction.commit();​
 +}
 +</​code>​ De remarcat faptul că referințele ''​R.id.frame1''​ și ''​R.id.frame2''​ se referă la conținutul activității.
 +
 +Pentru a asigura o experiență a utilizatorului consistentă,​ sistemul de operare Android asigură în mod automat persistența mecanismului de dispunere a framentelor și stiva asociată atunci când activitatea părinte este distrusă și (re)creată din cauza unei modificări a configurației. Din acest motiv, se recomandă ca toate interfețele grafice corespunzătoare diferitelor configurații să conțină toate containerele implicate în tranzacții de fragmente. Altfel, atunci când se va încerca restaurarea unor fragmente (în containere care nu exista în configurația respectivă),​ se pot produce anumite excepții. Dacă nu se dorește ca un container să fie afișat în cadrul unei interfețe grafice aferente unei configurații,​ se poate folosi atributul ''​visibility''​.
 +
 +<columns 100% 50%>
 +
 +<code xml>
 +<​LinearLayout ... >
 +  <​FrameLayout
 +    android:​id=@+id/​frame1
 +    ... />
 +  <​FrameLayout
 +    android:​id=@+id/​frame2
 +    android:​visibility="​gone"​
 +    ... />
 +</​LinearLayout>​
 +</​code>​
 +
 +<​newcolumn>​
 +
 +<code java>
 +FrameLayout frame1 = (FrameLayout)findViewById(R.id.frame1);​
 +FrameLayout frame2 = (FrameLayout)findViewById(R.id.frame2);​
 +frame2.setVisibility(Visibility.GONE);​
 +</​code>​
 +
 +</​columns>​
 +
 +=== Scoaterea unui Fragment ===
  
-Alternativ, fragmentul poate fi atașat în mod dinamic doar din codul sursă al activitățiifără a mai fi specificat în fișierul XML:+Pentru scoaterea unui fragment ​din cadrul unei activitățitrebuie ca anterior să se obțină o referință către acesta, pe baza identificatorului (metoda ''​findFragmentById()''​),​ respectiv pe baza etichetei (metoda ''​findFragmentByTag()''​). Ulterior, se apelează metoda ''​remove()''​ din clasa ''​FragmentTransaction''​ care primește ca parametru această referință.
  
 <code java> <code java>
 FragmentManager fragmentManager = getFragmentManager();​ FragmentManager fragmentManager = getFragmentManager();​
 FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​
-Fragment1 ​fragment1 = new Fragment1(); +Fragment ​fragment1 = fragmentManager.findFragmentById(R.id.fragment1); 
-fragmentTransaction.replace(android.R.id.content, ​fragment1);+Fragment fragment2 = fragmentManager.findFragmentByTag(Constants.FRAGMENT2_TAG);​ 
 +fragmentTransaction.remove(fragment1); 
 +fragmentTransaction.remove(fragment2);
 fragmentTransaction.commit();​ fragmentTransaction.commit();​
 </​code>​ </​code>​
  
-De remarcat faptul că referința ​''​R.id.content'' ​se referă la conținutul activității. Pentru a atașa / detașa fragmente de la o activitate se pot folosi și metodele ''​add()''​respectiv ​''​replace()''​.+Metoda ​''​findViewById()'' ​poate primi ca parametru și identificatorul containerului care conține fragmentul. În cazul în care aceasta nu are nici un conținutrezultatul metodei este ''​null''​.
  
-Pentru a crea interfața grafică corespunzătoare fragmentului respectiv, în codul sursă (indicat de proprietatea ''​android:​name''​) se va suprascrie metoda ''​onCreateView()''​ ce utilizează un obiect de tip ''​LayoutInflator''​ pentru a încărca fișierul XML. Alți parametri ai metodei sunt un ''​ViewGroup''​ care referă containerul din care face parte fragmentul (adică activitatea părinte), respectiv un obiect de tip ''​Bundle''​ utilizat pentru restaurarea stării în cazul în care s-a produs un eveniment de tip întrerupere.+=== Înlocuirea unui Fragment ===
  
-Clasa care definește codul sursă al fragmentului ​trebuie să extindă în mod necesar ​clasa ''​Fragment''​.+Pentru înlocuirea unui fragment din cadrul unei activități, trebuie să se precizeze, ca parametrii ai metodei ''​replace()''​ din clasa ''​FragmentTransaction'' ​identificatorul containerului în care va fi plasat fragmentul, obiectul de tip fragment și (opțional) o etichetă (de tip șir de caractere) prin intermediul căreia acesta poate fi ulterior referit.
  
-<file java Fragment1.java> +<code java> 
-package ro.pub.cs.pdsd.laborator03;+FragmentManager fragmentManager = getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
 +Fragment fragment2 = new Fragment2();​ 
 +fragmentTransaction.replace(R.id.frame1, fragment2, Constants.FRAGMENT2_TAG); 
 +fragmentTransaction.commit();​ 
 +</​code>​
  
-import android.app.Fragment;​ +=== Utilizarea Stivei de Fragmente === 
-import android.os.Bundle+ 
-import android.view.LayoutInflater+Prin intermediul fragmentelor,​ se oferă posibilitatea de a se crea interfețe grafice asociate activităților în mod dinamicUnele modificări între diferite stări pot fi considerate de utilizatori ca fiind ecrane noi, comportamentul așteptat fiind acela de restaurare a acestora atunci când se accesează butonul //Back//Un astfel de comportament presupune realizarea unei tranzacții de fragmente în sens invers. Această funcționalitate poate fi obținută prin utilizarea unei stive de fragmente, în care o tranzacție poate fi identificată prin intermediul unei etichete. 
-import android.view.View+ 
-import android.view.ViewGroup;+În acest sens, va trebui apelată explicit metoda ''​addToBackStack()''​ pentru obiectul de tip ''​FragmentTransaction''​ care se ocupă de atașarea și detașarea fragmentelor de la activitate, înainte de a se apela metoda ''​commit()''​. 
 + 
 +<code java> 
 +FragmentManager fragmentManager = getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
 +fragmentTransaction.add(R.id.frame1,​ new Fragment1(),​ Constants.FRAGMENT1_TAG);​ 
 +Fragment ​fragment2 = fragmentManager.findFragmentByTag(Constants.FRAGMENT2_TAG)
 +fragmentTransaction.remove(fragment2);​ 
 +fragmentTransaction.addToBackStack(null)
 +fragmentTransaction.commit();​ 
 +</​code>​ 
 + 
 +Astfel, atunci când se apelează metoda ''​commit()'',​ fragmentul 2 este oprit și salvat pe stiva de fragmente (în loc să fie distrus). Când utilizatorul accesează butonul //Back// fragmentul 1 este distrus și fragemtnul 2 este restaurat de pe stivă și (re)pornit. 
 + 
 +=== Folosirea de Animații la Tranziția dintre Fragmente === 
 + 
 +Tranzițiile între diferitele stări ale activității care implică operații cu fragmente pot fi realizate:​ 
 +  - folosind metoda ''​setTransition()''​ a unui obiect de tip ''​FragmentTransaction'',​ utilizând oricare dintre variantele predefinite,​ de tipul ''​FragmentTransaction.TRANSIT_FRAGMENT_...''​ <code java> 
 +fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_...)
 +</​code>​ 
 +  - folosind metoda ''​setCustomAnimation()''​ a unui obiect de tip ''​FragmentTransaction'',​ pentru utilizarea de animații definite de utilizator; metoda primește două argumente, indicând referințele către fișiere XML care descriu animațiile folosite pentru fragmentele care sunt adăugate, respectiv pentru fragmentele care sunt scoase <code java> 
 +fragmentTransaction.setCustomAnimations(R.animator.fade_in,​ R.animator.fade_out)
 +</​code>​ 
 + 
 +==== Interacțiunea dintre fragmente ==== 
 + 
 +Elementele grafice dintr-un fragment al unei activități pot fi accesate și din alt fragment al activității,​ prin obținerea unei referințe către activitatea părinte (folosind metoda ''​getActivity()''​) și accesarea controlului respectiv prin intermediul identificatorului său din cadrul fragmentului (folosind metoda ''​findViewById()''​). 
 + 
 +O astfel de abordare (folosind obiectul de tip ''​Activity''​ ca intermediar al comunicației) este recomandată pentru a asigura cuplarea slabă și autonomia fragmentelorAltfel, este permisă comunicația directă prin intermediul obiectului de tip ''​FragmentManager''​ asociat activității. 
 + 
 +În cazul evenimentelor produse la nivelul unui fragment, decizia cu privire la impactul pe care acesta îl are asupra interfeței grafice trebuie să aparțină activității. Astfel, fragmentul va invoca o metodă de callback la nivelul activității care va determina modul în care va fi tratat evenimentul. 
 + 
 +<code java> 
 +public interface OnEventProducedListener { 
 +  public void onEventProduced(Event event); 
 +}; 
 +</​code>​ 
 + 
 +<code java> 
 +public class SomeFragment extends Fragment { 
 + 
 +  OnEventProducedListener onEventProducedListener;
  
-public class Fragment1 extends Fragment { 
   @Override   @Override
-  public ​View onCreateView(LayoutInflater layoutInflater,​ ViewGroup container, Bundle state) { +  public ​void onAttach(Activity activity) { 
-    ​return layoutInflater.inflate(R.layout.fragment1container, false);+    ​super.onAttach(activity);​ 
 +    try { 
 +      onEventProducedListener = (OnEventProducedListener)activity;​ 
 +    } catch (ClassCastException classCastException) { 
 +      Log.e(Constants.TAG"​Parent activity does not implement OnEventProducedListener!"​);​ 
 +    } 
 +  } 
 +   
 +  private void onEventProduced(Event event) { 
 +    onEventProducedListener.onEventProduced(event);
   }   }
 } }
-</file>+</code>
  
-==== Ciclul de viață al unui fragment ====+<code java> 
 +public class SomeActivity extends Activity implements OnEventProducedListener { 
 +  @Override 
 +  protected void onCreate(Bundle savedStateInstance) { 
 +    super.onCreate(savedInstanceState);​ 
 +    FragmentManager framentManager ​new FragmentManager();​ 
 +    FragmentTransaction fragmentTransaction ​fragmentManager.beginTransaction();​ 
 +    fragmentTransaction.add(R.id.frame,​ new SomeFragment(),​ Constants.SOME_FRAGMENT_TAG);​ 
 +    fragmentTransaction.commit();​ 
 +    // ... 
 +  } 
 +   
 +  @Override 
 +  public void onEventProduced(Event event) { 
 +    // ... 
 +  } 
 +
 +</​code>​
  
-Ca și în cazul unei activități,​ ciclul ​de viață al unui fragment poate fi controlat prin intermediul unor metode care sunt apelate în mod automat. În cazul în care sunt suprascrise,​ acestea trebuie să apeleze în mod necesar metodele din clasa părinte. +===== Activitate ​de Laborator =====
-  * la creare sunt apelate metodele: ''​onAttach()'',​ ''​onCreate()'',​ ''​onCreateView()''​ și ''​onActivityCreated()''​ +
-  * în momentul în care devine vizibil, interacționând cu utilizatorul,​ sunt apelate ''​onStart()''​ și ''​onResume()''​ +
-  * în momentul în care nu mai este vizibil, nemaiinteracționând cu utilizatorul,​ sunt apelate ''​onPause()''​ și ''​onStop()''​ +
-  * la distrugere sunt apelate în plus și metodele ''​onDestroyView()'',​ ''​onDestroy()''​ și ''​onDetach()''​+
  
-Unele dintre aceste metode sunt aceleași ca în cazul activitățiloravând aceeași funcționalitate, în timp ce altele sunt specifice:​ +Se dorește implementarea unei aplicații Androidconținând o activitate ​conținând două fragmente, ​care să ofere utilizatorilor funcționalitatea necesară pentru a stoca un număr de telefon ​în agenda ​de contacte, specificând pentru acesta mai multe informații.
-  * ''​onAttach(Activity)''​ - apelată în momentul în care fragmentul este atașat la activitate +
-  * ''​onCreateView(LayoutInflator,​ ViewGroup, Bundle)''​ - apelată pentru a crea interfața grafică a fragmentului +
-  * ''​onActivityCreated(Bundle)''​ - apelată în momentul în care s-a terminat metoda ''​onCreate()''​ a activității care conține fragmentul +
-  * ''​onDestroyView()''​ - apelată pentru a distruge interfața grafică a fragmentului +
-  * ''​onDetach()''​ - apelată în momentul în care fragmentul este detașat ​de la activitate+
  
-Se observă faptul că restaurarea stării se poate face în oricare din metodele ''​onCreate()'',​ ''​onCreateView()'',​ respectiv ''​onActivityCreated()''​.+{{ :​laboratoare:​laborator04:​activitate_de_laborator01.png?​nolink&​300 }}
  
-<note important>​O distincție între activități și fragmente constă în faptul că fragmentele nu sunt adăugate în mod automat pe stivă, asemenea activităților în momentul în care aplicația nu mai este vizibilă astfel încât restaurarea stării nu se poate realiza corespunzătorÎn acest sens, va trebui apelată explicit metoda ''​addToBackStack(null)''​ pentru obiectul de tip ''​FragmentTransaction''​ care se ocupă de atașarea și detașarea fragmentelor de la activitate.</​note>​+{{ :​laboratoare:​laborator04:​activitate_de_laborator02.png?​nolink&​300 }}
  
-==== Interacțiunea dintre fragmente ====+**1.** În contul Github personal, să se creeze un depozit denumit '​Laborator04'​. Acesta trebuie să conțină unui fișier ''​README.md'',​ un fișier ''​.gitignore''​ specific unei aplicații Android și un fișier ''​LICENSE''​ care să descrie condițiile pentru utilizarea aplicației.
  
-Elementele grafice dintr-un fragment al unei activități pot fi accesate și din al fragment al activității, ​prin obținerea unei referințe către activitatea respectivă (metoda ​''​getActivity()''​) și accesarea controlului respectiv prin intermediul identificatorului său din cadrul fragmentului (folosind metoda ​''​findViewById()''​).+**2.** Să se cloneze într-un director de pe discul local conținutul depozitului la distanță astfel creat. În urma acestei operații, directorul Laborator04 va trebui să se conțină fișierele ​''​README.md''​, ''​.gitignore''​ care indică tipurile de fișiere (extensiileignorate ​și ''​LICENSE''​. ​<​code>​ 
 +student@eim2016:​~$ git clone https://​www.github.com/​perfectstudent/​Laborator04 
 +</​code>​
  
-===== Servicii =====+**3.** În directorul Laborator04 de pe discul local, să se creeze un proiect Eclipse denumit //​ContactsManager//​.
  
-TODO+Se utilizează următoarele detalii ale proiectului:​ 
 +  * **Application Name** - //Contacts Manager// 
 +  * **Project Name** - //​ContactsManager//​ 
 +  * **Package Name** - ''​ro.pub.cs.systems.eim.lab04.contactsmanager''​ 
 +  * **Minimum Required SDK** - API 16: Android 4.1 (Jelly Bean) 
 +  * **Target SDK** - API 16: Android 4.1 (Jelly Bean) 
 +  * **Compile With** - API 16: Android 4.1 (Jelly Bean) 
 +  * **Theme** - Holo Light with Dark Action Bar
  
-===== Activitate ​de Laborator =====+Ceilalți parametrii ​de configurare sunt impliciți:​ 
 +  * denumirea activității - ''​ContactsManagerActivity'';​ 
 +  * denumirea fișierului XML din ''​res/​layout''​ în care va fi construită interfața grafică - ''​activity_contacts_manager''​.
  
-==== Intenții ==== +**4.** În fișierul ''​activity_contacts_manager''​ din directorul ''​res/​layout'' ​să se construiască interfața grafică folosind: 
-  - Să se creeze încă o fereastră (activitate) pe care să se afișeze o imagine. Aceasta trebuie ​să apară pe ecran în momentul în care se apasă butonul //Fereastră Nouă//. +  ​* editorul vizual (//Graphical Layout//) 
-  ​- Să se adauge la fereastra creată anterior un buton de revenire. +  ​* editorul XML (manual)
-  - Să se afișeze fereastra creată anterior și atunci când se apasă butonul ​//Inserați Fereastra//. Să se afișeze sub imagine, într-un câmp text, care a fost butonul ce a determinat invocarea ferestrei. +
-  ​- Să se adauge la fereastra creată anterior un buton prin intermediul căruia să se schimbe imaginea existentă cu o alta.+
  
-==== Fragmente ​==== +Acesta va fi format din două containere de tip ''​FrameLayout''​ în care vor fi plasate fragmentele,​ dispuse vertical. 
-  - Să se plaseze ​conținutul celor două ferestre create anterior ​în două fragmente aparținând aceleiași activități, ​afișându-se simultan pe ecran+ 
-  - Să se adauge un două butoane ​în cadrul ​primului fragment ​pentru ​atașarearespectiv detașarea celui de-al doilea fragment ​de la activitate.+**5.** Să se definească două fragmente:​ 
 +  * ''​BasicDetailsFragment''​ cu interfața grafică în fișierul ''​fragment_basic_details.xml''​ din directorul ''​res/​layout'';​ acesta va fi afișat în permanență în cadrul activității (pe metoda ''​onCreate()''​ a acesteia se adaugă o instanță a fragmentului la containerul ​ <code java> 
 +FragmentManager fragmentManager ​getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction ​fragmentManager.beginTransaction();​ 
 +fragmentTransaction.add(R.id.containerTop,​ new BasicDetailsFragment());​ 
 +fragmentTransaction.commit();​ 
 +</​code>​ 
 +  * ''​AdditionalDetailsFragment''​ cu interfața grafică în fișierul ''​fragment_additional_details.xml''​ din directorul ''​res/​layout'';​ acesta va fi atașat / detașat la activitate la accesarea butonului //Show Additional Fields//, respectiv //Hide Additional Fields//. 
 + 
 +O clasă asociată unui fragment este derivată din ''​android.app.Fragment''​ și implementează metoda ''​onCreateView()''​ pe care se încarcă interfața grafică corespunzătoare:​ 
 + 
 +<code java> 
 +public class SomeFragment extends Fragment { 
 +  @Override 
 +  public View onCreateView(LayoutInflater layoutInflater,​ ViewGroup container, Bundle state) { 
 +    return layoutInflater.inflate(R.layout.fragment_some,​ container, false); 
 +  } 
 +
 +</​code>​ 
 + 
 +**6**. Să se implementeaze intefețele grafice ale fragmentelor,​ conținând următoarele controale:​ 
 +  * ''​fragment_basic_details.xml''​ conține mai multe elemente dispuse vertical și ocupând pe lățime întregul spațiu avut la dispoziție:​ 
 +    * un buton (''​Button''​) având mesajul //Show Additional Fields// în cazul în care celălalt fragment nu este afișat, respectiv mesajul //Hide Additional Fields// în cazul în care celălalt fragment este afișat, determinând atașarea / detașarea acestuia la activitate;​ 
 +    * patru controale de tip câmpuri text (''​EditText''​) prin care se introduc: 
 +      * numele; 
 +      * numărul de telefon - acest câmp este dezactivat (are proprietatea ''​android:​enabled="​false"''​),​ urmând ca valoarea sa să fie preluată din câmpul ''​extra''​ al unei intenții;​ 
 +      * adresa electronică;​ 
 +      * adresa poștală. 
 +  * ''​fragment_additional_details.xml''​ conține patru controale de tip câmpuri text dispuse vertical și ocupând pe lățime întregul spațiu avut la dispoziție,​ prin care se introduc: 
 +    * poziția ocupată; 
 +    * denumirea companiei;​ 
 +    * site-ul web; 
 +    * identificatorul pentru mesagerie instantanee. 
 + 
 +**7.** Să se implementeaze interacțiunea cu utilizatorul a aplicației.  
 +  * în metoda ''​onActivityCreated()''​ a fragmentului ''​BasicDetailsFragment''​ se obțin referințe către butoanele //Show Additional Details// / //Hide Additional Details//, respectiv //Save// și //Cancel// prin intermediul metodei ''​getActivity().findViewById(R.id....)'';​ 
 +  * se implementează o clasă ascultător pentru butoane, care implementează ''​View.OnClickListener''​ și (re)definește metoda ''​onClick(View v)'';​ în funcție de identificatorul butonului care este transmis ca parametru al metodei, sunt realizate următoarele acțiuni: 
 +    * butonul //Show Additional Details// / //Hide Additional Details// - atașează / detașează fragmentul ''​AdditionalDetailsFragment''​ la activitate în funcție de existența / inexistența acestuia, modificând corespunzător textul afișat pe buton: <​code>​ 
 +FragmentManager fragmentManager ​getActivity().getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction ​fragmentManager.beginTransaction();​ 
 +AdditionalDetailsFragment additionalDetailsFragment ​(AdditionalDetailsFragment)fragmentManager.findFragmentById(R.id.containerBottom);​ 
 +if (additionalDetailsFragment ​== null) { 
 +  ​fragmentTransaction.add(R.id.containerBottom,​ new AdditionalDetailsFragment());​ 
 +  ((Button)v).setText(getActivity().getResources().getString(R.string.hide_additional_fields));​ 
 +  fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK);​ 
 +} else { 
 +  fragmentTransaction.remove(additionalDetailsFragment);​ 
 +  ((Button)v).setText(getActivity().getResources().getString(R.string.show_additional_fields));​ 
 +  fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_EXIT_MASK);​ 
 +
 +fragmentTransaction.commit();​ 
 +</​code>​ 
 +    * butonul //​Save// ​lansează în execuție aplicația Android nativă pentru stocarea unui contact în agenda telefonică,​ după ce în prealabil au fost preluate informațiile din controalele grafice: <code java> 
 +Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);​ 
 +intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);​ 
 +if (name != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.NAME,​ name); 
 +
 +if (phone != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.PHONE,​ phone); 
 +
 +if (email != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.EMAIL,​ email); 
 +
 +if (address != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.POSTAL,​ address); 
 +
 +if (jobTitle != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE,​ jobTitle);​ 
 +
 +if (company != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.COMPANY,​ company); 
 +
 +ArrayList<​ContentValues>​ contactData = new ArrayList<​ContentValues>​();​ 
 +if (website != null) { 
 +  ContentValues websiteRow = new ContentValues();​ 
 +  websiteRow.put(ContactsContract.Data.MIMETYPE,​ ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);​ 
 +  websiteRow.put(ContactsContract.CommonDataKinds.Website.URL,​ website); 
 +  contactData.add(websiteRow);​ 
 +
 +if (im != null) { 
 +  ContentValues imRow = new ContentValues();​ 
 +  imRow.put(ContactsContract.Data.MIMETYPE,​ ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);​ 
 +  imRow.put(ContactsContract.CommonDataKinds.Im.DATA,​ im); 
 +  contactData.add(imRow);​ 
 +
 +intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA,​ contactData);​ 
 +getActivity().startActivity(intent);​ 
 +</​code>​ Intenția pentru realizarea acestei operații are asociată acțiunea ''​ContactsContract.Intents.Insert.ACTION''​ și tipul ''​ContactsContract.RawContacts.CONTENT_TYPE''​. Informațiile care se doresc a fi completate sunt atașate în câmpul ''​extra''​ al acesteia, având cheile:\\ ✔ ''​ContactsContract.Intents.Insert.NAME'';​\\ ✔ ''​ContactsContract.Intents.Insert.PHONE'';​\\ ✔ ''​ContactsContract.Intents.Insert.EMAIL'';​\\ ✔ ''​ContactsContract.Intents.Insert.POSTAL'';​\\ ✔ ''​ContactsContract.Intents.Insert.JOB_TITLE'';​\\ ✔ ''​ContactsContract.Intents.Insert.COMPANY'';​\\ Pentru site-ul web și identificatorul de mesagerie instantanee,​ se folosește un tablou de elemente ''​ContentValues''​ în care se specifică înregistrări de tipul ''​CommonDataKinds.Website.URL'',​ respectiv ''​CommonDataKinds.Im.DATA'';​\\ Pentru a putea gestiona agenda telefonică,​ este necesar ca în fișierul ''​AndroidManifest.xml''​ să fie specificate următoarele permisiuni: <code xml> 
 +<​uses-permission 
 +  android:​name="​android.permission.READ_CONTACTS"​ /> 
 +<​uses-permission 
 +  android:​name="​android.permission.WRITE_CONTACTS"​ /> 
 +</​code>​ 
 +    * butonul //Cancel// - termină aplicația Android: <code java> 
 +getActivity().finish();​ 
 +</​code>​ 
 +  * se înregistrează o instanță a clasei ascultător ca mecanism de tratare a evenimentelor de tip accesare a butoanelor din cadrul interfeței grafice, prin apelul metodei ''​setOnClickListener()''​. 
 + 
 +**8.** ​Să se modifice aplicația Android [[https://​github.com/​eim2016/​Laborator03.git|Phone Dialer]] astfel încât să conțină un buton suplimentar prin care este invocată aplicația //Contacts Manager// căreia îi transmite numărul de telefon format și așteptând un rezultat cu privire la stocarea contactului ​în agenda telefonică
 + 
 +{{ :​laboratoare:​laborator04:​activitate_de_laborator03.png?​nolink&​300 }} 
 + 
 +{{ :​laboratoare:​laborator04:​activitate_de_laborator04.png?​nolink&​500 }} 
 + 
 +Metoda de tratare a evenimentului de tip accesare a butonului de stocare a numărului de telefon în agenda telefonică invocă o intenție asociată aplicației //Contacts Manager//, transmițând ​și numărul de telefon în câmpul ''​extra''​ asociat acesteia, identificabil prin intermediul unei chei. <code java> 
 +if (phoneNumber.length() > 0) { 
 +  intent = new Intent("​ro.pub.cs.systems.eim.lab04.contactsmanager.intent.action.ContactsManagerActivity"​);​ 
 +  intent.putExtra("​ro.pub.cs.systems.eim.lab04.contactsmanager.PHONE_NUMBER_KEY",​ phoneNumber);​ 
 +  startActivityForResult(intent,​ Constants.CONTACTS_MANAGER_REQUEST_CODE);​ 
 +} else { 
 +  Toast.makeText(getApplication(),​ getResources().getString(R.string.phone_error),​ Toast.LENGTH_LONG).show();​ 
 +
 +</​code>​ 
 + 
 +**9.** Să se modifice aplicația Android //Contacts Manager// astfel încât să poată fi lansată în execuție doar din contextul altei activități, ​prin intermediul unei intenții care conține în câmpul ''​extra''​ un număr de telefon, identificabil prin cheia ''​ro.pub.cs.systems.eim.lab04.contactsmanager.PHONE_NUMBER_KEY'',​ acesta fiind plasat în câmpul text needitabil corespunzător. Totodată, va transmite înapoi rezultatul operației de stocare (''​Activity.RESULT_OK''​ sau ''​Activity.RESULT_CANCELED''​). 
 + 
 +  * în fișierul ''​AndroidManifest.xml'' ​se modifică filtrul de intenții (acțiunea și categoria), astfel încât activitatea să poată fi rulată doar prin intermediul unei intenții <file xml AndroidManifest.xml> 
 +<​manifest ...> 
 +  ​<​application ...> 
 +    <​activity 
 +      android:​name="​.graphicuserinterface.ContactsManagerActivity"​ 
 +      android:​label="​@string/​app_name"​ > 
 +      <intent-filter>​ 
 +        <action android:​name="​ro.pub.cs.systems.eim.lab04.contactsmanager.intent.action.ContactsManagerActivity"​ /> 
 +        <​category android:​name="​android.intent.category.DEFAULT"​ /> 
 +      </​intent-filter>​ 
 +    </​activity>​ 
 +  </​application>​ 
 +</​manifest>​ 
 +</​file>​ 
 +  * în metoda ''​onActivityCreated()''​ asociată fragmentului ''​BasicDetailsFragment''​ este verificată intenția cu care este pornită activitatea părinte, și în cazul în care aceasta nu este nulă, este preluată informația din secțiunea ''​extra'',​ identificată prin cheia ''​ro.pub.cs.systems.eim.lab04.contactsmanager.PHONE_NUMBER_KEY'',​ conținutul său fiind plasat ​în cadrul ​câmpului text corespunzător:​ <code java> 
 +if (intent != null) { 
 +  String phone = intent.getStringExtra("​ro.pub.cs.systems.eim.lab04.contactsmanager.PHONE_NUMBER_KEY"​);​ 
 +  if (phone != null) { 
 +    phoneEditText.setText(phone);​ 
 +  } else { 
 +    Activity activity = getActivity();​ 
 +    Toast.makeText(activity,​ activity.getResources().getString(R.string.phone_error),​ Toast.LENGTH_LONG).show();​ 
 +  } 
 +}  
 +</​code>​ 
 +  * pe metodele de tratare a evenimentelor de accesare a butoanelor:​ 
 +    * //Save// - este lansată în execuție aplicația nativă ​pentru ​gestiunea agendei telefonicefolosind un cod de cerere prin intermediul căruia se va verifica rezultatul furnizat: <code java> 
 +getActivity().startActivityForResult(intent,​ Constants.CONTACTS_MANAGER_REQUEST_CODE);​ 
 +</​code>​ 
 +    * //​Cancel// ​se transmite înapoi rezultatul <code java> 
 +getActivity().setResult(Activity.RESULT_CANCELED,​ new Intent());​ 
 +</​code>​ 
 + 
 +  * în metoda ''​onActivityResult()''​ asociată activității ''​ContactsManagerActivity'',​ în momentul în care s-a părăsit aplicația nativă pentru gestiunea agendei telefonice, se verifică codul de cerere și se transmite înapoi un rezultat: <code java> 
 +public void onActivityResult(int requestCode,​ int resultCode, Intent intent) { 
 +switch(requestCode) { 
 +  case Constants.CONTACTS_MANAGER_REQUEST_CODE:​ 
 +    setResult(resultCode,​ new Intent());​ 
 +    finish(); 
 +    break; 
 +  } 
 +
 +</​code>​ 
 + 
 +**10.** ​ Să se încarce modificările realizate în cadrul depozitului '​Laborator04'​ de pe contul Github personal, folosind un mesaj sugestiv. <​code>​ 
 +student@eim2016:​~/​Laborator04$ git add * 
 +student@eim2016:​~/​Laborator04$ git commit -m "​implemented taks for laboratory 04" 
 +student@eim2016:​~/​Laborator04$ git push origin master 
 +</​code>​
  
 ===== Resurse Utile ===== ===== Resurse Utile =====
laboratoare/laborator04.1426448739.txt.gz · Last modified: 2016/02/09 11:19 (external edit)
CC Attribution-Share Alike 3.0 Unported
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0