Differences

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

Link to this comparison view

laboratoare:laborator05_old [2016/03/23 23:10] (current)
Andrei Roșu-Cojocaru created
Line 1: Line 1:
 +====== Laborator 05. Proiectarea Interfețelor Grafice (II) ======
  
 +===== Controale pentru colecții de date =====
 +
 +În situația în care aplicația Android operează cu un volum semnificativ informații provenind din diverse surse de date, trebuie utilizat un set de obiecte specializate pentru gestiunea lor:
 +  - de tip **container** (derivate din clasa ''​android.widget.AdapterView''​) responsabil pentru afișarea acestora pe ecran; el conține câte un element de tip ''​android.widget.View''​ pentru fiecare element al listei, oricât de complex;
 +  - de tip **adaptor** (ce implementează interfața ''​android.widget.Adapter''​) care asigură ​
 +    - legătura dintre resursele care dețin datele respective (fișiere, furnizori de conținut) și controalele de pe suprafața de afișare, cu care interacționează utilizatorul;​ orice modificare asupra datelor va fi realizată prin intermediul acestei componente;
 +    - modul de dispunere a informațiilor pentru fiecare element în parte. ​
 +
 +Această arhitectură corespunde șablonului MVC (Model - View - Controller) în care modelul este reprezentat de sursele de date, vizualizarea de componenta grafică în care este afișat conținutul acestora iar controlorul de clasa adaptor.
 +
 +{{ :​laboratoare:​laborator05:​arhitectura_de_nivel_inalt_controale_grafice.png?​nolink }}
 +
 +==== Tipuri de containere pentru colecții de date ====
 +
 +Pentru afișarea colecțiilor de date se folosesc elemente grafice de tip container, referite sub denumirea de liste. Acestea controlează dimensiunile și modul de dispunere al componentelor,​ structura unei componente fiind însă gestionată de elementul de tip adaptor asociat. Vor fi prezentate inițial particularitățile pe care le pun la dispoziție fiecare dintre aceste controale, detaliile de implementare referitoare la tipurile de adaptoare asociate fiind tratate ulterior.
 +
 +Cele mai utilizate tipuri de containere pentru colecțiile de date sunt ''​ListView'',​ ''​GridView'',​ ''​Spinner''​ și ''​Gallery''​.
 +
 +=== ListView ===
 +
 +Un obiect de tip [[http://​developer.android.com/​reference/​android/​widget/​ListView.html|ListView]] afișează elementele pe care le conține vertical, existând posibilitatea ca acestea să fie derulate în situația în care se depășește dimensiunea suprafeței de afișare. ​
 +
 +Elementele pot fi separate de anumite delimitatoare (resursă grafică sau culoare) specificate prin proprietatea ''​divider''​ (pentru care se poate preciza și o înălțime - atributul ''​dividerHeight''​). Un astfel de control poate fi precedat, respectiv succedat de un control grafic, existând posibilitatea distingerii acestor elemente prin delimitatoare (proprietățile ''​headerDividersEnabled'',​ respectiv ''​footerDividersEnabled''​).
 +
 +Dacă este folosit într-o activitate obișnuită,​ controlul de tip ''​ListView''​ trebuie să fie specificat în interfața grafică din fișierul XML asociat ferestrei din care face parte, precizându-se și un identificator prin intermediul căruia să poată fi referit:
 +
 +<file xml activity_movies.xml>​
 +<​RelativeLayout 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"  ​
 +  tools:​context="​.MoviesActivity"​ >
 +
 +  <​ListView
 +    android:​id="​@+id/​movies_list_view"​
 +    android:​layout_width="​wrap_content"​
 +    android:​layout_height="​wrap_content"​
 +    android:​divider="​@color/​lightblue"​
 +    android:​dividerHeight="​5dp" ​  />
 +
 +</​RelativeLayout>​
 +</​file>​
 +
 +Elementele (de tip ''​View''​) pe care le afișează un astfel de obiect sunt furnizate de un ''​ListAdapter''​ care trebuie precizat prin intermediul metodei ''​setAdapter(ListAdapter)''​. În codul sursă, se va specifica un tip de adaptor care va fi inițializat cu datele pe care le va conține obiectul respectiv, după care acesta va fi asociat controlului grafic.
 +
 +<code java>
 +public class MoviesActivity extends Activity {
 +
 +  String[] movies = new String[] { 
 +    "The Shawshank Redemption", ​
 +    "The Godfather", ​
 +    "The Godfather: Part II", ​
 +    "The Dark Knight", ​
 +    "Pulp Fiction", ​
 +    "The Good, the Bad and the Ugly", ​
 +    "​Schindler'​s List", ​
 +    "12 Angry Men", ​
 +    "The Lord of the Rings: The Return of the King", ​
 +    "Fight Club" ​
 +  };
 +
 +  @Override
 +  protected void onCreate(Bundle savedInstanceState) {
 +    super.onCreate(savedInstanceState);​
 +    setContentView(R.layout.activity_movies);​
 +    ​
 +    ListView listview = (ListView)findViewById(R.id.movies_list_view);​
 +    final ArrayAdapter<​String>​ adapter = new ArrayAdapter<​String>​(this,​ android.R.layout.simple_list_item_1,​ movies);
 +    listview.setAdapter(adapter);​
 +  }
 +}
 +</​code>​
 +
 +Activitatea ''​MoviesActivity''​ afișează denumirea unor filme sub forma unei liste în care elementele sunt dispuse unele sub altele.
 +
 +<note tip>Se observă că elementul de tip adaptor primește un parametru care indică modul de dispunere a conținutului listei. În acest sens se pot utiliza layout-uri predefinite,​ corespunzătoare tipului de obiect de tip ''​AdapterView''​ sau se pot folosi layout-uri definite de utilizator (pentru structuri de date mai complexe ce implică afișarea informației într-un format dependent de acest context).</​note>​
 +
 +<note tip>​Toate operațiile asupra conținutului elementului de tip ''​ListView''​ se vor realiza prin intermediul obiectului ''​ListAdapter''​.</​note>​
 +
 +În cazul în care se produc anumite evenimente ce implică interacțiunea utilizatorului asupra acestui control, ele vor fi tratate prin clase ascultător distincte pentru fiecare tip de acțiune (apăsarea - de scurtă durată sau de lungă durată - asupra unui element din cadrul listei, selectarea unei intrări).
 +
 +<code java>
 +listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 +  @Override
 +  public void onItemClick(AdapterView<?>​ parent, View view, int position, long id) {
 +    Toast.makeText(MainActivity.this,​ "You have clicked on "​+adapter.getItem(position)+"​ item", Toast.LENGTH_SHORT).show();​
 +  }
 +});
 +listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
 +  @Override
 +  public boolean onItemLongClick(AdapterView<?>​ parent, View view, int position, long id) {
 +    Toast.makeText(MainActivity.this,​ "You have long clicked on "​+adapter.getItem(position)+"​ item", Toast.LENGTH_SHORT).show();​
 +    return true;
 +  }
 +});
 +listview.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
 +  @Override
 +  public void onItemSelected(AdapterView<?>​ parent, View view, int position, long id) {
 +    Toast.makeText(MainActivity.this,​ "You have selected the "​+adapter.getItem(position)+"​ item", Toast.LENGTH_SHORT).show();​
 +  }
 +  @Override
 +  public void onNothingSelected(AdapterView<?>​ parent) {
 +    Toast.makeText(MainActivity.this,​ "​Currently,​ the list has no item selected",​ Toast.LENGTH_SHORT).show();​
 +  }
 +});
 +</​code>​
 +
 +Aceste metode de tratare a evenimentelor din clasele ascultător primesc ca parametri:
 +  * un obiect de tip ''​AdapterView'',​ indicând lista din care a fost accesat un element;
 +  * un ''​View'',​ reprezentând elementul din cadrul listei care a fost accesat;
 +  * poziția la care se află în listă elementul care a fost accesat;
 +
 +<note important>​Numerotarea elementelor în cadrul listei se face începând cu 0.</​note>​
 +
 +  * identificatorul elementului care a fost accesat, conform obiectului de tip adaptor (poate fi obținut prin intermediul metodei ''​getItemId()''​);​ această valoare este dependentă atât de obiectul de tip adaptor folosit, cât și de sursa de date
 +
 +Conținutul listei poate fi precizat și în fișierul de resurse ''/​res/​values/​strings.xml''​
 +
 +<file xml strings.xml>​
 +<?xml version="​1.0"​ encoding="​utf-8"?>​
 +<​resources>​
 +  <string name="​app_name">​Top 10 Movies</​string>​
 +  <​string-array name="​movies">​
 +    <​item>​The Shawshank Redemption</​item>​
 +    <​item>​The Godfather</​item>​
 +    <​item>​The Godfather: Part II</​item>​
 +    <​item>​The Dark Knight</​item>​
 +    <​item>​Pulp Fiction</​item>​
 +    <​item>​The Good, the Bad and the Ugly</​item>​
 +    <​item>​Schindler&​apos;​s List</​item>​
 +    <​item>​12 Angry Men</​item>​
 +    <​item>​The Lord of the Rings: The Return of the King</​item>​
 +    <​item>​Fight Club</​item>​
 +  </​string-array>​
 +</​resources>​
 +</​file>​
 +și apoi
 +  * specificat drept conținut al elementului de tip ''​ListView''​ <code xml>
 +<​ListView ... android:​entries="​@array/​movies"​ ... />
 +</​code>​ (situație în care obiectul de tip adaptor nu va fi instanțiat,​ fiind preluat cel creat în mod automat cu intrările din resursa de tip tablou specificată în fișierul XML) 
 +sau
 +  * încărcat în codul sursă: <code java>
 +movies = getResources().getStringArray(R.array.movies);​
 +</​code>​
 +
 +<note tip>​Utilizarea fișierelor de resurse pentru specificarea unei surse de date scalează foarte bine cu situația în care aplicația trebuie să fie localizată.</​note>​
 +
 +Dacă se dorește ca elementele din listă să poată fi selectate, în constructorul obiectului de tip adaptor se va specifica un model care suportă o astfel de operație ''​android.R.layout.simple_list_item_checked''​.
 +<code java>
 +final ArrayAdapter<​String>​ adapter = new ArrayAdapter<​String>​(this,​ android.R.layout.simple_list_item_checked,​ movies);
 +</​code>​
 +Numărul de componente din cadrul listei care pot fi selectate concomitent pot fi:
 +  * nici unul (valoare implicită):​ ''​ListView.CHOICE_MODE_NONE''​
 +  * unul singur: ''​ListView.CHOICE_MODE_SINGLE''​
 +  * mai multe: ''​ListView.CHOICE_MODE_MULTIPLE''​
 +Aceste constante vor fi transmise ca parametru metodei ''​setChoiceMode()''​ definită de clasa ''​ListView''​.
 +Pentru a se determina intrările care au fost selectate de utilizator, va fi parcursă întreaga listă (numărul de elemente fiind obținut prin metoda ''​getCount()''​),​ starea în care se găsesc acestea fiind verificată prin intermediul meteodei ''​isItemChecked(int)''​ care primește ca parametru poziția din listă pentru care se dorește să se realizeze textul.
 +<code java>
 +String itemsSelected = "You have selected the following items:​\n";​
 +for (int position = 0; position < listview.getCount();​ position++)
 +  if (listview.isChecked(position)) {
 +    itemsSelected += " * "​+listview.getItemAtPosition(position)+"​\n";​
 +  }
 +</​code>​
 +
 +Totodată, există posibilitate de filtrare a conținutului unei liste în funcție de o valoare dată de utilizator, comportament ce poate fi obținut printr-un apel al metodei ''​setFilterEnabled(boolean)''​.
 +
 +== ExpandableListView ==
 +
 +Un caz particular de listă este ''​ExpandableListView'',​ care permite asocierea conținuturilor în grupuri, acestea putând fi expandate. ​
 +
 +Elementele sunt însoțite de un indicator care specifică starea sa (grup - expandat sau nu, respectiv element al grupului, evidențiindu-se ultimul din sublistă), existând astfel elemente de demarcație în modurile de dispunere implicite, ele putând fi de asemenea precizate de utilizator prin intermediul proprietăților ''​groupIndicator'',​ respectiv ''​childIndicator''​. ​
 +
 +Layout-urile predefinite pentru acest obiect sunt ''​android.R.layout.simple_expandable_list_item_1''​ și ''​android.R.layout.simple_expandable_list_item_2''​.
 +
 +=== GridView ===
 +
 +[[http://​developer.android.com/​reference/​android/​widget/​GridView.html|GridView]] este un tip de obiect folosit pentru afișarea conținuturilor sub formă tabulară, existând posibilitatea de derulare a acestora în cazul în care se depășește dimensiunea suprafeței de afișare. ​
 +
 +Se poate preciza numărul de coloane prin proprietatea ''​numColumns'',​ aceasta având de regulă valoarea ''​AUTO_FIT'',​ astfel încât să se determine în mod automat acest atribut în funcție de conținutul tabelului și de spațiul avut la dispoziție. Dimensiunea fiecărei coloane poate fi specificată explicit (prin ''​columnWidth''​ - dându-se o valoare însoțită de o unitate de măsură). De asemenea, în condițiile în care dimensiunile spațiului de afișare permit, tabelul poate fi extins în funcție de valoarea indicată pentru proprietatea ''​stretchMode''​ (''​spacingWidth''​ / ''​spacingWidthUniform''​ pentru extinderea spațiului dintre coloane, respectiv ''​columnWidth''​ pentru extinderea coloanelor). Spațierea dintre rânduri și coloane este controlată prin ''​horizontalSpacing''​ și ''​verticalSpacing''​.
 +
 +<file xml activity_contacts.xml>​
 +<​RelativeLayout 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"​
 +  tools:​context="​.ContactsActivity"​ >
 +
 +  <​GridView
 +    android:​id="​@+id/​contacts_grid_view"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​match_parent"​
 +    android:​horizontalSpacing="​10dp"​
 +    android:​verticalSpacing="​10dp"​
 +    android:​numColumns="​auto_fit"​
 +    android:​gravity="​center"​ >
 +    ​
 +    <!-- grid layout content -->
 +    ​
 +  </​GridLayout>​
 +
 +</​RelativeLayout>​
 +</​file>​
 +
 +<file java ContactsActivity.java>​
 +public class ContactsActivity extends Activity implements LoaderManager.LoaderCallbacks<​Cursor>​ {
 +
 +  GridView gridview;
 +  SimpleCursorAdapter adapter;
 +   
 +  @Override
 +  protected void onCreate(Bundle savedInstanceState) {
 +    super.onCreate(savedInstanceState);​
 +    setContentView(R.layout.activity_main);​
 +    ​
 +    gridview = (GridView)findViewById(R.id.contacts_grid_view);​
 +    String[] projections = new String[] {Contacts.DISPLAY_NAME};​
 +    int[] views = new int[] {android.R.id.text1}; ​
 +    adapter = new SimpleCursorAdapter(this,​ android.R.layout.simple_list_item_1,​ null, projections,​ views, 0);
 +    gridview.setAdapter(adapter);​
 +    getLoaderManager().initLoader(0,​ null, this);
 +  }
 +
 +  public Loader<​Cursor>​ onCreateLoader(int id, Bundle data) {
 +    return new CursorLoader(this,​ Contacts.CONTENT_URI,​ null, null, null, Contacts.DISPLAY_NAME);​
 +  }
 +
 +  public void onLoadFinished(Loader<​Cursor>​ loader, Cursor cursor) {
 +    adapter.swapCursor(cursor);​
 +  }
 +
 +  public void onLoaderReset(Loader<​Cursor>​ loader) {
 +    adapter.swapCursor(null);​
 +  }
 +}
 +</​file>​
 +
 +Activitatea ''​ContactsActivity''​ afișează numele persoanelor din lista de contacte a telefonului sub forma unui tabel. Având în vedere faptul că volumul de informații poate fi semnificativ,​ încărcarea acestora nu se face în cadrul activității (pentru a nu genera situații de neresponsivitate a interfeței grafice), ci prin intermediul unui obiect de tip ''​Loader''​.
 +
 +<note important>​Pentru a putea accesa lista de contacte a telefonului,​ aplicația trebuie să specifice permisiunile corespunzătoare în fișierul ''​AndroidManifest.xml'':​\\
 +''<​uses-permission android:​name="​android.permission.READ_CONTACTS"​ />''</​note>​
 +
 +=== Spinner ===
 +
 +În cadrul elementului de tip [[http://​developer.android.com/​reference/​android/​widget/​Spinner.html|Spinner]],​ nu se afișează întregul conținut al listei (astfel încât să nu se ocupe întregul spațiu disponibil),​ ci numai valoarea selectată în mod curent, existând posibilitatea expandării pentru vizualizare a tuturor componentelor în momentul accesării acesteia.
 +
 +Modul în care sunt afișate intrările dintre care se poate realiza selecția este controlat prin proprietatea ''​spinnerMode''​ care poate lua valorile ''​dialog''​ dacă se dorește afișarea unei ferestre de dialog, respectiv ''​dropdown''​ dacă lista va fi expandată sub controlul care conține valoarea curentă.
 +  * pentru modul de dispunere **''​dialog''​**,​ se poate indica un mesaj suplimentar care va fi afișat în cadrul ferestrei ce prezintă toate opțiunile ce pot fi selectate prin intermediul atributului ''​prompt''​
 +  * pentru modul de dispunere **''​dropdown''​** pot fi specificate în plus următoarele proprietăți
 +    * ''​dropDownHorizontalOffset''​ / ''​dropDownVerticalOffset''​ indică valoarea cu care va fi decalată pe orizontală / verticală lista derulante ce conține celelalte opțiuni
 +    * ''​dropDownSelector''​ este o referință către o resursă pentru a indica modul de selecție a unui element din cadrul listei
 +    * ''​dropDownWidth''​ reprezintă lățimea listei derulante ce conține celelalte opțiuni (poate avea valorile ''​match_parent''​ sau ''​wrap_content''​)
 +    * ''​popupBackground''​ specifică o resursă ce va fi desenată pe fundal atâta vreme cât este afișată lista derulantă ce conține celelalte opțiuni
 +
 +<columns 100% 50%>
 +
 +<code xml>
 +<Spinner
 +  android:​id="​@+id/​spinner"​
 +  android:​layout_width="​match_parent"​
 +  android:​layout_height="​wrap_content"​
 +  android:​entries="​@array/​device_manufacturer"​
 +  android:​spinnerMode="​dialog"​
 +  android:​prompt="​@string/​prompt"​ />
 +</​code>​
 +
 +<​newcolumn>​
 +
 +<code xml>
 +<Spinner
 +  android:​id="​@+id/​spinner"​
 +  android:​layout_width="​match_parent"​
 +  android:​layout_height="​wrap_content"​
 +  android:​entries="​@array/​device_manufacturer"​
 +  android:​spinnerMode="​dropdown"​
 +  android:​dropDownHorizontalOffset="​10dp"​
 +  android:​dropDownVerticalOffset="​10dp"​
 +  android:​popupBackground="​@drawable/​mobiledevice"​
 +  android:​dropDownSelector="​@color/​lightblue"​ />
 +</​code>​
 +</​columns>​
 +
 +{{ :​laboratoare:​laborator05:​sample_spinner_dialog.png?​nolink&​400 }}
 +
 +{{ :​laboratoare:​laborator05:​sample_spinner_dropdown.png?​nolink&​400 }}
 +
 +Pentru un astfel de obiect vor trebui specificate două mecanisme de afișare a conținutului:​
 +  - pentru afișare în momentul în care lista nu este expandată, parametru care va fi specificat în momentul în care se construiește obiectul de tip adaptor; valoarea predefinită este ''​android.R.layout.simple_spinner_item'';​
 +  - pentru afișare în momentul în care lista este expandată, atribut care va fi indicată prin intermediul metodei ''​setDropDownViewResource''​ a obiectului adaptor; valoarea predefinită este ''​android.R.layout.simple_spinner_dropdown_item''​.
 +
 +<code java>
 +Spinner spinner = (Spinner)findResourceById(R.id.spinner);​
 +ArrayAdapter<​String>​ adapter = new ArrayAdapter<​String>​(this,​ android.R.layout.simple_spinner_item,​ deviceManufacturers);​
 +adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);​
 +spinner.setAdapter(adapter);​
 +</​code>​
 +
 +=== Gallery ===
 +
 +Elementul de tip [[http://​developer.android.com/​reference/​android/​widget/​Gallery.html|Gallery]] este un tip de control folosit pentru afișarea conținutului în cadrul unei liste dispuse orizontal, în care obiectul selectat este întotdeauna centrat. De obicei, este folosit pentru resurse multimedia de tip imagine.
 +
 +<note important>​Începând cu API 16, utilizarea acestui tip de conținut este descurajată,​ recomandându-se în schimb utilizarea altor elemente care suportă derularea componentelor pe orizontală,​ cum ar fi ''​HorizontalScrollView''​ și ''​ViewPager''​.</​note>​
 +
 +Pentru un ''​Gallery''​ pot fi specificate următoarele atribute:
 +  * ''​animationDuration''​ - durata animației de tranziție de la un element la altul, exprimată în milisecunde
 +  * ''​spacing''​ - spațierea dintre elementele galeriei; această proprietate se folosește mai ales atunci când nu se specifică un anumit stil
 +<spoiler |Stiluri asociate unei galerii>
 +Pentru a aplica un stil galeriei, dintre cele care sunt deja predefinite,​ acesta trebuie declarat sub o denumire în fișierul ''/​res/​values/​attrs.xml''​.
 +
 +<file xml attrs.xml>​
 +<​resources>​
 +  <​declare-styleable name="​gallery">​
 +    <attr name="​android:​galleryItemBackground"​ />
 +  </​declare-styleable>​
 +</​resources>​
 +</​file>​
 +
 +Ulterior, în codul sursă, trebuie obținută o referință către acest stil, care trebuie asociată elementului de tip ''​ImageView''​ din cadrul galeriei.
 +
 +<code java>
 +TypedArray typedArray = obtainStyledAttributes(R.styleable.gallery);​
 +int backgroundResource = typedArray.getResourceId(R.styleable.gallery_android_galleryItemBackground);​
 +typedArray.recycle();​
 +// ...
 +imageview.setBackgroundResource(backgroundResource);​
 +</​code>​
 +</​spoiler>​
 +  * ''​unselectedAlpha''​ - nivelul de transparență pentru elementele care nu sunt selectate, exprimată ca număr zecimal, cuprins între 0 și 1
 +
 +<file xml activity_faculty.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:​orientation="​vertical"​
 +  tools:​context="​.FacultyActivity"​ >
 +  <​TextView
 +    android:​id="​@+id/​textview"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​wrap_content"​
 +    android:​gravity="​center"​
 +    android:​text="​@string/​app_name"​
 +    android:​textSize="​16sp"​ />
 +  <Gallery
 +    android:​id="​@+id/​gallery"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​wrap_content"​
 +    android:​padding="​10dp" ​
 +    android:​animationDuration="​500"​
 +    android:​spacing="​5dp"​
 +    android:​unselectedAlpha="​0.5"​ />
 +  <​ImageView
 +    android:​id="​@+id/​imageview"​
 +    android:​layout_gravity="​center"​
 +    android:​padding="​10dp"​
 +    android:​layout_width="​320dp"​
 +    android:​layout_height="​240dp"​
 +    android:​scaleType="​fitXY"​
 +    android:​contentDescription="​@string/​content_description"​ />
 +</​LinearLayout>​
 +</​file>​
 +
 +În situația în care conținutul redat de acest element nu este de tip text, va trebui utilizat un tip de adaptor definit de utilizator, care specifică modul în care vor fi afișate astfel de elemente. În cadrul acesteia, pentru fiecare obiect conținut trebuie să se specifice un mecanism de dispunere prin intermediul clasei ''​Gallery.LayoutParams''​ (specificându-se mai ales dimensiunile elementului care se dorește a fi afișat).
 +
 +<file java FacultyActivity.java>​
 +public class FacultyActivity extends Activity {
 +
 +  Integer[] facultyImages = {
 +    R.drawable.faculty01,​
 +    R.drawable.faculty02,​
 +    R.drawable.faculty03
 +    // add resources for other faculties
 +  };
 +
 +  class GalleryAdapter extends BaseAdapter {
 +    Context context;
 +    ​
 +    public ImageAdapter(Context context) {
 +      this.context = context;
 +    }
 +    ​
 +    public int getCount() {
 +      return facultyImages.length;​
 +    }
 +    ​
 +    public Object getItem(int position) {
 +      return position;
 +    }
 +    ​
 +    public long getItemId(int position) {
 +      return position;
 +    }
 +    ​
 +    public View getView(int position, View convertView,​ ViewGroup parent) {
 +      ImageView imageview;
 +      if (convertView == null) {
 +        imageview = new ImageView(context);​
 +        imageview.setImageResource(facultyImages [position]);​
 +        imageview.setScaleType(ImageView.ScaleType.FIT_XY);​
 +        imageview.setLayoutParams(new Gallery.LayoutParams(160,​ 120));
 +      } else {
 +        imageview = (ImageView)convertView;​
 +      }
 +      return imageview;
 +    }
 +  }
 +
 +  @Override
 +  protected void onCreate(Bundle savedInstanceState) {
 +    super.onCreate(savedInstanceState);​
 +    setContentView(R.layout.activity_faculty);​
 +    ​
 +    Gallery gallery = (Gallery)findViewById(R.id.gallery);​
 +    gallery.setAdapter(new GalleryAdapter(this));​
 +    gallery.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 +      @Override
 +      public void onItemClick(AdapterView<?>​ parent, View view, int position, long id) {
 +        ImageView imageview = (ImageView)(findViewById(R.id.imageview));​
 +        imageview.setImageResource(facultyImages [position]);​
 +      }
 +    });
 +  }
 +}
 +</​file>​
 +
 +{{ :​laboratoare:​laborator05:​sample_gallery.png?​nolink&​400 }}
 +
 +==== Tipuri de adaptoare pentru colecții de date ====
 +
 +În cazul unei colecții de date, rolul unui adaptor este de a gestiona modelul de date transpunându-l (adaptându-l) la fiecare element din cadrul interfeței grafice (listă sau tabel). Toate adaptoarele (predefinite sau definite de utilizator) sunt derivate din clasa ''​BaseAdapter''​.
 +
 +În funcție de proveniența datelor, se vor utiliza tipuri de adaptoare specializate:​
 +  * ''​ArrayAdapter<​T>''​ - pentru date reținute sub forma unui tablou (de tipul ''​T[]''​ sau ''​java.util.List<​T>''​)
 +  * ''​SimpleCursorAdapter''​ - pentru date preluate de la un furnizor de conținut (de exemplu, o bază de date)
 +Acestea au asociate anumite moduri de dispunere a conținutului,​ predefinite în ''​android.R.layout'',​ adaptate la tipul de control grafic utilizat.
 +De acele mai multe ori, pentru structuri de date complexe ce trebuie afișate folosind mecanisme de afișare particularizate pentru asigurarea unei anumite funcționalități,​ trebuie utilizat un tip de adaptor definit de utilizator, ce suprascrie metodele clasei ''​BaseAdapter''​.
 +
 +=== ArrayAdapter<​T>​ ===
 +
 +Clasa [[http://​developer.android.com/​reference/​android/​widget/​ArrayAdapter.html|ArrayAdapter]] este utilizată pentru gestiunea unui tablou sau a unei liste de obiecte Java. 
 +
 +Fiecare element din cadrul acesteia va corespunde unui element din cadrul controlului grafic, conținutul acestuia fiind generat de rezultatul metodei ''​toString()''​ a clasei din care fac parte obiectele componente a vectorului sau a listei respective. Identificatorul textului de tip ''​TextView''​ în cadrul căruia se va afișa conținutul referit de adaptor poate fi specificat în constructorul acestuia, în caz contrar folosindu-se un element predefinit (al cărui identificator este ''​android.R.id.text1''​).
 +
 +Instanțierea unui obiect de tip ''​ArrayAdapter<​T>''​ se face prin specificarea cel puțin a contextului în care acesta va fi afișat (activitate,​ fragment) și a identificatorului unei resurse de tip layout care va fi utilizată pentru dispunerea conținutului său (aceasta putând fi predefinită sau definită de utilizator). În plus, se pot preciza identificatorul unui control de tip ''​TextView''​ în cadrul căruia va fi plasat fiecare element al colecției, precum și obiectele propriu-zise,​ sub formă de tablou sau de listă.
 +
 +<code java>
 +ArrayAdapter(Context context, int resource);
 +ArrayAdapter(Context context, int resource, int textViewResourceId);​
 +ArrayAdapter(Context context, int resource, T[] objects);
 +ArrayAdapter(Context context, int resource, int textViewResourceId,​ T[] objects);
 +ArrayAdapter(Context context, int resource, List<​T>​ objects);
 +ArrayAdapter(Context context, int resource, int textViewResourceId,​ List<​T>​ objects);
 +</​code>​
 +
 +<note tip>În situația în care conținutul sursei de date se regăsește în cadrul unei resurse, instanțierea unui obiect de tip ''​ArrayAdapter<​CharSequence>''​ se face prin intermediul metodei statice ''​createFromResource(Context,​ int, int)''​ ce primește ca parametri identificatorul resursei care conține colecția de date (de tipul ''​R.array....''​) și identificatorul modului de dispunere al elementelor.</​note>​
 +
 +Conținutul colecției de date poate fi modificat prin intermediul obiectului de tip ''​ArrayAdapter<​T>''​ numai în situația în care tipul sursei de date suportă acest tip de operații (tablourile nu suportă modificarea prin intermediul adaptorului,​ în timp ce obiectele de tip listă parametrizată implementează această acțiune).
 +  - apelul metodelor puse la dispoziție de clasa ''​ArrayAdapter<​T>''​
 +    * ''​add(T)''​ - adaugă un obiect
 +    * ''​addAll(Collection<?​ extends T>​)''​ - adaugă o colecție de obiecte
 +    * ''​clear()''​ - șterge toate obiectele
 +    * ''​insert(T,​ int)''​ - adaugă un obiect la o anumită poziție
 +    * ''​remove(T)''​ - șterge un anumit obiect
 +  - modificarea elementelor din sursa de date cu notificarea obiectului adaptor asociat prin intermediul metodei ''​notifyDataSetChanged()'',​ astfel încât operațiile realizate asupra sursei de date să se propage și la nivelul controlului grafic care îi afișează conținutul.
 +
 +<note tip>​Metodele pentru modificarea colecției de date prin intermediul obiectului de tip adaptor (''​add'',​ ''​addAll'',​ ''​clear'',​ ''​insert'',​ ''​remove''​) apelează în mod automat ''​notifyDataSetChanged()''​. Acest comportament poate fi suprascris prin intermediul metodei ''​setNotifyOnChange(boolean)''​.</​note>​
 +
 +<note tip>​Ordonarea elementelor din sursa de date poate fi realizată prin obiectul de tip adapter, pe baza unui criteriu de comparare, prin intermediul metodei ''​sort(Comparator<?​ super T>​)''​.</​note>​
 +
 +Pentru o listă ce afișează coordonatele unor puncte pe ecran în formatul ''​(x,​ y)''​ (dat de implementarea metodei ''​toString()''​ a clasei ''​Point''​),​ gestiunea conținutului se poate face atât prin intermediul colecției de date (de tip ''​ArrayList<​Point>''​) cât și prin intermediul obiectului adator, de tip ''​ArrayAdapter<​Point>''​.
 +
 +<code java>
 +class Point {
 +    double x, y;
 +    public Point() { x = 0; y = 0; }
 +    public Point(double x, double y) { this.x = x; this.y = y; }
 +    public double getX() { return x; }
 +    public void setX(double x) { this.x = x; }
 +    public double getY() { return y; }
 +    public void setY(double y) { this.y = y; }
 +    public String toString() { return "​("​+x+",​ "​+y+"​)";​ }
 +}
 +</​code>​
 +
 +<columns 100%>
 +<code java>
 +ListView listview = (ListView)findViewById(R.id.listview);​
 +ArrayList<​Point>​ points = new ArrayList<​Point>​();​
 +ArrayAdapter<​Point>​ adapter = new ArrayAdapter<​Point>​(this,​ android.R.layout.simple_list_item_1,​ points);
 +listview.setAdapter(adapter);​
 +</​code>​
 +<columns 100% 50%>
 +<code java>
 +points.add(new Point());
 +Point point = new Point(1, 2);
 +points.insert(point,​ 0);
 +points.remove(point);​
 +adapter.notifyDataSetChanged();​
 +</​code>​
 +<​newcolumn>​
 +<code java>
 +adapter.add(new Point());
 +Point point = new Point(1, 2);
 +adapter.insert(point,​ 0);
 +adapter.remove(point);​
 +</​code>​
 +</​columns>​
 +
 +</​columns>​
 +
 +În cazul în care se dorește afișarea conținutului în cadrul unei intefețe grafice mai complexe decât un simplu ''​TextView'',​ se poate defini o clasă derivată din ''​ArrayAdapter<​T>''​ care va suprascrie metoda responsabilă cu element unui obiect al listei, cunoscându-se poziția sa în cadrul acesteia
 +<code java>
 +public View getView (int position, View convertView,​ ViewGroup parent);
 +</​code>​
 +unde
 +  * ''​position''​ este poziția elementului care este afișat în mod curent
 +  * ''​convertView''​ este un obiect de tip ''​View''​ (dintre cele create anterior, dar care nu mai este vizibil) ce poate fi reutilizat pentru construirea elementului curent
 +  * ''​parent''​ este un obiect de tip ''​ViewGroup''​ ce reprezintă container-ul în care va fi conținut elementul
 +
 +În cadrul acestei metode, se va construi un obiect de tip ''​View''​ (sau se va reutiliza parametrul ''​convertView'',​ pentru optimizarea memoriei folosite) având modul de dispunere dorit care va fi returnat ca rezultat al metodei.
 +
 +Astfel, dacă se dorește ca pentru fiecare punct să se afișeze o imagine corespunzătoare cadranului din care face parte, se va defini un tip de layout pentru fiecare element al listei:
 +
 +<file xml pointlayout.xml>​
 +<?xml version="​1.0"​ encoding="​UTF-8"?>​
 +<​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"​ >
 +  <​ImageView
 +    android:​id="​@+id/​imageview"​
 +    android:​layout_width="​wrap_content"​
 +    android:​layout_height="​wrap_content"​
 +    android:​padding="​2dp"​
 +    android:​contentDescription="​@string/​content_description"​ />
 +  <​TextView
 +    android:​id="​@+id/​textview"​
 +    android:​layout_width="​wrap_content"​
 +    android:​layout_height="​wrap_content"​ />
 +</​LinearLayout>​
 +</​file>​
 +
 +Implementarea clasei extinse din ''​ArrayAdapter<​Point>''​ va trebui să redefinească metoda ''​getView(int,​ View, ViewGroup)''​ care va întoarce un control grafic conținând un element al listei, format dintr-o imagine sugestivă pentru tipul de cadran (obiect de tip ''​ImageView''​) și un câmp text conținând coordonatele punctului (obiect de tip ''​TextView''​).
 +
 +<code java>
 +class PointArrayAdapter extends ArrayAdapter<​Point>​ {
 +  private Context context;
 +  private int resource;
 +  private ArrayList<​Point>​ content;
 +
 +  public PointArrayAdapter(Context context, int resource, ArrayList<​Point>​ content) {
 +    super(context,​ resource, content);
 +    this.context = context;
 +    this.resource = resource;
 +    this.content = content;
 +  }
 +
 +  @Override
 +  public View getView(int position, View convertView,​ ViewGroup parent) {
 +    View view;
 +    if (convertView == null) {
 +      LayoutInflater layoutinflator = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);​
 +      view = layoutinflator.inflate(resource,​ null);
 +    } else {
 +      view = convertView;​
 +    }
 +    ImageView imageview = (ImageView)view.findViewById(R.id.imageview);​
 +    TextView textview = (TextView)view.findViewById(R.id.textview);​
 +    Point point = content.get(position);​
 +    if (point.getX() >= 0 && point.getY() >= 0) {
 +      imageview.setImageResource(R.drawable.firstquadrant);​
 +    } else if (point.getX() < 0 && point.getY() >= 0) {
 +      imageview.setImageResource(R.drawable.secondquadrant);​
 +    } else if (point.getX() < 0 && point.getY() < 0) {
 +      imageview.setImageResource(R.drawable.thirdquadrant);​
 +    } else if (point.getX() >= 0 && point.getY() < 0) {
 +      imageview.setImageResource(R.drawable.forthquadrant);​
 +    }
 +    textview.setText(point.toString());​
 +    return view;
 +  }
 +}
 +</​code>​
 +
 +<note tip>De remarcat reutilizarea parametrului ''​convertView''​ în cazul în care acesta nu este ''​null''​. Conținutul său este cel al unui control grafic instanțiat anterior care nu mai este vizibil pe ecran. În felul acesta se asigură optimizarea memoriei folosite dar și a timpului de execuție, întrucât nu se mai creează obiecte de tip ''​View''​ în cazul în care acestea există deja.</​note>​
 +
 +În cadrul activității va fi definit un obiect de tip adaptor care va primi ca parametri contextul (fereastra),​ identificatorul modului de dispunere pentru componentele listei (''​R.layout.pointlayout''​) și conținutul acesteia.
 +
 +<code java>
 +setContentView(R.layout.activity_main);​
 +ListView listview = (ListView)findViewById(R.id.listview);​
 +ArrayList<​Point>​ content = new ArrayList<​Point>​();​
 +// add points to content
 +PointArrayAdapter adapter = new PointArrayAdapter(this,​ R.layout.pointlayout,​ content);
 +listview.setAdapter(adapter);​
 +</​code>​
 +
 +{{ :​laboratoare:​laborator05:​sample_base_addapter.png?​nolink&​400 }}
 +
 +=== SimpleCursorAdapter ===
 +
 +În situația în care se lucrează cu furnizori de conținut sau cu baza de date din cadrul sistemului de operare Android, este recomandat să se utilizeze un obiect de tipul [[http://​developer.android.com/​reference/​android/​widget/​SimpleCursorAdapter.html|SimpleCursorAdapter]],​ care asociază rezultatele obținute în urma interogării unor astfel de resurse la controale grafice de tip ''​TextView''​ sau ''​ImageView''​ existente în layout-uri predefinite sau definite de utilizator.
 +
 +Maparea între cele două tipuri se resurse este realizată în două etape:
 +  - dacă există un obiect de tip ''​SimpleCursorAdapter.ViewBinder''​ disponibil, se apelează metoda ''​setViewValue(View,​ Cursor, int)''​
 +    - dacă rezultatul întors este ''​true'',​ asocierea a fost realizată
 +    - dacă rezultatul întors este ''​false''​
 +      - se încearcă să se realizeze maparea cu un control de tip ''​TextView''​ se apelează metoda ''​setViewText(TextView,​ String)''​
 +      - se încearcă să se realizeze maparea cu un control de tip ''​ImageView''​ se apelează metoda ''​setViewImage(ImageView,​ String)''​
 +  - dacă nu se identifică nici un mod de asociere corespunzător,​ se generează o excepție de tipul ''​IllegalStateException''​
 +
 +Constructorul clasei ''​SimpleCursorAdapter''​
 +<code java>
 +public SimpleCursorAdapter (Context context, int layout, Cursor cursor, String[] from, int[] to, int flags);
 +</​code>​
 +primește următorii parametri:
 +  * ''​context''​ - activitatea / fragmentul în contextul căruia este afișat controlul grafic conținând rezultatul interogării conținut de cursor
 +  * ''​layout''​ - identificatorul către o resursă predefinită sau definită de utilizator care descrie modul în care sunt vizualizate elementele din cadrul cursorului
 +  * ''​cursor''​ - cursorul conținând rezultatul interogării din sursa de date
 +
 +<note warning>​Cursorul poate avea valoarea ''​null''​ dacă nu este încă disponibil.</​note>​
 +
 +  * ''​from''​ - denumirile coloanelor din cadrul cursorului (proiecția) care vor fi afișate în cadrul controlului grafic
 +  * ''​to''​ - tablou conținând identificatorii elementelor grafice (predefinite / definite de utilizator) în cadrul cărora vor fi afișate valorile extrase din interogare
 +
 +<note important>​Elementele tablourilor ''​from''​ și ''​to''​ identifică maparea între cursor și view, relația între componentele acestora fiind de 1:​1.</​note>​
 +
 +  * ''​flags''​ - stabilesc comportamentul elementului de tip adaptor, putând avea valorile
 +    * ''​FLAG_AUTO_REQUERY''​ - utilizarea sa este descurajată începând cu API 11, forțează cursorul să realizeze interogarea din nou atunci când se primește o notificare cu privire la modificarea conținutului sursei de date; întrucât operația se realizează în contextul interfeței grafice, poate determina o diminuare a responsivității aplicației
 +    * ''​FLAG_REGISTER_CONTENT_OBSERVER''​ - determină asocierea unui obiect pentru monitorizarea conținutului sursei de date.
 +
 +De exemplu, dacă se dorește afișarea unor informații din calendarul asociat contului cu care a fost înregistrat dispozitivul mobil, va fi interogată resursa respectivă (specificându-se URI-ul prin care poate fi accesată, lista cu denumirile coloanelor ce trebuie obținute, condiția de selecție a rezultelor și parametrii acesteia (în cazul unei interogări parametrizate) precum și criteriile de sortare a informațiilor). Rezultatul va fi plasat într-un obiect de tip ''​Cursor''​. Se instanțiază apoi un obiect de tip ''​SimpleCursorAdapter''​ ce primește ca parametrii contextul curent (activitatea),​ modalitatea de dispunere a informațiilor (predefinită,​ listă având conținutul afișat pe două linii), cursorul ce conține rezultatul interogarii (obținut anterior), precum și listele cu denumirile coloanelor, respectiv identificatorii controalelor grafice în care va fi plasat conținutul acestora.
 +
 +<note important>​Accesarea calendarului este posibilă numai prin specificarea permisiunilor necesare în fișierul ''​AndroidManifest.xml''​\\
 +''<​uses-permission android:​name="​android.permission.READ_CALENDAR"​ />''​
 +</​note>​
 +
 +<file java CalendarActivity.java>​
 +public class CalendarActivity extends Activity {
 +  @Override
 +  protected void onCreate(Bundle savedInstanceState) {
 +    super.onCreate(savedInstanceState);​
 +    setContentView(R.layout.activity_main);​
 +    Cursor cursor = getContentResolver().query(
 +        Calendars.CONTENT_URI, ​
 +        new String[]{Calendars._ID,​ Calendars.CALENDAR_DISPLAY_NAME,​ Calendars.CALENDAR_TIME_ZONE}, ​
 +        null, 
 +        null,
 +        null);
 +    SimpleCursorAdapter adapter = new SimpleCursorAdapter(
 +        this,
 +        android.R.layout.two_line_list_item,​
 +        cursor,
 +        new String[]{Calendars.CALENDAR_DISPLAY_NAME,​ Calendars.CALENDAR_TIME_ZONE},​
 +        new int[] {android.R.id.text1,​ android.R.id.text2}, ​
 + 0);
 +    ListView listview = (ListView)findViewById(R.id.listview);​
 +    listview.setAdapter(adapter);​
 +  }
 +}
 +</​file>​
 +
 +=== SimpleAdapter ===
 +
 +Obiectul de tip [[http://​developer.android.com/​reference/​android/​widget/​SimpleAdapter.html|SimpleAdapter]] este utilizat atunci când se realizează asocieri între date statice și elemente ale interfeței grafice, definite în cadrul unor moduri de dispunere (predefinite sau definite de utilizator). Datele ce vor fi afișate au structura unei liste (''​List''​) al cărei conținut constă în mapări (''​Map''​) de tip ''​(atribut,​ valoare)''​. Arhitectura corespunde unui tablou de entități caracterizate prin anumiți parametri. Fiecare astfel de obiect va fi afișat în cadrul unui element din cadrul controlului cu care va interacționa utilizatorul.
 +
 +Mecanismul de asociere între sursa de date și interfața grafică se realizează de asemenea în două etape, folosind clasa internă ''​SimpleAdapter.ViewBinder'',​ astfel:
 +  * printr-un apel al metodei generice ''​setViewValue(View,​ Object, String)''​
 +  * prin încercarea de mapare a conținutului cu un View de tip ''​Checkbox'',​ ''​TextView'',​ respectiv ''​ImageView''​
 +
 +Constructorul clasei ''​SimpleAdapter''​ se evidențiază prin structura sursei de date care va fi afișată, având forma ''​List<?​ extends Map<​String,​ ?​%%>>​%%''​ (este obligatoriu ca tipul cheii din asociere să fie șir de caractere).
 +<code java>
 +public SimpleAdapter (Context context, List<? extends Map<​String,​ ?>> data, int resource, String[] from, int[] to);
 +</​code>​
 +
 +<note important>​Valorile specificate pentru proiecție (câmpul ''​from''​) trebuie să corespundă cheilor din asocierea conținută în listă (obiectul de tip ''​Map<​String,​ ?>''​).</​note>​
 +
 +De exemplu, pentru entitatea ''​StaffMember''​ cu atributele ''​name''​ și ''​position''​ se dorește afișarea elementelor de acest tip (care se pot regăsi local, în ''/​assets/​staffmember.xml''​ sau la distanță, pe un server) în cadrul unei liste, se poate folosi un obiect de tip ''​SimpleAdapter''​. Utilitarul care va parsa fișierul XML va întoarce un obiect ''​ArrayList<​HashMap<​String,​ String%%>>​%%'',​ unde fiecare element este o proprietate a entității (nume sau poziție). Cu un astfel de rezultat se poate construi un obiect de tip ''​SimpleAdapter'',​ specificându-se totodată un identificator al tipului de layout, atributele care vor fi afișate precum și controalele cărora le vor fi repartizate.
 +
 +<file java StaffMemberActivity.java>​
 +public class StaffMemberActivity extends Activity {
 +
 +  @Override
 +  protected void onCreate(Bundle savedInstanceState) {
 +    super.onCreate(savedInstanceState);​
 +    setContentView(R.layout.activity_staff_member);​
 +    ​
 +    try {
 +      InputStream inputstream = getAssets().open("​staffmember.xml"​);​
 +      StaffXmlParser staffxmlparser = new StaffXmlParser();​
 +      List<​Map<​String,?>>​ staffmembers = staffxmlparser.parse(inputstream);​
 +      SimpleAdapter adapter = new SimpleAdapter(
 +          this,
 +          staffmembers,​
 +          android.R.layout.simple_list_item_2,​
 +          new String[] {"​name",​ "​position"​},​
 +          new int[] {android.R.id.text1,​ android.R.id.text2});​
 +      ListView listview = (ListView)findViewById(R.id.listview)
 +      listview.setAdapter(adapter);​
 +    } catch (Exception exception) {
 +      Log.println(Log.ERROR,​ "​exception",​ exception.getMessage());​
 +    }
 +  }
 +}
 +</​file>​
 +
 +=== Obiecte de tip adaptor definite de utilizator ===
 +
 +Există situații în care funcționalitatea pusă la dispoziție de obiectele standard de tip adaptor nu este adecvată cerințelor unei aplicații, atât din punctul de vedere al gestiunii datelor cât și al interfeței grafice în contextul căreia trebuie să fie afișate informațiile.
 +
 +În acest scop, este pusă la dispoziția programatorilor clasa abstractă [[http://​developer.android.com/​reference/​android/​widget/​BaseAdapter.html|BaseAdapter]] care poate fi extinsă cu operațiile impuse de aplicație. Definirea unui astfel de obiect de tip adaptor implică de fapt suprascrierea unor metode, definite de interfețele ''​ListAdapter''​ și ''​SpinnerAdapter'':​
 +
 +<code java>
 +public int getCount();
 +public Object getItem(int position);
 +public long getItemId(int position);
 +public View getView(int position, View convertView,​ ViewGroup parent);
 +</​code>​
 +
 +  * metoda ''​getCount()''​ furnizează numărul de elemente din cadrul listei;
 +  * metoda ''​getItem()''​ întoarce elementul care se află pe o anumită poziție în cadrul listei;
 +  * metoda ''​getItemId()''​ returnează identificatorul elementului de pe o anumită poziție în cadrul listei;
 +  * metoda ''​getView()''​ construiește un control grafic corespunzător elementului de pe o anumită poziție în cadrul listei; acesta poate fi o instanță nouă, existând totodată posibilitatea de a se reutiliza parametrul ''​convertView''​ (care reprezintă o componentă a listei creată anterior, ce nu mai este vizibilă, putând fi astfel reciclată);​ obiectul ''​parent''​ indică elementul de tip container în care este afișat conținutul sursei de date.
 +
 +<note important>​Clasa care implementează tipul de adaptor definit de utilizator trebuie să conțină (intern) și modelul de date, operând asupra acestuia.</​note> ​
 +
 +În plus, în cazul în care elementele listei conțin moduri de dispunere diferite (în funcție de poziția pe care se găsesc, spre exemplu - rândurile impare și rândurile pare), vor fi suprascrise încă alte două metode:
 +
 +<code java>
 +public int getViewTypeCount();​
 +public Object getItemViewType(int position);
 +</​code>​
 +
 +  * metoda ''​getViewTypeCount()''​ va întoarce numărul tipurilor de layout-uri care vor fi afișate în cadrul listei;
 +  * metoda ''​getItemViewType()''​ returnează - pentru elementul aflat pe o anumită poziție - mecanismul de dispunere care va fi utilizat.
 +
 +Spre exemplu, pentru gestiunea unei liste formată dintr-un ''​ImageView''​ și două ''​TextView'',​ dispuse diferit în funcție de rândul pe care se găsesc, vor trebui definite două layout-uri (în ''/​res/​layout''​) - denumite ''​odd_layout.xml''​ și ''​even_layout.xml''​ ce vor fi încărcate de metoda ''​getView()''​ în funcție de poziția respectivă:​
 +
 +{{ :​laboratoare:​laborator05:​listview_definit_de_utilizator.png?​nolink }}
 +
 +<code java>
 +public class CustomAdapter extends BaseAdapter {
 +
 +  Activity context;
 +  List<​CustomObject>​ data;
 +
 +  // ...
 +
 +  @Override
 +  public View getView(int position, View convertView,​ ViewGroup parent) {
 +    View customView;
 +    CustomObject customObject = data.get(position);​
 +    LayoutInflater layoutInflater = (LayoutInflater)context.getLayoutInflater();​
 +    if (position % 2 == 0) {
 +       ​customView = layoutInflater.inflate(R.layout.odd_layout,​ parent, false);
 +    } else {
 +       ​customView = layoutInflater.inflate(R.layout.even_layout,​ parent, false);
 +    }
 +    ImageView imageView = (ImageView)customView.findViewById(R.id.imageView);​
 +    imageView.setImageResource(customObject.getImageId());​
 +    TextView textView1 = (TextView)customView.findViewById(R.id.textView1);​
 +    textView1.setText(customObject.getText1());​
 +    TextView textView2 = (TextView)customView.findViewById(R.id.textView2);​
 +    textView2.setText(customObject.getText2());​
 +    return customView;
 +  }
 +}
 +</​code>​ Clasa adaptor definită de utilizator va trebui să rețină referințe către context (activitatea în cadrul căreia este afișată lista) și către sursa de date.
 +
 +Metoda ''​getView()''​ construiește un element de pe o anumită poziție din cadrul listei pe care îl va întoarce apoi ca rezultat.
 +
 +Inițial, trebuie să se obțină conținutul intrării din listă ce trebuie afișată (considerându-se o corespondență 1:1 între conținutul sursei de date și elementele listei) și să se instanțieze obiectul responsabil cu expandarea layout-ului. Ulterior se obține obiectul de tip ''​View''​ pe baza mecanismului de dispunere (corespunzător poziției curente) specificat în fișierul XML. Acesta va furniza referințe către controalele din cadrul său (prin intermediul identificatorilor unici) pentru care se va putea specifica conținutul,​ preluat din obiectul curent al sursei de date.
 +
 +== Tehnici pentru optimizarea performanțelor ==
 +
 +Implementarea unor tehnici pentru optimizarea performanțelor este foarte importantă atunci când se dezvoltă aplicații pentru dispozitive mobile. Având în vedere resursele limitate (în special în privința memoriei și a capacității de procesare) de care dispun acestea, un program care nu le gestionează în mod eficient poate determina o depreciere a timpului de răspuns.
 +
 +Fiecare element din cadrul unei liste este un ''​View''​ obținut prin expandarea layout-ului,​ atât construirea obiectului pentru gestiunea modurilor de dispunere (metoda ''​getLayoutInflater()''​) cât și operația de instanțiere a unei intrări din listă pe baza specificației XML (metoda ''​inflate()''​) ​ fiind operații extrem de costisitoare. De asemenea, obținerea unei referințe către un control din cadrul interfeței grafice pe baza identificatorului său (metoda ''​findViewById()''​) poate avea afecta performanțele aplicației.
 +
 +Printre cele mai utilizate tehnici pentru optimizarea performanțelor se numără:
 +
 +  * **instanțierea obiectului responsabil cu expandarea mecanismului de dispunere a unui element din cadrul listei pe constructorul clasei adaptor** <code java>
 +public CustomAdapter(Activity context, List<​CustomObject>​ data) {
 +  this.data = data;
 +  layoutInflater = (LayoutInflater)context.getLayoutInflater();​
 +}
 +</​code>​ În acest fel, se evită apelul acestei metode - costisitoare din punctul de vedere al utilizării resurselor - de fiecare dată când trebuie creat un element al listei.
 +  * **reutilizarea elementelor din cadrul listei care nu mai sunt vizibile** \\ De regulă, o listă conține mai multe elemente decât pot fi reprezentate pe suprafața de afișare a dispozitivului mobil, parcurgerea lor implicând folosirea unui mecanism de derulare. Obiectele corespunzătoare unor intrări care nu sunt vizibile la un moment dat pot fi reutilizate,​ ele fiind transmise în metoda ''​getView()''​ prin intermediul parametrului ''​convertView'',​ prevenindu-se astfel operația de expandare a layout-ului.\\ <code java>
 +@Override
 +public View getView(int position, View convertView,​ ViewGroup parent) {
 +  View customView;
 +  CustomObject customObject = data.get(position);​
 +  if (convertView == null) {
 +    if (position % 2 == 0) {
 +      customView = layoutInflater.inflate(R.layout.odd_layout,​ parent, false);
 +    } else {
 +      customView = layoutInflater.inflate(R.layout.even_layout,​ parent, false);
 +    }
 +  }
 +  else {
 +    customView = convertView;​
 +  }
 +  // ...
 +  return customView;
 +}
 +</​code>​ Prin această metodă se evită apelarea metodei ''​inflate()''​ de fiecare dată când trebuie să se afișeze un element al listei. ​
 +<note important>​Nu întotdeauna este posibilă reutilizarea obiectelor ce reprezintă elemente ale listei, situație în care parametrul ''​convertView''​ are valoarea ''​null''​. De aceea, în metoda ''​getView()''​ trebuie verificată întotdeauna valoarea acestui parametru.</​note> ​
 +<note warning> În cazul în care există mai multe posibilități de dispunere în cadrul listei, există riscul ca tipul de element care va fi reutilizat să nu fie cel necesar. Pentru a se asigura evitarea unei astfel de situații, este necesară suprascrierea metodelor ''​getViewTypeCount()'',​ respectiv ''​getItemViewType()''​.
 +<code java>
 +public final static int LIST_VIEW_TYPES ​    ​= 2;
 +public final static int LIST_VIEW_TYPE_ODD ​ = 0;
 +public final static int LIST_VIEW_TYPE_EVEN = 1;
 + 
 +public ​int getViewTypeCount() {
 +  return LIST_VIEW_TYPES; ​
 +}
 +public Object getItemViewType(int position) {
 +  if (position % 2 == 0)    
 +    ​return LIST_VIEW_TYPE_ODD;​
 +  return ​LIST_VIEW_TYPE_EVEN;​
 +}
 +</​code>​
 +</​note> ​
 +  * **reținerea unei referințe către structura layout-ului prin intermediul unor etichete**\\ În acest scop se utilizează un șablon denumit ''​ViewHolder''​ prin intermediul căruia pot fi accesate controalele grafice din cadrul unui element al listei, fără a fi necesară utilizarea identificatorului din cadrul documentului XML.\\ \\ ''​ViewHolder''​ este o clasă internă din cadrul adaptorului,​ definind exact structura pe care o are layout-ul unei intrări din cadrul listei. În momentul în care un obiect corespunzător unui element al listei este creat, în atributele clasei ''​ViewHolder''​ se rețin referințe către controalele grafice, acestea fiind atașate, ca etichetă, prin intermediul metodei ''​setTag()''​. Ulterior, ele vor fi preluate (prin metoda ''​getTag()''​),​ utilizându-se referințele către elementele componente în loc să se apeleze metoda ''​findViewById()''​ de fiecare data, rezultatul fiind o apreciere a performanțelor cu circa 15%.\\ <code java>
 +public class CustomAdapter extends BaseAdapter {
 +
 +  public static class ViewHolder {
 +    ImageView imageView;
 +    TextView textView1, textView2;
 +  };
 +
 +  // ...
 +
 +  @Override
 +  public View getView(int position, View convertView,​ ViewGroup parent) {
 +    View customView;
 +    ViewHolder viewHolder;
 +    CustomObject customObject = (CustomObject)data.get(position);​
 +    if (convertView == null) {
 +      if (position % 2 == 0) {
 +        customView = layoutInflater.inflate(R.layout.odd_layout,​ parent, false);
 +      } else {
 +        customView = layoutInflater.inflate(R.layout.even_layout,​ parent, false);
 +      }
 +      viewHolder = new ViewHolder();​
 +      viewHolder.imageView = (ImageView)customView.findViewById(R.id.imageView);​
 +      viewHolder.textView1 = (TextView)customView.findViewById(R.id.textView1);​
 +      viewHolder.textView2 = (TextView)customView.findViewById(R.id.textView2);​
 +      customView.setTag(viewHolder);​
 +    } else {
 +      customView = convertView;​
 +    }
 +    viewHolder = (ViewHolder)customView.getTag();​
 +    viewHolder.imageView.setImageResource(customObject.getImageeId());​
 +    viewHolder.textView1.setText(customObject.getText1());​
 +    viewHolder.textView2.setText(customObject.getText2());​
 +    ​
 +    return customView;
 +  }
 +}
 +</​code>​
 +
 +==== Componente specializate pentru gestiunea listelor ====
 +
 +Întrucât de regulă în cadrul unei interfețe grafice nu există decât o singură listă (care ocupă tot spațiul disponibil și se găsește centrată pe suprafața de afișare), în Android au fost definite componente speciale pentru gestiunea acesteia: ''​ListActivity'',​ respectiv ''​ListFragment''​. Nu este necesar să se utilizeze un layout în această situație, lista fiind declarată în mod implicit.
 +
 +<note warning>​În cazul în care se foloșește un layout definit de utilizator, este absolut necesar ca lista să fie declarată explicit, având identificatorul **''​@android:​id/​list''​**,​ în caz contrar generându-se o excepție.
 +<code xml>
 +<​ListView
 +  android:​id="​@android:​id/​list"​
 +  android:​layout_width="​match_parent"​
 +  android:​layout_height="​wrap_content"​ />
 +</​code>​
 +</​note>​
 +
 +<note tip>În layout-urile definite de utilizator, se mai poate folosi un control grafic (de orice tip), având identificatorul ''​@android:​id/​empty''​ care va fi afișat pe ecran numai în cazul în care lista este vidă (aceasta fiind ascunsă în acest caz).</​note>​
 +
 +Clasele ''​ListActivity''​ și ''​ListFragment''​ funcționează doar cu obiecte de tip ''​ListView''​. În cazul în care se dorește folosirea altor tipuri particulare de liste, utilizarea acestora nu va fi posibilă.
 +
 +=== ListActivity ===
 +
 +Clasa [[http://​developer.android.com/​reference/​android/​app/​ListActivity.html|ListActivity]] definește o activitate ce încapsulează un obiect de tip ''​ListView'',​ conectat la o sursă de date (cursor sau tablou/​listă),​ expunând către programatori metode de tratare a evenimentului de selectare a unui element.
 +
 +Gestiunea adaptorului pentru lista implicită din ''​ListActivity''​ se face prin intermediul metodelor:
 +  * ''​ListAdapter getListAdapter()''​ - întoarce obiectul de tip adaptor asociat listei
 +  * ''​void setListAdapter(ListAdapter adapter)''​ - asociază listei un obiect de tip adaptor ​
 +Referința către lista implicită poate fi obținută prin intermediul metodei ''​ListView getListView()''​.
 +
 +Metoda ce va fi apelată automat în momentul în care utilizatorul selectează un element din cadrul listei este:
 +<code java>
 +protected void onListItemClick(ListView l, View v, int position, long id);
 +</​code>​
 +parametrii având următoarea semnificație:​
 +  * ''​l''​ - lista pe care s-a produs evenimentul
 +  * ''​v''​ - elementul din cadrul listei care a fost apăsat ​
 +  * ''​position''​ - poziția elementului care a fost apăsat în cadrul listei
 +  * ''​id''​ - identificatorul elementului care a fost apăsat în cadrul listei (întors de metoda ''​getItemId()''​ a clasei adaptor)
 +
 +Clasa ''​ListActivity''​ nu definește însă și o metodă pentru tratarea apăsărilor de lungă durată (//eng.// long-click),​ în acest sens fiind necesară implementarea unei clase ascultător ce implementează interfața ''​AdapterView.OnItemLongClickListener'',​ asociată obiectului de tip ''​ListView''​ din cadrul ferestrei.
 +<code java>
 +public boolean onItemLongClick(AdapterView<?>​ parent, View view, int position, long id);
 +</​code>​
 +Semnificația parametrilor este aceeași ca în cazul metodei precedente. Rezultatul întors este ''​true''​ în cazul în care evenimentul de tip apăsare lungă a unui eveniment a fost consumat și ''​false''​ altfel.
 +
 +=== ListFragment ===
 +
 +Clasa [[http://​developer.android.com/​reference/​android/​app/​ListFragment.html|ListFragment]] definește un fragment ce încapsulează un obiect de tip ''​ListView'',​ conectat la o sursă de date (cursor sau tablou/​listă),​ expunând către programatori metode de tratare a evenimentului de selectare a unui element.
 +
 +Metodele pe care le pune la dispoziție ''​ListFragment''​ sunt similare cu cele oferite de ''​ListActivity''​.
 +
 +<note important>​În cazul în care se dorește gestiunea unei aplicații cu mai multe fragmente utilizând liste, acestea vor fi incluse într-o fereastră ce extinde ''​FragmentActivity''​ și nu ''​ListActivity'',​ în caz contrar evenimentele de tip apăsare a unui element fiind consumate de clasa părinte.
 +</​note>​
 +
 +Pentru utilizarea de layout-uri definite de utilizatori,​ lista implicită trebuie referită prin ''​android:​id/​list'',​ mecanismul de dispunere fiind încărcat pe metoda ''​onCreateView()''​.
 +
 +<code java>
 +@Override
 +public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
 +  View view = inflater.inflate(R.layout.fragment_custom,​ container, false);
 +  return view;
 +}
 +</​code>​
 +
 +<note warning>​Accesarea controalelor grafice din cadrul fragmentului nu poate fi realizată decât în metoda ''​onActivityCreated()'',​ când există siguranța că activitatea care îl conține a fost creată. Specificarea unui tip de adaptor definit de utilizator se va face tot în contextul acestei metode.
 +<code java>
 +@Override
 +public void onActivityCreated(Bundle state) {
 +  super.onActivityCreated(state);​
 +  CustomAdapter customAdapter = new CustomAdapter(getActivity(),​ data);
 +  setListAdapter(customAdapter);​
 +}
 +</​code>​
 +Se observă faptul că referința către activitatea care conține fragmentul se face prin metoda ''​getActivity()''​. Numai după ce activitatea a fost creată se garantează că rezultatul acestei metode este relevant.
 +</​note>​
 +
 +Specificarea unui fragment în cadrul unei activități poate fi realizată:
 +  - **static**, dacă fragmentul respectiv nu va fi înlocuit în cadrul interfeței grafice (sau în situația în care este înlocuit, el este păstrat pe stivă prin apelul metodei ''​addToBackStack()'',​ pentru a se putea reveni la el)\\ <code xml>
 +<​RelativeLayout 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"​
 +  tools:​context="​.MainActivity"​ >
 +
 +  <​fragment
 +    android:​id="​@+id/​fragment1"​
 +    android:​name="​ro.pub.cs.systems.pdsd.lab05.Fragment1"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​wrap_content"​ />
 +
 +</​RelativeLayout>​
 +</​code>​ Un astfel de fragment nu poate fi niciodată distrus, dar poate fi înlocuit cu un alt conținut, plasându-se conținutul său pe stivă:\\ <code java>
 +Fragment2 fragment2 = new Fragment2();​
 +FragmentManager fragmentManager = getFragmentManager();​
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​
 +fragmentTransaction.replace(android.R.id.content,​ fragment2);
 +fragmentTransaction.addToBackStack(null);​
 +fragmentTransaction.hide(this);​
 +fragmentTransaction.commit();​
 +</​code>​ De remarcat faptul că pentru fragment au fost specificate următoarele proprietăți:​
 +    * ''​android:​id''​ prin intermediul căruia fragmentul poate fi identificat în mod unic (alternativ se poate specifica ''​android:​tag''​)
 +    * ''​android:​name''​ reprezentând clasa corespunzătoare fragmentului,​ care va fi instanțiată în momentul în care este expandat layoutul corespunzător activității (în locul elementului ''<​fragment>''​ va fi plasat rezultatul metodei ''​onCreateView()''​)\\ ​
 +  - **dinamic**,​ în situația în care conținutul fragmentului va fi diferit, în funcție de anumite operații realizate de utilizator\\ <code xml>
 +<​RelativeLayout 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"​
 +  tools:​context="​.CartoonActivity"​ >
 +  ​
 +  <​FrameLayout ​
 +    android:​id="​@+id/​fragment1"​
 +    android:​layout_width="​0dp"​
 +    android:​layout_height="​match_parent"​
 +    android:​layout_weight="​1"​ />
 +
 +</​RelativeLayout>​
 +</​code>​ În această situație se utilizează un mecanism de dispunere de tip ''​FrameLayout''​ care permite afișarea unui singur conținut la un moment dat. În cazul în care în cadrul acestui container se găsea anterior un alt fragment, acesta este distrus (spre diferență de cazul anterior).\\ <code java>
 +Fragment2 fragment2 = new Fragment2();​
 +FragmentManager fragmentManager = getFragmentManager();​
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​
 +fragmentTransaction.replace(R.id.fragment1,​ fragment2);
 +fragmentTransaction.commit();​
 +</​code>​
 +
 +===== Meniuri =====
 +
 +În Android, meniurile sunt implementate folosind clasa [[http://​developer.android.com/​reference/​android/​view/​Menu.html|android.view.Menu]],​ fiecare activitate având asociat un astfel de obiect.
 +
 +{{ :​laboratoare:​laborator05:​arhitectura_de_nivel_inalt_meniuri.png?​nolink }}
 +
 +Așa cum se poate observa, un meniu poate conține: ​
 +  * 0 sau mai multe submeniuri (reprezentate de clasa ''​android.view.SubMenu''​)
 +  * 0 sau mai multe intrări propriu-zise (reprezentate de clasa ''​android.view.MenuItem''​),​ caracterizate prin următoarele atribute:
 +    * **denumire** reprezintă valoarea care este afișată în cadrul interfeței grafice;
 +    * **identificator** generat de obicei în mod automat pentru meniurile declarate prin intermediul fișierelor XML;
 +    * **identificator al grupului de meniuri** prin intermediul căruia mai multe opțiuni din cadrul meniului pot fi grupate împreună;
 +    * **număr de ordine** specifică importanța intrării, determinând o sortare a acestora, fiind folosit pentru a se indica ordinea de afișare a meniurilor sau pentru a preciza faptul că întregul grup aparține unei categorii (identificatorul grupului de meniuri fiind același cu numărul de ordine); unele intervale sunt rezervate pentru anumite categorii de meniuri
 +
 +^  **CATEGORIE** ​ ^  **TIP** ​ ^  **INTERVAL** ​ ^  **DESCRIERE** ​ ^
 +| container | ''​Menu.CATEGORY_CONTAINER''​ | ''​0x10000''​ | intrări de meniu legate de modul de dispunere al interfeței grafice |
 +| sistem | ''​Menu.CATEGORY_SYSTEM''​ | ''​0x20000''​ | intrări de meniu create de sistemul de operare Android, descriind funcționalități valabile pentru toate activitățile |
 +| secundare | ''​Menu.CATEGORY_SECONDARY''​ | ''​0x30000''​ | intrări de meniu de o importanță mai redusă decât celelalte (mai puțin utilizate) |
 +| alternative | ''​Menu.CATEGORY_ALTERNATIVE''​ | ''​0x40000''​ | intrări de meniu create de aplicații externe, oferind modalități alternative de a gestiona datele |
 +
 +<​note>​Astfel de valori pot fi însă utilizate pentru identificatorul intrării sau pentru identificatorul grupului de meniuri, neexistând nici un fel de restricții în acest sens.</​note>​
 +
 +Un meniu ce conține operațiile relevante pentru o anumită activitate se numește //meniu de opțiuni//, acesta fiind accesibil:
 +  * pentru aplicațiile destinate platformelor **anterioare Android 2.3.x (API 10) - inclusiv**, la apăsarea tastei ''​Menu'',​ afișându-se o porţiune a meniului ce cuprinde șase poziții disponibile;​ în situația în care meniul respectiv este format din mai multe elemente, va exista și o opțiune ''​More''​ prin care pot fi accesate şi celelalte opţiuni
 +  * pentru aplicaţiile destinate platformelor **ulterioare Android 3.0 (API 11) - inclusiv**, prin intermediul barei de acțiuni în care sunt plasate pictogramele corespunzătoare acțiunilor,​ în funcție de prioritățile specificate;​ dacă nu pot fi afișate toate opțiunile, va exista o pictogramă pentru accesarea celorlalte opțiuni, acestea putând fi accesate și prin apăsarea tastei ''​Menu'',​ în cazul în care acesta există
 +
 +El este instanțiat în momentul apelării metodei:
 +
 +<code java>
 +@Override
 +public boolean onCreateOptionsMenu(Menu menu) {
 +  // populate menu, via XML resource file or programatically
 +  return true;
 +}
 +</​code>​
 +
 +Pentru sistemele Android anterioare versiunii 2.3.x, aceasta este apelată în mod automat atunci când utilizatorul accesează meniul, iar pentru sistemele Android anterioare versiunii 3.0 în momentul instanțierii activității,​ de vreme ce opțiunile meniului trebuie să fie accesibile și prin intermediul barei de acțiuni.
 +
 +<note important>​Este foarte important ca rezultatul metodei ''​onCreateOptionsMenu()''​ să fie ''​true'',​ pentru a face meniul vizibil. În situația în care metoda întoarce ''​false'',​ meniul va fi invizibil. </​note>​
 +
 +Mai multe informații cu privire la meniuri pot fi consultate [[http://​developer.android.com/​guide/​topics/​ui/​menus.html|aici]].
 +
 +==== Mecanisme de gestiune a meniurilor ====
 +
 +Un meniu poate fi definit:
 +
 +**1.** **prin intermediul unui fișier de resurse XML** plasat în ''​res/​menu''​ - variantă preferată datorită faptului că:
 +  * meniul poate fi identificat prin intermediul unei denumiri (generată în mod automat în clasa ''​R.menu''​);​
 +  * intrările propriu-zise din cadrul meniului vor fi ordonate în mod automat, generarea identificatorilor corespunzători acestora realizându-se de asemenea automat;
 +  * textele conținute vor fi localizate automat (prin referințele la fișierele ce conțin șiruri de caractere);
 +  * structura poate fi vizualizată mai clar, separându-se partea de prezentare (conținutului meniului) de logica aplicației (mecanismul de răspuns la acțiunile utilizatorului vis-a-vis de intrările ale meniului);
 +  * permite definirea de configurații diferite pentru diverse platforme (dimensiuni ale suprafeței de afișare).
 +
 +<file xml /​res/​menu/​main.xml>​
 +<menu xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​ >
 +  <item
 +    android:​id="​@+id/​operations"​
 +    android:​title="​@string/​operations"​ >
 +    <​menu>​
 +      <item
 +        android:​id="​@+id/​create"​
 + android:​icon="​@drawable/​create"​
 +        android:​orderInCategory="​100"​
 + android:​showAsAction="​ifRoom"​
 + android:​title="​@string/​create"​ />
 +      <!-- add other submenu entries -->​  ​   ​
 +    </​menu>​
 +  </​item>​
 +  <group
 +    android:​id="​@+id/​generic_actions"​
 +    android:​visible="​true"​
 +    android:​enabled="​true"​
 +    android:​checkableBehavior="​none"​ >
 +    <item
 +      android:​id="​@+id/​settings"​
 +      android:​icon="​@drawable/​settings"​
 +      android:​orderInCategory="​100"​
 +      android:​showAsAction="​ifRoom"​
 +      android:​title="​@string/​settings"​ />
 +    <!-- add other menu entries -->
 +  </​group>​
 +  <item
 +    android:​id="​@+id/​help"​
 +    android:​icon="​@drawable/​help"​
 +    android:​orderInCategory="​100"​
 +    android:​showAsAction="​ifRoom"​
 +    android:​title="​@string/​help"​ />
 +  <!-- add other menu entries -->
 +</​menu>​
 +</​file>​
 +Așa cum se observă, elementul rădăcină este un obiect de tip ''<​menu>'',​ definește un meniu (de tip ''​android.view.Menu''​). Acesta poate conține elemente de tip:
 +
 +**a.** ''<​item>''​ care creează
 +  * un submeniu (de tip ''​android.view.Submenu''​) în cazul în care are imbricat un alt element de tip ''<​menu>'';​
 +  * o intrare propriu-zisă a meniului (de tip ''​android.view.MenuItem''​),​ dacă nu conține alte elemente;
 +Atributele elementului ''<​item>''​ sunt:
 +  * ''​android:​id''​ - un identificator unic pentru resursă care va fi utilizat ca referință pentru elementul respectiv
 +  * ''​android:​icon''​ - o resursă de tip ''​drawable''​ folosită pe post de pictogramă,​ atunci când elementul este inclus în cadrul barei de acțiuni
 +  * ''​android:​orderInCategory''​ - numărul de ordine ce definește importanța intrării meniului în cadrul grupului
 +  * ''​android:​showAsAction''​ - precizează criteriile în funcție de care elementul respectiv va fi inclus în cadrul barei de acțiuni, putând avea valorile:
 +    * ''​ifRoom''​ - va fi plasat în bara de acțiuni numai dacă este spațiu disponibil
 +    * ''​withText''​ - în bara de acțiuni se va afișa atât pictograma intrării cât și denumirea intrării din cadrul meniului
 +    * ''​never''​ - nu va fi plasat niciodată în bara de acțiuni
 +    * ''​collapseActionView''​ - elementul grafic asociat intrării din cadrul meniului poate fi rabatat
 +    * ''​always''​ - va fi plasat întotdeauna în bara de acțiuni
 +<note important>​În situația în care există mai multe intrări ale meniului care definesc această valoare decât spațiul de afișare pus la dispoziție de bara de acțiuni, elementele respective se vor suprapune. Această opțiune nu trebuie utilizată decât în cazul în care intrarea meniului care o definește este critică în cadrul aplicației.</​note>​
 +  * ''​android:​title''​ - denumirea elementului,​ așa cum este afișată către utilizator (dacă valoarea este un șir de caractere prea lung, poate fi folosită valoarea indicată de proprietatea ''​android:​titleCondensed''​)
 +  * ''​android:​alphabeticShortcut'',​ ''​android:​numericShortcut''​ - indică o scurtătură (de tip caracter sau număr) prin care poate fi accesată intrarea din cadrul meniului
 +
 +**b.** ''<​group>'',​ un container pentru intrări propriu-zise ale meniului (elemente ''<​item>''​) care partajează același comportament,​ acesta putând fi specificat ca proprietate a grupului (în loc de a fi precizat pentru fiecare element în parte)
 +  * ''​android:​visible''​ - vizibile sau invizibile
 +  * ''​android:​enabled''​- activate sau dezactivate
 +  * ''​android:​checkableBehavior''​ - selectate sau deselectate (valori posibile fiind ''​none'',​ ''​all''​ sau ''​single''​ după cum pot fi selectate nici una, toate sau o singură intrare a meniului din cadrul grupului)
 +
 +Pentru ambele tipuri de elemente, poate fi specificată proprietatea ''​android:​menuCategory''​ ce indică prioritatea corespunzătoare categoriei de meniu (''​container'',​ ''​system'',​ ''​secondary'',​ respectiv ''​alternative''​).
 +
 +Încărcarea meniului în cadrul activității (sau fragmentului) este realizată prin intermediul metodei ''​onCreateOptionsMenu(Menu)'':​
 +
 +<code java>
 +@Override
 +public boolean onCreateOptionsMenu(Menu menu) {
 +  getMenuInflater().inflate(R.menu.main,​ menu);
 +  return true;
 +}
 +</​code>​
 +
 +**2.** **programatic**,​ în codul sursă, de regulă în cadrul metodei ''​onCreateOptionsMenu()''​
 +Metodele prin intermediul cărora sunt adăugate intrări în cadrul meniului sunt:
 +<code java>
 +public SubMenu addSubmenu(int titleRes);
 +public SubMenu addSubmenu(CharSequence title);
 +public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes);
 +public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title);
 +
 +public MenuItem add(int titleRes);
 +public MenuItem add(CharSequence title);
 +public MenuItem add(int groupId, int itemId, int order, int titleRes);
 +public MenuItem add(int groupId, int itemId, int order, CharSequence title);
 +</​code>​
 +
 +Astfel, funcționalitatea descrisă prin intermediul fișierelor resursă XML poate fi obținută și prin intermediul codului sursă:
 +<code java>
 +final public static int GROUP_ID_NONE ​           = 0;
 +final public static int GROUP_ID_GENERIC_ACTIONS = 1;
 +
 +final public static int SUBMENU_ID_OPERATIONS ​   = 0;
 +
 +final public static int MENU_ID_CREATE ​          = 1;
 +final public static int MENU_ID_SETTINGS ​        = 2;
 +final public static int MENU_ID_ABOUT ​           = 3;
 +
 +final public static int MENU_ORDER ​              = 100;
 +
 +@Override
 +public boolean onCreateOptionsMenu(Menu menu) {
 +  MenuItem menuItem;
 +    ​
 +  SubMenu subMenu = menu.addSubMenu(GROUP_ID_NONE,​ SUBMENU_ID_OPERATIONS,​ MENU_ORDER, R.string.operations);​
 +  menuItem = subMenu.add(GROUP_ID_NONE,​ MENU_ID_CREATE,​ MENU_ORDER, R.string.create);​
 +  menuItem.setIcon(R.drawable.create);​
 +  menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);​
 +  // add other submenu entries
 +    ​
 +  menuItem = menu.add(GROUP_ID_GENERIC_ACTIONS,​ MENU_ID_SETTINGS,​ MENU_ORDER, R.string.settings);​
 +  menuItem.setIcon(R.drawable.settings);​
 +  menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);​
 +  // add other menu entries
 +    ​
 +  menu.setGroupVisible(GROUP_ID_GENERIC_ACTIONS,​ true);
 +  menu.setGroupEnabled(GROUP_ID_GENERIC_ACTIONS,​ true);
 +  menu.setGroupCheckable(GROUP_ID_GENERIC_ACTIONS,​ false, false);
 +    ​
 +  menuItem = menu.add(GROUP_ID_NONE,​ MENU_ID_SETTINGS,​ MENU_ORDER, R.string.about);​
 +  menuItem.setIcon(R.drawable.about);​
 +  menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);​
 +  // add other menu entries
 +    ​
 +  return true;
 +}
 +</​code>​
 +
 +Aceste metode pot fi utilizate și în situația în care meniul este definit prin intermediul unor fișiere resursă de tip XML, dorindu-se ulterior modificarea dinamică a acestuia.
 +
 +Alte metode utile puse la dispoziție de clasa ''​Menu''​ sunt:
 +  * ''​findItem(int)''​ - întoarce un obiect de tip ''​MenuItem'',​ dându-se identificatorul acestuia;
 +  * ''​getItem(int)''​ - întoarce un obiect de tip ''​MenuITem'',​ dându-se poziția acestuia;
 +  * ''​removeGroup(int)''​ - șterge toate elementele din cadrul unui grup, furnizându-se identificatorul acestuia;
 +  * ''​clear()''​ - șterge toate intrările intrărilor din cadrul meniului, acesta rămânând vid;
 +  * ''​removeItem(int)''​ - șterge o intrare din cadrul meniului, dându-se identificatorul acestuia;
 +  * ''​size()''​ - furnizează numărul de intrări din cadrul meniului;
 +  * ''​hasVisibleItems()''​ - precizează dacă există intrări din cadrul meniului care sunt vizible sau nu;
 +  * ''​close()''​ - determină închiderea meniului, în cazul în care acesta este deschis.
 +
 +<note tip>În situația în care sunt definite meniuri atât pentru activitate cât și pentru fragmentele pe care le conține, acestea sunt combinate, fiind incluse inițial opțiunile din activitate și ulterior acțiunile din fragmente, în ordinea în care acestea sunt declarate. De asemenea, se poate realiza și sortarea prin intermediul numărului de ordine definit de fiecare intrare propriu-zisă a meniului în parte.</​note>​
 +
 +<note tip>În cazul în care aplicația conține mai multe activități care folosesc același meniu de opțiuni, se va crea o clasă ''​Activity''​ care va implementa numai metodele ''​onCreateOptionsMenu()'',​ respectiv ''​onOptionsItemSelected()'',​ aceasta fiind extinsă pentru a se prelua funcționalitatea de instanțiere a meniului și de tratare a evenimentelor de apăsare.</​note>​
 +
 +==== Tratarea evenimentelor asociate meniurilor ====
 +
 +Tratarea evenimentelor de accesare a unei intrări a meniului se face în cadrul metodei ''​onOptionsItemSelected(MenuItem)'',​ apelată în mod automat în momentul producerii unui astfel de eveniment. Aceasta primește ca parametru opțiunea din cadrul meniului care a fost aleasă de utilizator, recunoașterea sa realizându-se prin intermediul identificatorului unic asociat fiecărei intrări:
 +
 +<code java>
 +@Override
 +public boolean onOptionsItemSelected(MenuItem item) {
 +  switch(item.getItemId()) {
 +    case R.id.create:​
 +    case MENU_ID_CREATE:​
 +      // ...
 +      return true;
 +    // ...           
 +  }
 +  return super.onOptionsItemSelected(item);​
 +}
 +</​code>​
 +
 +Este important ca ulterior tratării evenimentului de accesare a unei intrări din cadrul meniului, rezultatul întors să fie ''​true''​. În situația în care intrarea care a fost selectată nu este gestionată,​ se recomandă să se apeleze metoda părinte care returnează în mod implicit ''​false''​.
 +
 +Acesta este mecanismul recomandat pentru tratarea evenimentelor legate de meniuri. Există însă și alte alternative:​
 +
 +**1.** **definirea unei clase ascultător dedicate** care implementează interfața ''​MenuItem.OnMenuItemClickListener''​ și metoda ''​onMenuItemClick(MenuItem)'';​ atașarea obiectului ascultător se face prin intermediul metodei ''​setOnMenuItemClickListener()''​.
 +
 +<code java>
 +public class CustomMenuItemClickListener implements MenuItem.OnMenuItemClickListener {
 +  @Override
 +  public boolean onMenuItemClick(MenuItem item) {
 +    switch(item.getItemId()) {
 +      case R.id.create:​
 +      case MENU_ID_CREATE:​
 +        // ...
 +        return true;
 +      // ...           
 +    }
 +    return true;
 +  }
 +}
 +
 +// ..
 +
 +CustomMenuItemClickListener customMenuItemClickListener = new CustomMenuItemClickListener();​
 +menuItem.setOnMenuItemClickListener(customMenuItemClickListener);​
 +</​code>​
 +
 +Această metodă trebuie să fie apelată pentru fiecare intrare de meniu în parte. Ea are însă precedență în fața altor metode, astfel încât în situația în care rezultatul pe care îl întoarce este ''​true'',​ nu mai sunt apelate și alte metode, cum ar fi ''​onOptionsItemSelected()''​.
 +
 +**2.** **utilizarea unei intenții asociate la o intrare din cadrul meniului** prin intermediul metodei ''​setIntent(Intent)'',​ lansându-se în execuție activitatea corespunzătoare în condițiile în care:
 +  * nu este suprascrisă metoda de tratare a evenimentului de selectare a unei intrări din cadrul meniului ''​onOptionsItemSelected()'';​
 +  * în situația în care metoda de tratare a evenimentului de selectare a unei intrări din cadrul meniului ''​onOptionsItemSelected()''​ este suprascrisă,​ trebuie să se apeleze metoda părinte (''​super.onOptionsItemSelected(menu)''​) pentru acele intrări ale meniului ce nu sunt procesate.
 +
 +În mod implicit, nici o intrare din cadrul unui meniu nu are asociată o intenție.
 +
 +==== Alte tipuri de meniuri în Android ====
 +
 +=== Meniuri contextuale ===
 +
 +Un //meniu contextual//​ (clasa [[http://​developer.android.com/​reference/​android/​view/​ContextMenu.html|ContextMenu]]) este asociat de regulă unui control din cadrul interfeței grafice (spre diferență de meniurile de opțiuni care sunt asociate activităților). De regulă, acesta este accesat la operația de apăsare prelungită pe obiectul respectiv (de tip ''​View''​),​ având aceeași structură ca orice meniu, metodele utilizate pentru construirea sa fiind aceleași.
 +
 +<note important>​În cazul meniurilor contextuale,​ nu sunt suportate submeniurile,​ pictogramele sau furnizarea unor scurtături.</​note>​
 +
 +O activitate poate avea asociat un singur meniu de opțiuni, însă mai multe meniuri contextuale,​ asociate fiecare câte unui element din interfața sa grafică.
 +
 +Instanțierea unui meniu contextual este un proces realizat în mai multe etape:
 +  - indicarea faptului că obiectul din cadrul interfeței grafice va avea asociat un meniu contextual <code java>
 +registerForContextMenu(view);​
 +</​code>​ O astfel de operație este necesară datorită faptului că numai unele dintre elementele interfeței grafice din cadrul activității (sau fragmentului) vor avea asociat un astfel de meniu.\\ Apelul metodei va atașa activității un obiect ascultător de tip ''​View.OnCreateContextMenuListener'',​ astfel încât operația de apăsare prelungită pe obiectul respectiv va declanșa apelul metodei ''​onCreateContextMenu(ContextMenu,​ View, ContextMenuInfo)''​ care trebuie definită de programator.\\ În cazul în care se dorește atașarea unui meniu contextual pentru elementele unui container pentru colecții de date (''​ListView'',​ ''​GridView''​) este suficient ca acesta să fie transmis ca parametru al metodei pentru ca meniul contextual să fie afişat pentru fiecare dintre intrările pe care le conţine.
 +  - implementarea metodei ''​onCreateContextMenu(ContextMenu,​ View, ContextMenuInfo)''​ în cadrul activității (sau fragmentului) <code java>
 +@Override
 +public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
 +  super.onCreateContextMenu(menu,​ v, menuInfo);
 +  // option 1
 +  MenuInflater inflater = getMenuInflater();​
 +  inflater.inflate(R.menu.context_menu,​ menu);
 +  // option 2
 +  menu.setHeaderTitle(R.string.operations);​
 +  menu.add(GROUP_ID_NONE,​ MENU_ID_CREATE,​ MENU_ORDER, R.string.create);​
 +}
 +</​code>​ Metoda primește următorii parametri (ce pot fi utilizați pentru a se determina ce obiect din cadrul interfeței grafice a fost accesat, pentru a se instanția meniul contextual corespunzător):​
 +    * ''​menu''​ - meniul contextual care se dorește a fi afișat;
 +    * ''​view''​ - elementul din cadrul interfeței grafice căruia îi este asociat meniul contextual;
 +    * ''​menuInfo''​ - un obiect care oferă informații suplimentare cu privire la elementul selectat.
 +  - implementarea metodei ''​onContextItemSelected(MenuItem)''​ care gestionează acțiunea de selectare a unei intrări din cadrul meniului contextual <code java>
 +@Override
 +public boolean onContextItemSelected(MenuItem item) {
 +  AdapterContextMenuInfo adapterContextMenuInfo = (AdapterContextMenuInfo)item.getMenuInfo();​
 +  // or
 +  ExpandableContextMenuInfo expandableContextMenuInfo = (ExpandableContextMenuInfo)item.getMenuInfo();​
 +  switch(item.getItemId()) {
 +    case R.id.create:​
 +    case MENU_ID_CREATE:​
 +      // ...
 +      return true;
 +    // ...           
 +  }
 +  return super.onContextItemSelected(item);​
 +}
 +</​code>​ Tratarea acțiunii de selectare a unei intrări din cadrul meniului contextual trebuie semnalată prin întoarcerea unui rezultat al metodei de tip ''​true'',​ în caz contrar trebuind apelată metoda părinte ''​super.onContextItemSelected(item)''​ care va determina apelarea metodelor corespunzătoare eventualelor fragmente pe care le poate conține activitatea (în ordinea în care acestea au fost declarate) până în momentul în care una dintre acestea va întoarce un rezultat (implementarea implicită a metodei întoarce ''​false''​ atât pentru clasa ''​Activity''​ cât și pentru clasa ''​Fragment''​).
 +
 +=== Meniuri alternative ===
 +
 +Prin intermediul unui //meniu alternativ//​ ce poate face parte dintr-un meniu de opțiuni (inclusiv dintr-unul din submeniurile sale) sau dintr-un meniu contextual, pot fi lansate în execuție activități din cadrul aplicației curente sau din cadrul altor activități,​ dacă acestea sunt disponibile,​ pentru a gestiona datele curente. În situația în care nu sunt identificate astfel de activități,​ meniul contextual respectiv nu va conține nici o intrare.
 +
 +Instanțierea unui meniu alternativ se realizează **în cadrul metodei ''​onCreateOptionsMenu(Menu)''​** și este un proces realizat în mai multe etape:
 +  - definirea unei intenții care va indica care sunt cerințele referitoare la activitatea care urmează a fi invocată prin intermediul meniului contextual; intenția va specifica o categorie ce are valoarea ''​Intent.CATEGORY_ALTERNATIVE''​ sau ''​Intent.CATEGORY_SELECTED_ALTERNATIVE''​ (dacă activitatea se referă la intrarea selectată din cadrul meniului, intenția fiind definită în cadrul metodei ''​onCreateContextMenu()''​) <code java>
 +Intent intent = new Intent(null,​ getIntent().getData());​ // the URI of the data application is working with; getIntent() may return null!!!
 +intent.addCategory(Intent.CATEGORY_ALTERNATIVE);​
 +</​code>​ O activitate poate fi invocată prin intermediul unui meniu contextual, dacă specifică valorile respective în câmpul ce indică filtrul pentru intenții în fișierul ''​AndroidManifest.xml'':​ <code xml>
 +<​intent-filter>​
 + ...
 + <​category android:​name="​android.intent.category.ALTERNATIVE"​ />
 + <​category android:​name="​android.intent.category.SELECTED_ALTERNATIVE"​ />
 + ...
 +</​intent-filter>​
 +</​code>​
 +  - căutarea unor activități care pot fi invocate folosind intenția respectivă prin intermediul metodei ''​Menu.addIntentOptions''​ (apelată pe obiectul transmis ca parametru metodei ''​onCreateOptionsMenu()''​) <code java>
 +menu.addIntentOptions(
 +  Menu.CATEGORY_ALTERNATIVE,​ // the group the contextual menu will be attached to
 +  Menu.CATEGORY_ALTERNATIVE,​ // menu item ID for all of the menu items
 +  Menu.CATEGORY_ALTERNATIVE,​ // order number for each of the menu items
 +  this.getComponentName(), ​  // the name of the current activity
 +  null,                      // no specific items to place with priority
 +  intent, ​                   // intent that can invoke the corresponding activities (previously defined)
 +  0,                         // no additional flags to manage the menu items
 +  null); ​                    // no array of menu items to correlate with specific items
 +</​code>​ Metoda furnizează o listă de activități care corespund criteriilor precizate de intenție populând cu acestea intrările din cadrul meniului, în cadrul unui anumit grup, alocându-le identificatori și numere de ordine începând cu o anumită valoare.\\ Semnătura metodei este: <code java>
 +public abstract int addIntentOptions (int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems)
 +</​code>​ semnificația parametrilor fiind:
 +    * ''​groupId''​ - grupul din cadrul meniului din care vor face parte intrările meniului contextual; se poate folosi valoarea ''​Menu.NONE''​ în situația în care elementele respective nu trebuie asociate unui grup
 +    * ''​itemId''​ - identificator unic folosit pentru elementele meniului contextual; în cazul în care acesta nu va mai fi utilizat ulterior, se poate furniza valoarea ''​Menu.NONE''​
 +    * ''​order''​ - număr de ordine utilizat pentru sortarea intrărilor din cadrul meniului contextual; dacă acest criteriu nu va fi utilizat, poate fi specificată valoarea ''​Menu.NONE''​
 +    * ''​caller''​ - denumirea componentei activității curente (denumirea pachetului și denumirea clasei) folosită de sistemul de operare pentru a se identifica resursa care a invocat altă activitate
 +    * ''​specifics''​ - elemente specifice care se doresc a fi plasate cu prioritate
 +    * ''​intent''​ - intenție care descrie tipurile de elemente cu care va fi populat meniul contextual
 +    * ''​flags''​ - opțiuni suplimentare care controlează mecanismul de atașare al intrărilor (spre exemplu, crearea unui meniu contextual numai cu activitățile specificate de intenția curentă sau folosirea lor împreună cu cele existente anterior - valoarea 0 indică faptul că doar valorile identificate în cadrul apelului curent al metodei ar trebui folosite)
 +    * ''​outSpecificItems''​ - un tablou în care se plasează intrările din cadrul meniului contextual care au fost generate pentru fiecare dintre elementele specifice (indicate ca parametru); pentru acțiunile în cazul cărora nu a fost identificată nici o activitate, se va furniza un rezultat ''​null''​
 +
 +<note tip>​Titlul intrărilor din cadrul meniului contextual este preluat din valoarea ''​android:​label''​ specificată pentru elementul ''<​intent-filter>''​ corespunzător activității respective din cadrul fișierului ''​AndroidManifest.xml''​.</​note>​
 +
 +=== Meniuri de tip pop-up ===
 +
 +Un //meniu de tip pop-up// (clasa [[http://​developer.android.com/​reference/​android/​widget/​PopupMenu.html|PopupMenu]]) este asociat unui element din cadrul interfeței grafice, afișat deasupra sau sub aceasta în momentul în care se produce un eveniment legat de acesta. ​
 +
 +El se distinge însă față de celelalte tipuri de meniuri, față de care ar părea să implementeze o funcționalitate comună:
 +  * spre diferență de meniurile de opțiuni, acesta este creat în mod manual și doar atunci când este accesat elementul căruia îi este atașat;
 +  * spre diferență de meniurile contextuale,​ acesta nu are nici un impact asupra controlului grafic căruia îi este atașat;
 +
 +Un meniu de tip pop-up poate fi creat - ca orice meniu - atât prin intermediul unui fișier XML de resurse cât și programatic.
 +
 +Utilizarea unui meniu de tip pop-up presupune definirea unei metode care va fi apelată (manual, de către programator) atunci când se realizează o acțiune legată de controlul grafic căruia i se atașază:
 +<code java>
 +public void showPopupMenu(View view) {
 +
 +  PopupMenu popupMenu = new PopupMenu(this,​ menu);
 +    ​
 +  // option 1: defining a popup menu via a XML resource file
 +    ​
 +  // android 3.0
 +  MenuInflater menuInflater = popupMenu.getMenuInflater();​
 +  menuInflater.inflate(R.menu.popup_menu,​ popupMenu.getMenu());​
 +        ​
 +  // android 4.0
 +  popupMenu.inflate(R.menu.popup_menu);​
 +        ​
 +  // option 2: defining a popup menu programatically
 +  Menu menu = popupMenu.getMenu();​
 +  menu.add(GROUP_ID_NONE,​ MENU_ID_CREATE,​ MENU_ORDER, R.string.create);​
 +  // ...
 +    ​
 +  popup.show();​
 +}
 +</​code>​
 +
 +Se observă că un obiect de tipul ''​PopupMenu''​ poate fi instanțiat transmițând ca parametri contextul (activitatea) în care acesta urmează să fie afișat precum și controlul grafic căruia îi va fi atașat (existând posibilitatea de a se preciza și modul de dispunere față de acesta).
 +
 +<code java>
 +public PopupMenu (Context context, View anchor);
 +public PopupMenu (Context context, View anchor, int gravity);
 +</​code>​
 +
 +Tratarea evenimentului de accesare a unui element din cadrul meniului de tip pop-up se face prin implementarea interfeței ''​PopupMenu.OnMenuItemClickListener'':​
 +<code java>
 +popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
 +    @Override
 +    public boolean onMenuItemClick(MenuItem item) {
 +      switch(item.getItemId()) {
 +        case R.id.create:​
 +        case MENU_ID_CREATE:​
 +          // ...
 +          return true;
 +        // ...           
 +      }
 +      return true;
 +    }
 +});
 +</​code>​
 +
 +===== Activitate de Laborator =====
 +
 +Se dorește implementarea unei aplicații Android care să afișeze lista tuturor contactelor din agenda telefonică,​ împreună cu unele informații despre aceștia (imaginea asociată, numele, numărul de accesări, cea mai recentă accesare, apartenența la grupul de favoriți). De asemenea, în momentul în care este selectat un element din listă, se vor afișa toate numerele telefonice și toate adresele de poștă electronică stocate cu privire la acesta, împreună cu tipul lor. Totodată, va exista o facilitate de căutare pe măsură ce se introduce un șir de caractere, care poate fi regăsit în oricare dintre detaliile contactului respectiv.
 +
 +{{ :​laboratoare:​laborator05:​addressbook.png?​nolink&​400 }}
 +
 +**0.** Se recomandă ca în situația în care se folosește un emulator, să se adauge în aplicația //People// cel puțin două contacte care să poată fi gestionate prin intermediul aplicației.
 +
 +<​HTML>​
 +<​center>​
 +</​HTML>​
 +{{:​laboratoare:​laborator05:​people1.png?​nolink&​300}} → {{:​laboratoare:​laborator05:​people2.png?​nolink&​300}}
 +<​HTML>​
 +</​center>​
 +</​HTML>​
 +
 +**1.** În contul Github personal, să se creeze un depozit denumit '​Laborator05'​. Inițial, acesta trebuie să fie gol (NU trebuie să bifați nici adăugarea unui fișier ''​README.md'',​ nici a fișierului ''​.gitignore''​ sau a a fișierului ''​LICENSE''​).
 +
 +**2.** Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://​www.github.com/​pdsd2015/​Laborator05|]]. În urma acestei operații, directorul ''​Laborator05''​ va trebui să se conțină în subdirectorul ''​labtasks''​ proiectul Eclipse denumit ''​AddressBook'',​ fișierul ''​README.md''​ și un fișier ''​.gitignore''​ care indică tipurile de fișiere (extensiile) ignorate. <​code>​
 +student@pdsd2015:​~$ git clone https://​www.github.com/​pdsd2015/​Laborator05
 +</​code>​
 +
 +**3.** Să se încarce conținutul descărcat în cadrul depozitului '​Laborator05'​ de pe contul Github personal. <​code>​
 +student@pdsd2015:​~$ cd Laborator05/​labtasks
 +student@pdsd2015:​~/​Laborator05/​labtasks$ git remote add Laborator05_perfectstudent https://​github.com/​perfectstudent/​Laborator05
 +student@pdsd2015:​~/​Laborator05/​labtasks$ git push Laborator05_perfectstudent master
 +</​code>​
 +
 +**4.** Să se încarce în mediul integrat de dezvoltare Eclipse proiectul ''​AddressBook'',​ folosind opțiunea //File// → //​Import...//​.
 +
 +**5.** Să se afișeze contactele din agenda telefonică sub forma unei liste, indicând, pentru fiecare dintre acestea:
 +  * numele persoanei (atributul ''​Contact.DISPLAY_NAME_PRIMARY''​);​
 +  * pictograma asociată (atributul ''​Contact.PHOTO_THUMBNAIL_URI''​) - în cazul în care aceasta lipsește, se va afișa imaginea din resursa ''​R.drawable.contact_photo'';​
 +  * frecvența cu care a fost contact (numărul de apeluri telefonice / mesaje electronice - SMS-uri, poștă electronică):​ atributul ''​Contact.TIMES_CONTACTED'';​
 +  * data calendaristică și timpul la care a fost contactat cel mai recent (atributul ''​Contact.LAST_TIME_CONTACTED''​);​
 +  * o imagine în funcție de apartenența contactului la grupul de favoriți (atributul ''​Contacts.STARRED''​):​
 +    * ''​R.drawable.star_set''​ - dacă persoana face parte din grupul de favoriți;
 +    * ''​R.drawable.star_unset''​ - dacă persoana NU face parte din grupul de favoriți.
 +
 +**a.** Se va defini fișierul ''​contact_view.xml''​ în care se vor declara controalele grafice necesare expunerii informațiilor legate de contacte. <file xml contact_view.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="​wrap_content"​ >
 +    ​
 +  <​ImageView
 +    android:​id="​@+id/​contact_photo_image_view"​
 +    android:​layout_width="​0dp"​
 +    android:​layout_height="​wrap_content"​
 +    android:​layout_gravity="​left|center_vertical"​
 +    android:​layout_margin="​10dp"​
 +    android:​layout_weight="​2"​
 +    android:​contentDescription="​@string/​photo_content_description"/>​
 +    ​
 +  <​LinearLayout
 +    android:​layout_width="​0dp"​
 +    android:​layout_height="​wrap_content"​
 +    android:​layout_gravity="​center"​
 +    android:​layout_weight="​12" ​  
 +    android:​orientation="​vertical"​ >
 +    ​
 +    <​TextView
 +      android:​id="​@+id/​contact_name_text_view"​
 +      android:​layout_width="​wrap_content"​
 +      android:​layout_height="​wrap_content"​ />
 +        ​
 +    <​TextView
 +      android:​id="​@+id/​contact_times_contacted_text_view"​
 +      android:​layout_width="​wrap_content"​
 +      android:​layout_height="​wrap_content"​
 +      android:​text="​@string/​times_contacted"​ />
 +        ​
 +    <​TextView
 +      android:​id="​@+id/​contact_last_time_contacted_text_view"​
 +      android:​layout_width="​wrap_content"​
 +      android:​layout_height="​wrap_content"​
 +      android:​text="​@string/​last_time_contacted"​ />
 +    ​
 +  </​LinearLayout>​
 +    ​
 +  <​ImageView
 +    android:​id="​@+id/​contact_starred_image_view"​
 +    android:​layout_width="​0dp"​
 +    android:​layout_height="​wrap_content"​
 +    android:​layout_gravity="​right|center_vertical"​
 +    android:​layout_margin="​10dp"​
 +    android:​layout_weight="​1" ​      
 +    android:​contentDescription="​@string/​starred_description"/> ​
 +
 +</​LinearLayout>​
 +</​file>​
 +
 +<note tip>Așa cum se poate observa, pentru ca atributul ''​android:​layout_weight''​ să fie luat în considerare,​ dimensiunea pe orientarea respectivă (''​android:​layout_width''​ / ''​android:​layout_height''​) trebuie să aibă valoarea ''​0dp''​.</​note>​
 +
 +**b.** În clasa ''​ContactAdapter''​ din pachetul ''​ro.pub.cs.systems.lab05.addressbook.controller''​ se vor implementa metodele necesare definirii unui obiect adaptor (de tip ''​BaseAdapter''​) particularizat.
 +  * ''​int getCount()''​ - întoarce numărul de înregistrări din sursa de date afișată în cadrul listei;
 +  * ''​Object getItem(int position)''​ - întoarce sursa de date corespunzătoare unui contact de la o anumită poziție a listei;
 +  * ''​long getItemId(int position)''​ - întoarce identificatorul interfeței grafice corespunzătoare unui contact de la o anumită poziție a listei;
 +
 +<note tip>De regulă, metoda ''​long getItemId(int position)''​ poate întoarce valoarea 0 în situația în care nu este necesară accesarea interfeței grafice prin intermediul metodei ''​View findViewById(int id)''​. </​note>​
 +
 +  * ''​View convertView(int position, View convertView,​ ViewGroup parent)''​ - întoarce interfața grafică corespunzătoare unui contact de la o anumită poziție a listei.
 +
 +<file java ContactAdapter.java>​
 +public class ContactAdapter extends BaseAdapter {
 +  @Override
 +  public int getCount() {
 +    return data.size();​
 +  }
 +
 +  @Override
 +  public Object getItem(int position) {
 +    return data.get(position);​
 +  }
 +
 +  @Override
 +  public long getItemId(int position) {
 +    return 0;
 +  }
 +
 +  @Override
 +  public View getView(int position, View convertView,​ ViewGroup parent) {
 +    Contact contact = data.get(position);​
 +    LayoutInflater inflater = (LayoutInflater)context.getLayoutInflater();​
 +    View contactView = inflater.inflate(R.layout.contact_view,​ parent, false);
 +    ImageView contactPhotoImageView = (ImageView)contactView.findViewById(R.id.contact_photo_image_view);​
 +    TextView contactNameTextView = (TextView)contactView.findViewById(R.id.contact_name_text_view);​
 +    TextView contactTimesContactedTextView = (TextView)contactView.findViewById(R.id.contact_times_contacted_text_view);​
 +    TextView contactLastTimeContactedTextView = (TextView)contactView.findViewById(R.id.contact_last_time_contacted_text_view);​
 +    ImageView contactStarredImageView = (ImageView)contactView.findViewById(R.id.contact_starred_image_view);​
 +
 +    if (getCheckedItemPosition() == position) {
 +      contactView.setBackgroundColor(context.getResources().getColor(R.color.light_blue));​
 +    } else {
 +      contactView.setBackgroundColor(context.getResources().getColor(R.color.light_gray));​
 +    }
 +
 +    AssetFileDescriptor assetFileDescriptor = null;
 +    try {
 +      Uri contactPhotoUri = contact.getPhoto();​
 +      if (contactPhotoUri == Uri.EMPTY) {
 +        contactPhotoImageView.setImageResource(R.drawable.contact_photo);​
 +      } else {
 +        assetFileDescriptor = context.getContentResolver().openAssetFileDescriptor(contactPhotoUri,​ "​r"​);​
 + FileDescriptor fileDescriptor = assetFileDescriptor.getFileDescriptor();​
 + if (fileDescriptor != null) {
 +   contactPhotoImageView.setImageBitmap(BitmapFactory.decodeFileDescriptor(fileDescriptor,​ null, null));
 + }
 +      }
 +    } catch (FileNotFoundException fileNotFoundException) {
 +      Log.e(Constants.TAG,​ "An exception has occurred: "​+fileNotFoundException.getMessage());​
 +      if (Constants.DEBUG) {
 + fileNotFoundException.printStackTrace();​
 +      }
 +    } finally {
 +      if (assetFileDescriptor != null) {
 +        try {
 +          assetFileDescriptor.close();​
 + } catch (IOException ioException) {
 +   Log.e(Constants.TAG,​ "An exception has occurred: "​+ioException.getMessage());​
 +          if (Constants.DEBUG) {
 +            ioException.printStackTrace();​
 +          }
 +        }
 +      }
 +    }
 +    ​
 +    contactNameTextView.setText(contact.getName());​
 +    contactTimesContactedTextView.setText(context.getResources().getString(R.string.times_contacted)+"​ "​+contact.getTimesContacted());​
 +    long value = contact.getLastTimeContacted();​
 +    if (value != 0) {
 +      contactLastTimeContactedTextView.setText(context.getResources().getString(R.string.last_time_contacted)+"​ "​+Utilities.displayDateAndTime(value));​
 +    } else {
 +      contactLastTimeContactedTextView.setText(context.getResources().getString(R.string.last_time_contacted)+"​ -");
 +    }
 +    if (contact.getStarred() == 1) {
 +      contactStarredImageView.setImageResource(R.drawable.star_set);​
 +    } else {
 +      contactStarredImageView.setImageResource(R.drawable.star_unset);​
 +    }
 +    return contactView;​
 +  }
 +}
 +</​file>​
 +
 +**6.** Să se implementeze un meniu, care să ofere următoarele funcționalități:​
 +  * adăugarea informațiilor referitoare la un contact (nume, numere de telefon, adrese de poștă electronică);​
 +
 +{{ :​laboratoare:​laborator05:​insert_option.png?​nolink&​400 }}
 +
 +  * editarea informațiilor referitoare la un contact selectat în listă (nu va fi posibilă adăugarea de alte numere de telefon / adrese de poștă electronică);​
 +
 +{{ :​laboratoare:​laborator05:​update_option.png?​nolink&​400 }}
 +
 +  * ștergerea informațiilor referitoare la un contact selectat în listă.
 +
 +<​note>​Pentru interfața grafică corespunzătoare acestor funcționalități se va utiliza activitatea ''​ContactsManagerActivity''​.</​note>​
 +
 +<​note>​Implementarea funcționalităților legate de furnizori de conținut nu face obiectul acestui laborator și nu trebuie să fie cunoscută în detaliu.</​note>​
 +
 +**a.** în directorul ''​res/​menu''​ se va defini un fișier ''​address_book.xml''​ conținând structura fișierului (câte un element de tip ''<​item>''​ pentru fiecare funcționalitate,​ specificând un text care trebuie afișat și o imagine care va fi plasată în bara de stare (dacă spațiul îl permite);
 +
 +<file xml addressbook.xml>​
 +<menu xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​
 +  xmlns:​tools="​http://​schemas.android.com/​tools"​
 +  tools:​context="​ro.pub.cs.systems.pdsd.lab05.addressbook.AddressBookActivity"​ >
 +
 +  <item
 +    android:​id="​@+id/​action_insert"​
 +    android:​icon="​@drawable/​insert"​
 +    android:​orderInCategory="​100"​
 +    android:​showAsAction="​ifRoom"​
 +    android:​title="​@string/​insert"/>​
 +    ​
 +  <item
 +    android:​id="​@+id/​action_update"​
 +    android:​icon="​@drawable/​update"​
 +    android:​orderInCategory="​100"​
 +    android:​showAsAction="​ifRoom"​
 +    android:​title="​@string/​update"/>​
 +    ​
 +  <item
 +    android:​id="​@+id/​action_delete"​
 +    android:​icon="​@drawable/​delete"​
 +    android:​orderInCategory="​100"​
 +    android:​showAsAction="​ifRoom"​
 +    android:​title="​@string/​delete"/> ​   ​
 +
 +</​menu>​
 +</​file>​
 +
 +**b.** În metoda ''​boolean onOptionsItemSelected(MenuItem item)''​ din clasa ''​AddressBookActivity''​ (pachetul ''​ro.pub.cs.systems.pdsd.lab05.addressbook.view''​) să se realizeze operațiile de adăugare, modificare, ștergere, în funcție de identificatorul intrării din meniu care a fost accesată.
 +
 +<code java>
 +@Override
 +public boolean onOptionsItemSelected(MenuItem item) {
 +
 +  ListView addressBookListView = (ListView)findViewById(R.id.address_book_list_view);​
 +  ContactAdapter addressBookContactAdapter = (ContactAdapter)addressBookListView.getAdapter();​
 +
 +  int id = item.getItemId();​
 +  if (id == R.id.action_insert) {
 +    Intent intentAdd = new Intent(this,​ ContactsManagerActivity.class);​
 +    intentAdd.putExtra(Constants.OPERATION,​ Constants.OPERATION_INSERT);​
 +    startActivityForResult(intentAdd,​ Constants.OPERATION_INSERT);​
 +    return true;
 +  }
 +
 +  if (id == R.id.action_update) {
 +    if (addressBookContactAdapter.getCheckedItemPosition() == -1) {
 +      Toast.makeText(this,​ "There is no selection made", Toast.LENGTH_LONG).show();​
 +      return false;
 +    }
 +    Intent intentUpdate = new Intent(this,​ ContactsManagerActivity.class);​
 +    intentUpdate.putExtra(Constants.OPERATION,​ Constants.OPERATION_UPDATE);​
 +    intentUpdate.putExtra(Constants.CONTACT_NAME,​ addressBookContactAdapter.getSelectedContact().getName());​
 +    int index;
 +    TextView contactPhonesTextView = (TextView)findViewById(R.id.contact_phones_text_view);​
 +    String contactPhoneNumbers = contactPhonesTextView.getText().toString();​
 +    String[] contactPhoneNumbersParts = contactPhoneNumbers.replaceAll("​ ", ""​).split("​[:​\n]"​);​
 +    if ((contactPhoneNumbersParts.length % 2) == 0) {
 +      ArrayList<​PhoneNumber>​ contactPhones = new ArrayList<​PhoneNumber>​();​
 +      index = 0;
 +      while (index < contactPhoneNumbersParts.length) {
 +        PhoneNumber contactPhoneNumber = new PhoneNumber(contactPhoneNumbersParts[index+1],​ contactPhoneNumbersParts[index]);​
 +        contactPhones.add(contactPhoneNumber);​
 +        index += 2;
 +      }
 +      intentUpdate.putExtra(Constants.CONTACT_PHONES,​ contactPhones);​
 +    }
 +    TextView contactEmailsTextView = (TextView)findViewById(R.id.contact_emails_text_view);​
 +    String contactEmailAddresses = contactEmailsTextView.getText().toString();​
 +    String[] contactEmailAddressesParts = contactEmailAddresses.replaceAll("​ ", ""​).split("​[:​\n]"​);​
 +    if ((contactEmailAddressesParts.length % 2) == 0) {
 +      ArrayList<​EmailAddress>​ contactEmails = new ArrayList<​EmailAddress>​();​
 +      index = 0;
 +      while (index < contactEmailAddressesParts.length) {
 +        EmailAddress contactEmailAddress = new EmailAddress(contactEmailAddressesParts[index+1],​ contactEmailAddressesParts[index]);​
 +        contactEmails.add(contactEmailAddress);​
 +        index += 2;
 +      }
 +      intentUpdate.putExtra(Constants.CONTACT_EMAILS,​ contactEmails);​
 +    }
 +    intentUpdate.putExtra(Constants.CONTACT_ID,​ String.valueOf(addressBookContactAdapter.getContactId()));​
 +    startActivityForResult(intentUpdate,​ Constants.OPERATION_UPDATE);​
 +    return true;
 +  }
 +
 +  if (id == R.id.action_delete) {
 +    ArrayList<​ContentProviderOperation>​ contentProviderOperations = new ArrayList<​ContentProviderOperation>​();​
 +    contentProviderOperations.add(ContentProviderOperation
 +        .newDelete(ContactsContract.RawContacts.CONTENT_URI)
 +        .withSelection(
 +            RawContacts.CONTACT_ID + " = ?", ​
 +            new String[] {
 +                String.valueOf(addressBookContactAdapter.getContactId())
 +            })
 +        .build()
 +    );
 +    try {
 +      getContentResolver().applyBatch(ContactsContract.AUTHORITY,​ contentProviderOperations);​
 +    } catch (RemoteException remoteException) {
 +      Log.e(Constants.TAG,​ "An exception has occurred: "​+remoteException.getMessage());​
 +      if (Constants.DEBUG) {
 +        remoteException.printStackTrace();​
 +      }
 +      return false;
 +    } catch (OperationApplicationException operationApplicationException) {
 +      Log.e(Constants.TAG,​ "An exception has occurred: "​+operationApplicationException.getMessage());​
 +      if (Constants.DEBUG) {
 +        operationApplicationException.printStackTrace();​
 +      }
 +      return false;
 +    }
 +    FragmentManager fragmentManager = getFragmentManager();​
 +    ContactAdditionalDetailsFragment contactAdditionalDetailsFragment = (ContactAdditionalDetailsFragment)fragmentManager.findFragmentById(R.id.contact_additional_details);​
 +    if (contactAdditionalDetailsFragment != null) {
 +      FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​
 +      fragmentTransaction.remove(contactAdditionalDetailsFragment);​
 +      fragmentTransaction.commit();​
 +      addressBookContactAdapter.setCheckedItemPosition(-1);​
 +    }     ​
 +    return true;
 +  }
 +  return super.onOptionsItemSelected(item);​
 +}
 +</​code>​
 +
 +**7.** Să se realizeze o dispunere alternativă a listei de contacte din agenda telefonică,​ astfel încât pe rândurile pare respectiv pe rândurile impare, imaginea asociată unei persoane și pictograma indicând dacă aceasta a fost inclusă în lista de favoriți să fie aranjate invers.
 +
 +{{ :​laboratoare:​laborator05:​address_book_dispunere_alternativa.png?​nolink&​400 }}
 +
 +  * în directorul ''​res/​layout''​ se vor defini două fișiere, ''​contact_view_odd.xml''​ și ''​contact_view_even.xml'',​ conținând cele două moduri de dispunere diferită;
 +  * în metoda ''​getView()''​ din clasa ''​ContactAdapter''​ se vor încărca cele două fișiere succesiv, în funcție de paritatea rândului pentru care se obține interfața grafică: <code java>
 +if (position % 2 == 0) {
 +  contactView = inflater.inflate(R.layout.contact_view_even,​ parent, false);
 +} else {
 +  contactView = inflater.inflate(R.layout.contact_view_odd,​ parent, false);
 +}
 +</​code>​
 +  * în clasa ''​ContactAdapter''​ se vor suprascrie metodele:
 +    * ''​int getViewTypeCount()''​ - care furnizează numărul de vizualizări diferite ale listei;
 +    * ''​int getItemViewType(int position)''​ - care întoarce indexul vizualizării,​ pentru poziția curentă. <code java>
 +public final static int CONTACT_VIEW_TYPES ​    = 2;
 +public final static int CONTACT_VIEW_TYPE_ODD ​ = 0;
 +public final static int CONTACT_VIEW_TYPE_EVEN = 1;
 +
 +@Override
 +public int getViewTypeCount() {
 +  return CONTACT_VIEW_TYPES;​
 +}
 +
 +@Override
 +public int getItemViewType(int position) {
 +  if (position % 2 == 0) {
 +    return CONTACT_VIEW_TYPE_ODD;​
 +  }
 +  return CONTACT_VIEW_TYPE_EVEN;​
 +}
 +</​code>​
 +
 +**8.** (opțional) Să se implementeze optimizări la nivelul codului sursă pentru a eficientiza timpul de încărcare a listei, în momentul în care utilizatorul realizează operația de derulare prin aceasta:
 +  * să se instanțieze obiectul responsabil cu expandarea mecanismului de dispunere a unui element din cadrul listei pe constructorul clasei adaptor <code java>
 +public ContactAdapter(Activity context) {
 +  this.context = context;
 +  inflater = (LayoutInflater)context.getLayoutInflater();​
 +  data = new ArrayList<​Contact>​();​
 +  initData();
 +}
 +</​code>​
 +  * să se reutilizeze componentele listei, încărcate în memorie, dar care nu mai sunt vizibile (la furnizarea interfeței grafice, în metoda getView()) <code java>
 +@Override
 +public View getView(int position, View convertView,​ ViewGroup parent) {
 +  View contactView;​
 +  Contact contact = data.get(position);​
 +  if (convertView == null) {
 +    LayoutInflater inflater = (LayoutInflater)context.getLayoutInflater();​
 +    contactView = inflater.inflate(R.layout.contact_view,​ parent, false);
 +    // ...
 +  } else {
 +    contactView = convertView;​
 +  }
 +  // ...
 +  return contactView;​
 +}
 +</​code>​
 +  * să se stocheze structura interfeței grafice într-un obiect de tip ''​ViewHolder''​ prin care controalele pot fi accesate, fără a mai fi necesară utilizarea metodei ''​findViewById()''​ <code java>
 +public class ContactAdapter extends BaseAdapter {
 +  public static class ViewHolder {
 +    ImageView contactPhotoImageView,​ contactStarredImageView;​
 +    TextView contactNameTextView,​ contactTimesContactedTextView,​ contactLastTimeContactedTextView;​
 +  };
 +  ​
 +  // ...
 +  ​
 +  @Override
 +  public View getView(int position, View convertView,​ ViewGroup parent) {
 +    View contactView;​
 +    ViewHolder viewHolder;
 +    Contact contact = data.get(position);​
 +    if (convertView == null) {
 +      LayoutInflater inflater = (LayoutInflater)context.getLayoutInflater();​
 +      contactView = inflater.inflate(R.layout.contact_view,​ parent, false);
 +      viewHolder = new ViewHolder();​
 +      viewHolder.contactPhotoImageView = (ImageView)contactView.findViewById(R.id.contact_photo_image_view);​
 +      viewHolder.contactNameTextView = (TextView)contactView.findViewById(R.id.contact_name_text_view);​
 +      viewHolder.contactTimesContactedTextView = (TextView)contactView.findViewById(R.id.contact_times_contacted_text_view);​
 +      viewHolder.contactLastTimeContactedTextView = (TextView)contactView.findViewById(R.id.contact_last_time_contacted_text_view);​
 +      viewHolder.contactStarredImageView = (ImageView)contactView.findViewById(R.id.contact_starred_image_view);​
 +      contactView.setTag(viewHolder);​
 +    } else {
 +      contactView = convertView;​
 +    }
 +    // ...
 +    viewHolder = (ViewHolder)contactView.getTag();​
 +
 +    viewHolder.contactPhotoImageView.setImageBitmap(...);​
 +    viewHolder.contactNameTextView.setText(...);​
 +    viewHolder.contactTimesContactedTextView.setText(...);​
 +    viewHolder.contactLastTimeContactedTextView.setText(...);​
 +    viewHolder.contactStarredImageView.setImageResource(...);​
 +  }
 +  return contactView;​
 +}
 +</​code>​
 +
 +**9.** ​ Să se încarce modificările realizate în cadrul depozitului '​Laborator05'​ de pe contul Github personal, folosind un mesaj sugestiv. <​code>​
 +student@pdsd2015:​~/​Laborator05/​labtasks$ git add AddressBook/​*
 +student@pdsd2015:​~/​Laborator05/​labtasks$ git commit -m "​implemented taks for laboratory 05"
 +student@pdsd2015:​~/​Laborator05/​labtasks$ git push Laborator05_perfectstudent master
 +</​code>​
 +
 +===== Resurse Utile =====
 +
 +[[http://​ptgmedia.pearsoncmg.com/​images/​9780321940261/​samplepages/​0321940261.pdf|Joseph ANNUZZI, Jr, Lauren DARCEY, Shane CONDER, Introduction to Android Application Development - Developer'​s Library, 4th Edition, Addison-Wesley,​ 2013]] - capitolul 8, subcapitolul //Using Container Control Classes//, capitolul 17, subcapitolul //​Menus//​\\ ​
 +[[http://​www.bignerdranch.com/​we-write/​android-programming/​|Bill PHILLIPS, Brian HARDY, Android Programming. The Big Nerd Ranch Guide, Pearson Technology Group, 2013]] - capitolele 9, 16\\ 
 +[[http://​www.amazon.com/​Professional-Android-4-Application-Development/​dp/​1118102274|Reto MEIER, Professional Android for Application Development,​ John Wiley & Sons, 2012]] - capitolele 4 (//The Android Widget Toolbok//, //​Introducing Adapters//​),​ 10 (//​Configuring Action Bar Icon Navigation Behavior//​)\\ ​
 +[[http://​books.google.ro/​books/​about/​The_Android_Developer_s_Cookbook.html?​id=Y4JR2yI2Fo0C&​redir_esc=y|Ronan SCHWARZ, Phil DUTSON, James STEELE, Nelson TO, Android Developer'​s Cookbook, Building Applications with the Android SDK, 2nd Edition, Addison Wesley, 2013]] - capitolele 6, 7\\ 
 +[[http://​eu.wiley.com/​WileyCDA/​WileyTitle/​productCd-1118199545.html|Wei Meng LEE, Beginning Android 4 Application Development,​ Wiley, 2012]]\\ ​
 +[[http://​www.apress.com/​9781430239307|Satya KOMATINENI, Dave MACLEAN, Pro Android 4, Apress, 2012]]\\ ​
 +[[http://​android.rosedu.org/​wiki/​|Dezvoltarea aplicațiilor pentru Android]]\\
 +[[http://​www.coreservlets.com/​android-tutorial|Android Programming Tutorials - Core Servlets]] - secțiunea Widgets II: Spinners (Combo Boxes)\\ ​
 +[[http://​www.vogella.com/​tutorials/​AndroidListView/​article.html|Using lists in Android (ListView) - Tutorial]]\\
 +[[http://​developer.android.com/​training/​basics/​network-ops/​xml.html|Parsing XML Data]]\\ ​
 +[[https://​developer.android.com/​training/​contacts-provider/​index.html|Accessing Contacts Data]]\\ ​
 +[[http://​android-er.blogspot.ro/​2013/​02/​query-contacts-database-using-loader.html|Query Contacts database using Loader, with Search Function]]\\ ​
 +[[http://​developer.android.com/​guide/​topics/​ui/​menus.html|Menus]]
laboratoare/laborator05_old.txt · Last modified: 2016/03/23 23:10 by Andrei Roșu-Cojocaru
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0