L'utilità delle activity, sia per la presentazione dei dati che per l'interazione con l'utente, è stata più volte messa in evidenza nei precedenti articoli. Android disegna i componenti di una schermata attraverso oggetti della classe
View
, raccolti all'interno di oggetti ViewGroup
(che possono raccogliere, a loro volta, altri oggetti View
e ViewGroup
). Esistono diverse specializzazioni degli oggetti detti sopra. View
è la classe base per i widgets, così vengono chiamati gli elementi dell'interfaccia grafica. Alcune specializzazioni della classe View
sono: la classe Button
(per i bottoni), la classe TextView
(per il testo), la classe ImageView
(per le immagini). ViewGroup
, invece, è la classe base per i layout, gli schemi che descrivono la disposizione degli oggetti sullo schermo (le activity). Alcune specializzazioni della classe ViewGroup
sono: la classe LinearLayout
(per un layout lineare), la classe ListView
(per un layout a elenco), la classe TableLayout
(per un layout tabellare). Le specializzazioni dette sopra ereditano dalle rispettive classi padre numerosi metodi e costanti. Pertanto, durante la scrittura del codice, vi invito a consultare la documentazione in linea se siete alla ricerca di metodi che non trovate documentati all'interno della classe!La presentazione di informazioni all'interno di un'activity, per alcune applicazioni, è spesso legata a una sorgente di dati. In tal caso è particolarmente importante collegare quest'ultima ai meccanismi che hanno il compito di presentare le informazioni all'interno degli oggetti
ViewGroup
dell'activity.Adapter
è l'interfaccia pubblica usata da Android per accedere a una sorgente di dati e istanziare oggetti View
per ogni record. In questa interfaccia, pertanto, sono raccolte le firme dei metodi che permettono di gestire le azioni dette sopra. Per l'accesso a un record, ad esempio, esistono i metodi:public abstrac Obejct getItem(int position)
: che ritorna i dati di un record nella posizione indicata daposition
;public abstract long getItemId(int position)
: che ritorna la chiave del record nella posizione indicata daposition
;
Per il conteggio dei record, invece, l'interfaccia prevede i metodi:
public abstract getCount()
: che ritorna il numero di record contenuti nell'oggetto Adapter;public abstract boolean isEmpty()
: che ritornatrue
se l'oggettoAdapter
non ha record, altrimentifalse
;
Il riferimento a un oggetto
View
, che rappresenta un record dell'oggetto istanziato da Adapter, viene ottenuto con il metodo:public abstract View getView(int position, View convertView, ViewGroup parent)
: doveposition
indica la posizione del record all'interno della lista,convertView
è il riferimento a una precedente View non più visibile (a causa dello scrolling e quindi riutilizzabile) eparent
, infine, è il riferimento all'oggettoViewGroup
che contiene le singoleView
dei record;
ViewGroup
che ne permette la visualizzazione sul display va dunque aggiornato. A tale proposito l'interfaccia Adapter prevede i seguenti metodi per la gestione del suddettoevento:public abstract void registereDataSetObserver(DataSetObserver observer)
: che registra un oggettoDataSetOberver
come ascoltatore che verrà chiamato non appena si verifica una modifica ai dati;public abstract void unregisterDataSetObserver(DataSetObserver observer)
: che cancella l'oggettoDataSetOberver
impostato dal metodo visto prima, rendendo l'oggettoViewGroup
insensibile alle modifiche applicate sui dati;
L'interfaccia prevede la firma di altri metodi, vi consiglio pertanto di consultare la documentazione in linea.
AdapterView
è la classe astratta che, estendendendo ViewGroup
, implementa i meccanismi per la comunicazione fra un oggetto ViewGroup
e un Adapter
. La disposizione delle singole View viene lasciata alle successive specializzazioni di AdapterView (come ad esempio ListView
, Gallery
e Spinner
). Un AdapterView
, riassumendo, si occupa di:- riempire il layout principale (dell'oggetto
ViewGroup
) con i dati, usando una specializzazione di Adapter (per accedere alla sorgente di dati); - gestire le interazioni dell'utente con i dati presentati;
- la classe
ListView
, che estendeViewGroup
(per il layout); - l'interfaccia
ListAdapter
che estende l'interfacciaAdapter
aggiungendo le firme dei metodi utili alla gestione dei dati organizzati secondo una lista scorrevole (i metodi aggiunti sono:isEnabled(int row)
, che ritornatrue
se l'elemento della lista è abilitato, altrimenti tornafalse
eareAllItemsEnabled()
, che ritornatrue
se tutti gli elementi della lista sono abilitati, altrimenti tornafalse
); - la classe
ArrayAdapter
implementa l'interfacciaListAdapter
(potete infatti verificare la presenza dei metodiisEnabled()
eareAllItemsEnabled()
all'intero della classe che effettivamente li implementa);
La classe
ArrayAdapter
, del package android.widget
, si interfaccia con un array di oggetti (la sorgente di dati) e istanzia per ogni record una View
. Esistono diversi costruttori di ArrayAdapter
, come ad esempio public ArrayAdapter (Context context, int resource, int textViewResourceId, T[] object)
, dove:context
è il riferimento a una risorsa o classe dell'applicazione;resource
è il riferimento al layout dell'oggettoViewGroup
;textViewResourceId
è il riferimento al layout che conterrà leView
da istanziare per ogni occorrenza dell'array di oggetti;T[]
è l'array di oggetti, quindi la sorgente di dati;
L'assegnazione dell'oggetto
ArrayAdapter
all'oggetto ListView
(il contenitore per le View) avviene con il metodo setAdapter(ListAdapter adapter)
. Vediamo con un esempio come applicare le cose fin qui dette.Supponiamo di voler realizzare un'applicazione in grado di gestire una lista di valori, le operazioni utili (per il momento) saranno svolte attraverso la pressione di due bottoni: uno per l'aggiunta di un nuovo elemento alla lista e uno per la cancellazione di tutti gli elementi della lista. Per l'aggiunta di un nuovo elemento, inoltre, sarà presente un'area di testo per l'input. Realizziamo con il seguente codice xml (file
main.xml
) il layout principale dell'applicazione:<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainLayout"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/labelForNewItem"
android:text="New item:"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
<EditText
android:id="@+id/newItem"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</EditText>
</LinearLayout>
<Button
android:id="@+id/addButton"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="Add item"
android:onClick="addItem">
</Button>
<Button
android:id="@+id/clearButton"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="Clear all items"
android:onClick="clearAll">
</Button>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewOfItems"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ListView>
</LinearLayout>
Dovendo in seguito specificare nel codice le azioni per gli eventi legati alla pressione di uno dei due bottoni ho deciso di utilizzare, per gli elementi Button, l'attributo
android:onClick
. La stringa riferita da questo attributo all'oggetto istanziato in fase di inflating ha l'importante compito di indicare allo stesso il metodo da invocare alla pressione della view (in questo caso il bottone). Tali metodi, dunque, vanno implementati all'interno della classe che descrive l'activity.Il layout principale mostrato sopra prevede infine un oggetto
ViewGroup
, in questo caso un oggetto ListView
. Sarà questo componente ad occuparsi delle View relative agli elementi aggiunti. Ogni singola View verrà presentata secondo uno schema descritto in un file xml a parte che per il nostro esempio è il file row.xml
:<?xml version="1.0" encoding="utf-8"?><LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/record"
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<ImageView
android:id="@+id/itemImage"
android:src="@drawable/item"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
</ImageView>
<TextView
android:id="@+id/itemLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="10px"
android:text="Information here...">
</TextView>
</LinearLayout>
Ogni nuovo elemento aggiunto sarà quindi descritto da un'immagine e da un testo a lato. Non ci resta che collegare tutto all'interno del codice. In questo esempio la sorgente di dati viene descritta con un oggetto della classe
ArrayList
(del package java.util
), i cui metodi add()
e clear()
permettono la gestione della lista costruita così come descritto già sopra. Tali metodi saranno allora invocati all'interno dei metodi addItem()
e clearAll()
dell'activity.package android.mylistview;
import java.util.ArrayList;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
public class ListViewActivity extends Activity {
private ArrayList<String> arrayOfItems;
private ArrayAdapter<String> arrayOfItemsAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
arrayOfItems=new ArrayList<String>();
ListView listView=(ListView)findViewById(R.id.viewOfItems);
arrayOfItemsAdapter=new ArrayAdapter<String>(this,R.layout.record,R.id.itemLabel,arrayOfItems);
listView.setAdapter(arrayOfItemsAdapter);
}
public void addItem(View v) {
EditText input=(EditText)findViewById(R.id.newItem);
String newItem=input.getText().toString();
if(newItem.trim().equals((String)""))
Toast.makeText(this,"Hey... i've a null item!",Toast.LENGTH_LONG).show();
else {
arrayOfItems.add(newItem);
arrayOfItemsAdapter.notifyDataSetChanged();
Toast.makeText(this,"New item in the list!",Toast.LENGTH_LONG).show();
input.setText("");
}
}
public void clearAll(View v) {
if(!arrayOfItems.isEmpty()) {
arrayOfItems.clear();
arrayOfItemsAdapter.notifyDataSetChanged();
Toast.makeText(this,"Bye bye old items!",Toast.LENGTH_LONG).show();
}
}
}
Tralasciando le fasi dedicate al setup del layout principale, all'interno del metodo onCreate() viene: creata la sorgente di dati per la lista di elementi; ottenuto un riferimento all'oggetto ListView; istanziato l'oggetto ArrayAdapter, fornendo i riferimenti ai layout (quello principale e quello di riga) e alla sorgente di dati; collegato l'oggetto
ArrayAdapter
all'oggetto ListView
. E' particolarmente importante, nei metodi add()
e clearAll()
, invocare sull'oggetto ArrayAdapter
il metodo notifyDataSetChanged()
per notificare un cambiamento nella sorgente di dati (l'aggiunta di un nuovo elemento oppure la cancellazzione dell'intera lista) e provocare l'aggionamento dell'oggetto ViewGroup
(ListView
ArrayAdapter nell'associazione fra elementi della sorgente di dati e il layout per la View. La stringa aggiunta viene legata di default a un elemento TextView
. Nessun problema, quindi, per l'esempio appena visto. Gli item aggiunti sono stringhe e una TextView è sufficiente a raccoglierli. Se l'informazione da aggiungere è strutturata diversamente occorre necessariamente sovrascrivere il metodo getView()
implementato nella classe ArrayAdapter
e personalizzare allora la View da restituire. Vedremo a breve come fare anche questo. Per adesso vi lascio lo screenshot dell'applicazione (provata sia sull'emulatore che sul mio dispositivo Android):Qui, trovate l'archivio compresso con il codice sorgente dell'applicazione. Attraverso il codice QR che segue potete, invece, installare l'applicazione sul vostro dispositivo Android:
Nessun commento:
Posta un commento