Liste

In aplicatiile realizate, un element important pe care il vom utiliza il reprezinta listele. Sub platforma Android, acestea sunt implementate folosind modelul MVC (Model View Controller). Modelul este reprezentat de datele ce trebuie afisate, View-ul este lista propriu-zisa si Controller-ul este programul care controleaza modul de afisare.

Model View Controller

MVC reprezintă un tipar de programare în care datele sunt separate de modul în care sunt reprezentate.

Modelul este cel care se ocupă de stocarea datelor, fără a cunoaște forma sub care ele sunt oferite utilizatorului. De afișaj se ocupă View-ul. Acesta stabilește dacă avem o listă sau un alt tip de View. În cazul în care este o listă, tot View-ul deține informații despre câte coloane sau pe câte nivele este structurată informația dintr-o intrare a listei. Modelul și View-ul nu comunică niciodată. Modelul nu știe cum va fi reprodusă
informația pe ecran, iar View-ul știe doar cum arată o listă, nu și informația care va popula lista. Legătura dintre aceste două elemente este realizată de Controller. El extrage din datele stocate de Model pe cele care vor fi afișare în View. De asemenea, la apelarea unui eveniment de pe
View, Controller-ul va hotărî cum se vor prelucra datele de către Model. Astfel se face diferențierea clară între stocarea datelor și afișarea lor.

ListView

Un ListView este un View care contine o lista de elemente, care poate fi parcursa. Un View de tip ListView poate fi plasat pe orice tip de activitate.Pentru a putea popula lista, se creează un string-array în fișierul strings.xml. Acesta se pasează ca parametru atributului android:entries, iar datele vor fi puse automat în listă.

<?xml version="1.0"?>
-<LinearLayout tools:context=".MainActivity" android:layout_height="match_parent"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<ListView android:layout_height="wrap_content" android:layout_width="wrap_content"
android:entries="@array/characters_array" android:id="@+id/lista"/>
</LinearLayout>
<?xml version="1.0" encoding="UTF-8"?>
-<resources>
<string name="app_name">Liste</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
-<string-array name="characters_array">
<item>Fred</item>
<item>Barney</item>
<item>Dino</item>
</string-array>
</resources>

Desi metoda este una foarte simplu de implementat, astfel se pot obține doar liste statice, care nu pot fi modificate în urma interacțiunii cu utilizatorul. Acestea sunt, de cele mai multe ori, inutile. Metoda care se folosește în majoritatea cazurilor implică un id pe care îl primește ListView-ul, cu ajutorul căruia se va lua o referință la View în codul Java.

ListActivity

Exista un mod mai simplu decat folosirea unui ListView. In general, pe o activitate exista o singura lista, astfel ca Android ne pune la dispozitie un tip special de activitate, numit ListActivity, care rezolva automat preluarea pointer-ului listei. In XML, trebuie declarat obligatoriu un ListView cu id-ul @android:id/list.
De asemenea, ListActivity pune la dispozitie urmatoarele functii:

// seteaza adaptorul pentru lista de pe fereastra
void setListAdapter (ListAdapter adapter);
 
// intoarce adaptorul listei de pe fereastra
ListAdapter getListAdapter ();
 
// intoarce un pointer catre lista din fereastra
ListView getListView ();

O alta functie oferita de catre ListActivity este onListItemClick(). Acesta este apelata automat de fiecare data cand se executa un click asupra unui element din lista. Initial functia este vida, in alte cuvinte, nu face nimic. Pentru a-i adauga o actiune, programatorul trebuie sa suprascrie acesta functie.

@Override
    public void onListItemClick (ListView list, View v, int position, long id)
    {
        // list - este lista de pe fereastra
        // v - este elementul din lista pe care s-a dat click
        // position - este pozitia pe care s-a dat click (de fapt pozitia la care se afla elementul v in lista)
        // id - este id-ul elementului (obtinut prin functia getItemId () a adaptorului)
    }

ListAdapter

In Android, lista este de fapt un ScrollView vertical. Sarcina programatorului este de fapt crearea componentelor View pentru fiecare linie din acest ScrollView, adica crearea unui element pe ecran pentru fiecare element pe care dorim sa il introducem in lista. In alte cuvinte, daca ne uitam la poza de mai sus, ceea ce trebuie noi sa facem este sa scriem componenta Adapter. Din punct de vedere ar programarii, asta inseamna crearea unui obiect care sa implementeze interfata ListAdapter.
Biblioteciile Android ne pun la dispozitie mai multe variante de a implementa ListAdapter.

ArrayAdapter

Pentru liste simple, ce contin elemente cu o singura linie de text se poate folosi o clasa mult simplificata, si anume ArrayAdapter. Acesta presupune ca toate elementele sunt stocate intr-un sir (ex: Object[]) sau o lista (orice obiect ce implementeaza interfata List<type>).
Deoarece fiecare element din lista este o linie de text si in sir sau lista noi pastram obiecte, adaptorul va apela functia toString() pe fiecare obiect.
Un exemplu de folosire este urmatorul:

class Personaj
{
    public String nume;
    public String desen;
 
    @Override
    public String toString ()
    {
        // acesta functie este apelata de catre ArrayAdapter pentru a transforma obiectul intr-un String ce
        // sa fie afisat in lista
        return nume+" din desenul animat "+desen;
    } 
}
 
public class ListaDeseneAnimate extends ListActivity
{
    ArrayList<Personaj> personaje;
    ArrayAdapter<Personaj> adapter;
 
    @Override
    public void onCreate (Bundle savedInstanceBundle)
    {
    	super.onCreate (savedInstanceBundle);
        personaje = new ArrayList<Personaj>();
        adapter = new ArrayAdapter<Personaj>(this, android.R.layout.simple_list_item_1, personaje);
        setContentView (R.layout.main);
        setListAdapter (adapter);
 
        // adaugam cateva personaje in lista
        adaugaFunnyGuy("Bugs Bunny", "Looney Toons");
        adaugaFunnyGuy("Fred Flinstone", "The Flinstones");
        adaugaFunnyGuy("Betty Rubble", "The Flinstones");
    }
 
    @Override
    public void onListItemClick (ListView list, View v, int position, long id)
    {
        // afisam numele personajului pe care s-a dat click folosind un Toast
        Toast.makeText(ListaDeseneAnimate.this, personaje.get(position).nume, Toast.LENGTH_LONG).show();
    }
 
    private void adaugaFunnyGuy (String nume, String desen)
    {
        Personaj p = new Personaj ();
        p.nume = nume;
        p.desen = desen;
        personaje.add (p);
        // acesta functie determina adaptorul sa ceara listei sa reafiseze continutul
        adapter.notifyDataSetChanged();
    }
}

Exercitii

  1. Realizati o lista (editabila) cu personaje din desene animate. Pe activitate veti avea posibilitatea de a adauga un personaj in lista. Stergerea se va face prin apasarea lunga (LongTouch sau LongClick) pe un element din lista.

BaseAdapter

Pentru o lista ce trebuie sa afiseze informatii mai complexe decat un rand de text, trebuie sa scriem un adaptor (o clasa ce implmenteaza interfata ListAdapter). Pentru ca interfata ListAdapter are foarte multe metode, un mod mai simplu este sa extindem clasa BaseAdapter. De fapt trebuie sa implementam urmatoarele patru functii:

class MyAdapter extends BaseAdapter
{
	@Override
	public View getView (int position, View convertView, ViewGroup list) 
	{
		// functia trebuie sa intoarca view-ul de pe pozitia position din lista
		// convertView este un element din lista ce nu mai este vizibil si poate fi convertit
	}
 
	@Override
        public int getCount ()
        {
		// intoarce nr de elemente din lista
	}
 
	@Override
	public Object getItem(int position) 
	{
		// intoarce elementul de pe pozitia position din model
	}
 
	@Override
	public long getItemId(int position) 
	{
		// fiecare element din lista poate avea un id, nu este insa obligatoriu
	}
}
Pentru a exemplifica mai bine, vom modifica exemplul de mai sus, astfel incat sa afisam doua linii de text. Clasa Personaj ramane aceeasi ca in exemplul precedent.
Primul pas este sa realizam un nou fisier XML (separat) ce va descrie cum arata fiecare element grafic (View) din lista. Il vom plasa in res/layout/personaj.xml.
personaj.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical">
  	<TextView android:id="@+id/personaj_nume"
  	     android:layout_width="fill_parent"
  	     android:layout_height="wrap_content"
  	     android:textStyle="bold"/>
 
  	<TextView android:id="@+id/personaj_desen"
  	     android:layout_width="fill_parent"
  	     android:layout_height="wrap_content"/>
</LinearLayout>

Pentru a exemplifica mai bine, vom modifica exemplul de mai sus, astfel încât să afișăm două linii de text. Clasa Personaj rămâne aceeași ca în exemplul precedent.

Primul pas este realizarea unui nou fișier xml (separat de cel existent deja și care nu se modifică) ce va descrie cum arată fiecare element grafic (View) din listă. Îl vom plasa în res/layout/personaj.xml.

Exercitii

  1. Realizati noul fisier xml care contine doua elemente de tip TextView, primul avant id-ul personaj_nume, iar cel de-al doilea, personaj_desen.

Urmeaza sa realizam fisierul cu codul sursa. Acesta va contine o clasa ce extinde BaseAdapter si clasa activitatii (ListActivity).

class Personaj
{
    public String nume;
    public String desen;
 
    @Override
    public String toString ()
    {
        // acesta functie este apelata de catre ArrayAdapter pentru a transforma obiectul intr-un String ce
        // sa fie afisat in lista
        return nume+" din desenul animat "+desen;
    } 
}
 
class PersonajeAdapter extends BaseAdapter
{
	private Activity context;
	ArrayList<Personaj> personaje;
 
	public PersonajeAdapter (Activity _context)
	{
		this.context = _context;
		personaje = new ArrayList<Personaj>();
	}
 
	@Override
	public View getView (int position, View convertView, ViewGroup list) 
	{
		// functia trebuie sa intoarca view-ul de pe pozitia position din lista
		// convertView este un element din lista ce nu mai este vizibil si poate fi convertit
		View element;
		LayoutInflater inflater = context.getLayoutInflater();
		element = inflater.inflate(R.layout.personaj, null);
 
		TextView nume = (TextView)element.findViewById(R.id.personaj_nume);
		TextView desen = (TextView)element.findViewById(R.id.personaj_desen);
 
		nume.setText(personaje.get(position).nume);
		desen.setText(personaje.get(position).desen);
 
		return element;
	}
 
	@Override
        public int getCount ()
        {
		// intoarce nr de elemente din lista
                return personaje.size ();
	}
 
	@Override
	public Object getItem(int position) 
	{
		// intoarce elementul de pe pozitia position din model
		return personaje.get(position);
	}
 
	@Override
	public long getItemId(int position) 
	{
		// fiecare element din lista poate avea un id, nu este insa obligatoriu
		return 0;
	}
 
        public void adaugaFunnyGuy (String nume, String desen)
        {
              Personaj p = new Personaj ();
              p.nume = nume;
              p.desen = desen;
              personaje.add (p);
              // acesta functie determina adaptorul sa ceara listei sa reafiseze continutul
              this.notifyDataSetChanged();
        }
}
 
public class ListaDeseneAnimate extends ListActivity
{
    // ArrayList<Personaj> personaje; -> mutat in PersonajeAdapter
    PersonajeAdapter adapter;
 
    @Override
    public void onCreate (Bundle savedInstanceBundle)
    {
    	super.onCreate (savedInstanceBundle);
        // personaje = new ArrayList<Personaj>(); -> mutat in Personaje Adapter
        adapter = new PersonajeAdapter (this);
        setContentView (R.layout.main);
        setListAdapter (adapter);
 
        // adaugam cateva personaje in lista
        adapter.adaugaFunnyGuy("Bugs Bunny", "Looney Toons");
        adapter.adaugaFunnyGuy("Fred Flinstone", "The Flinstones");
        adapter.adaugaFunnyGuy("Betty Rubble", "The Flinstones");
 
        // pentru a seta actiunea click lung
        getListView().setOnItemLongClickListener(new OnItemLongClickListener() 
		{
 
			public boolean onItemLongClick(AdapterView<?> listAdapter, View view,
					int position, long id) 
			{
                                Personaj p = (Personaj)adapter.getItem (position);
                                Toast.makeText(this, "click lung pe "+p.nume, Toast.LENGTH_LONG).show();
				return true;
			}
		});
    }
 
    @Override
    public void onListItemClick (ListView list, View v, int position, long id)
    {
        // afisam numele personajului pe care s-a dat click folosind un Toast
        Personaj p = (Personaj)adapter.getItem (position);
        Toast.makeText(this, p.nume, Toast.LENGTH_LONG).show();
    }
}

Pentru a înțelege mai bine modul de funcționare a clasei BaseAdapter, putem să ne imaginăm că totul este structurat sub forma unei structuri for care parcurge cele n elemente aflate în listă și le afisează pe rând:

//variabila adapter reprezinta adaptorul care este pasat ca parametru
//ListView-ului prin metoda setListAdapter() ce este apelata la desenarea view-ului
//n reprezinta numarul de elemente din lista
n = adapter.getCount();
for(int i=0; i<n; i++)
{
View v = adapter.getView(i,null, listView);
//view-ul intors este afisat in lista
}

Sunt importante urmatoarele observatii:

  • modelul (ArrayList<Personaj>) a fost mutat in adaptor. Acesta este de fapt legatura intre lista si date, deci el este cel care trebuie sa cunoasca datele, si nu fereastra
  • implementarea functiei getView() nu este eficienta, nu este folosit parametrul convertView.
  • setarea actiunii pentru click-ul lung se face direct pe lista, nu exista o functie in ListActivity ce sa poata fi suprascrisa.

ConvertView

Implementarea eficienta presupune folosirea parametrului convertView. Acesta este fie null, caz in care trebuie ignorat, fie un obiect intors aterior de catre getView(), insa obiect care nu mai este vizibil. Ideea este ca, in loc de a crea un nou View de fiecare data, sa se refoloseasca View-urile create anterior si care nu mai sunt vizibile. View-urile create deja sunt populate cu alte date și sunt reafișate.

@Override
	public View getView (int position, View convertView, ViewGroup list) 
	{
		// functia trebuie sa intoarca view-ul de pe pozitia position din lista
		// convertView este un element din lista ce nu mai este vizibil si poate fi convertit
		View element;
                if (convertView == null)
                {
		         LayoutInflater inflater = context.getLayoutInflater();
		         element = inflater.inflate(R.layout.personaj, null);
                }
                else element = convertView;
 
		TextView nume = (TextView)element.findViewById(R.id.personaj_nume);
		TextView desen = (TextView)element.findViewById(R.id.personaj_desen);
 
		nume.setText(personaje.get(position).nume);
		desen.setText(personaje.get(position).desen);
 
		return element;
	}

Exercitii

  1. Realizati o lista mai complexa pentru afisarea personajelor din desene animate. Pe fereastra veti avea posibilitatea de a introduce un nou personaj. Nu folositi parametrul convertView! Fiecare personaj contine:
  • Nume
  • Desenul animat din care face parte
  • O poza
  1. Modificati problema precedenta, folosind convertView, insa setati valorile View-urilor doar la creare. (In alte cuvinte, daca convertView != null, pur si simplu il intoarceti, fara sa il modificati.) Observati ce se intampla.
  2. Modificati problema precedenta, folosind corespunzator convertView.
  3. Optimizati lista de la exercitiul precedent, folosind tag-ul View-urilor.
  4. Modificati lista astfel incat pentru elementele pare imaginea sa fie situata in dreapta, iar pentru cele impare sa fie situata in stanga.
  5. Modificati aplicatia realizata astfel incat, in loc sa aveti in aceasi activitate interfata de adaugare la lista, sa deschideti o activitate noua unde utilizatorul sa introduca datele necesare.
  6. In aceasta a doua activitate includeti si un buton de back
  7. In activitatea principala, atunci cand utilizatorul apasa pe unul din item-uri deschideti intr-un browser pagina desenului animat respectiv.

Bonus

  1. Permiteti utilizatorului sa incarce din a doua activitate o imagine pentru desenul respectiv.
programare_android15/curs/curs3.txt · Ultima modificare: 2015/08/10 11:15 de către ioana.culic
CC Attribution-Noncommercial-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0