Autore Topic: [facile] Visualizzare gli oggetti di una ListView in gruppi omogenei con titolo  (Letto 13395 volte)

Offline Pinabello

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: +12
    • Mostra profilo
  • Dispositivo Android:
    Htc Dream Tim
  • Sistema operativo:
    Mac os X
Livello di difficoltà: facile
Versione SDK utilizzata: 1.6
Link al file compresso del progetto eclipse: file in allegato

Premetto di non essere un esperto riguardo alla programmazione su Android, e se scriverò qualche incorrettezza questo tutorial sarà un'occasione di crescita prima di tutto per me   :).

Per rendere più pulita e ordinata la visualizzazione delle liste in android è molto elegante l'uso degli header, in pratica si tratta di suddividere i dati della lista in gruppi omogeni rispetto ad una caratteristica comune ed inserire un titolo per ogni gruppo (come i contatti suddivisi per lettera in ordine alfabetico).
Eseguendo ricerche sull'implementazione di tale funzioni energono due strade possibili :

  • Utilizzo di diversi adapter : praticamente si tratta di separare l'adapter che gestisce i dati della lista in più adapter già suddivisi in gruppi omogenei e utilizzare il metodo setHeader() delle ListView prima di aggiungere ogni adapter alla lista.
    Questa soluzione non mi è sembrata molto pratica, sopratutto nel caso in cui si volesse utilizzare un CursorAdapter.
  • Utilizzare un layout custom per gli item : in questa soluzione si crea un layout custom per gli item della lista inserendo una TextView che conterrà il titolo. Tale TextView è di default invisibile e verrà visualizzata solo dove necessario.

Questo tutorial si preffigge di illustrare un'implementazione della seconda opzione.
In pratica si vuole ottenere un'ipotetica lista della spesa in cui i vari oggetti da comperare siano divisi per tipo :



Sorgenti:

1) Creazione Custom List Item simple_list_item.xml

Per prima cosa dobbiamo realizzare un custom layout che conterrà i dati da visualizzare nella lista e l'header nascosto :

Codice (XML): [Seleziona]
...
<TextView android:id="@+id/simple_header" android:textStyle="bold"
        android:textColor="#000"        android:textColorHighlight="#000"
        android:background="#DDD"       android:layout_centerInParent="true"
        android:visibility="gone"       android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
<TextView android:id="@+id/list_item_name" style="@style/listItemTitle"
        android:text="name" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
...

Il layout contiene due TextView :
  • simple_header    : conterrà il titolo del gruppo e sarà visibilile solo dove necessario, si noti android:visibility="gone" che non solo nasconde la TextView, ma non le fa occupare spazio nel layout.
  • list_item_name   : conterrà il nome dell'oggetto e sarà sempre visibile.

2) Creazione del database

In questo tutorial si utilizzerà un database gestito dalla classe it.pinabello.android.tutorial.SimpleHeaderTutorialDB che si occupa di creare una tabella SHOP_LIST per contenere i dati della lista.

Struttura tabella SHOP_LIST

Nome campoDescrizione
_idId autoincrementale
NAMEnome dell'oggetto
TYPEcategoria dell'item

Esempio di dati

NAMETYPE
CaroteV
RucolaV
AranceF
MandariniF
BananeF
SaponeC
StracciC
PaneA

Quindi quello che si vuole ottenere è suddividere la lista per valori omogenei del campo TYPE

4) Creazione Custom Adapter SimpleHeadersCursorAdapter

Per modificare il comportamento standard della classe ListView è necessario utilizzare un Custom Adapter, visto che questo tutorial utilizza un cursore per ottenere i dati la nostra classe adapter estende SimpleCursorAdapter.
L'unico metodo che è necessario sovrascrivere è getView(...) dove viene implementata la logica di visualizzazione dei titoli.

Codice (Java): [Seleziona]
...
/*
 * @param headers       :       hashmap che contiene come chiavi i possibili valori del campo da usare per
 *                              la suddivisione (A,C,F,V, per questo tutorial) e come value la descrizione da
 *                              visualizzare nell'header.
 * @param fieldName     :       nome del campo presente nel cursore da usare per la suddivisione
 *                              (TYPE per questo tutorial)
 * @param headerId      :       id della TextView del custom layout utilizzato per la lista
 *                              (R.id.simple_header per questo tutorial)
 */

public SimpleHeadersCursorAdapter(Context arg0, int arg1, Cursor cursor, String[] arg3, int[] arg4,
                                        HashMap<String, String> headers, String fieldName, int headerId) {
        super(arg0, arg1, cursor, arg3, arg4);
        this.headers    = headers;
        this.cursor     = cursor;
        this.fieldIndex = cursor.getColumnIndex(fieldName); // Ottengo l'indice del campo del cursore
        this.headerId   = headerId;
}


@Override
public View getView(int position, View convertView, ViewGroup parent) {
        cursor.moveToPosition(position);
        /*
         * L'hashmap headersIndex viene valorizzato con la coppia
         *      chiave/valore "tipo/posizione header da visualizzare"
         * se il tipo non è presente come chiave nell'hashmap significa che stiamo incontrando il primo
         * oggetto di questa categoria e che a questa posizione è necessario rendere visibile il titolo.
         * Per tutti gli altri oggetti della stessa categoria la condizione sarà false e il titolo non verrà
         * visualizzato.
         * Perchè questa logica funzioni correttamente il cursore deve essere ordinato per categoria.
         */

        if (!headersIndex.containsKey(cursor.getString(fieldIndex))) {
                headersIndex.put(cursor.getString(fieldIndex), position);
        }
        View            view            = super.getView(position, convertView, parent);
        // Ottengo la TestView del titolo
        TextView        textView        = (TextView) view.findViewById(headerId);
        /*
         * Se questa posizione è contenuta nell'HashMap headersIndex il titolo deve essere visibile
         */

        if (headersIndex.containsValue(position)) {
                // Il titolo viene reso visibile
                textView.setVisibility(View.VISIBLE);
                // Viene reperita la descrizione del titolo e settata nella TextView
                textView.setText(headers.get(cursor.getString(fieldIndex)));
        } else {
                // Il titolo viene reso nascosto
                textView.setVisibility(View.GONE);
                // Reset del titolo
                textView.setText("");
        }
        return view;
}

/*
 * Il metodo refreshHeaders va richiamato nel caso in cui vengano eliminati o aggiunti oggetti
 * alla lista, in quanto svuotanto l'hashMap si richiede un nuovo calcolo della posizione dei titoli
 */

public void refreshHeaders() {
        headersIndex = new HashMap<String, Integer>();
}

...

Come si può vedere dal codice nel metodo getView(...) viene creato un'HashMap che contiene le posizioni in cui visualizzare i titoli e successivamente viene ottenuta la View corrente dove, se la posizione della View è presente nell'HashMap, il titolo viene reso visibile.

4) Creazione ListActivity  SimpleListActivity

La classe SimpleListActivity si occupa di utilizzare tutte le componenti discusse e visualizzare la lista.

Codice (Java): [Seleziona]
...
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Reperimento riferimento al database.
        SimpleHeaderTutorialDB  simpleHeaderTutorialDB  = new SimpleHeaderTutorialDB(this);
        SQLiteDatabase          database                = simpleHeaderTutorialDB.getReadableDatabase();
        /*
         * Esecuzione query si noti il valore "Spesa.TYPE + ", " + Spesa.NAME" che esegue l'ordinamento
         * del cursore prima per tipo e poi per nome
         */

        Cursor cursor = database.query(Spesa.TABLE_NAME, new String[] {
                                Spesa.ID, Spesa.NAME, Spesa.TYPE }, null, null, null, null,
                                Spesa.TYPE + ", " + Spesa.NAME);
        startManagingCursor(cursor);
        /*
         * HashMap che contiene le coppie tipo/descrizione, per tali valori sarebbe più corretto utilizzare
         * il file values/array.xml, per semplicità in questo tutorial viene gestito quì.
         */

        HashMap<String, String> hash = new HashMap<String, String>();
        hash.put("V", "Verdure");
        hash.put("F", "Frutta");
        hash.put("A", "Altro");
        hash.put("V", "Verdure");
        hash.put("C", "Casalinghi");
        /*
         * Creazione SimpleHeadersCursorAdapter si noti in particolare :
         * R.layout.simple_list_item    : custom item layout
         * hash                         : HashMap tipo /descrizione appena creato
         * Spesa.TYPE                   : nome campo del cursore che contiene la categoria
         * R.id.simple_header           : id del TextView che contiene il titolo
         */

        CursorAdapter adapter = new SimpleHeadersCursorAdapter(this,
                        R.layout.simple_list_item, cursor, new String[] { Spesa.NAME },
                        new int[] { R.id.list_item_name }, hash, Spesa.TYPE,
                        R.id.simple_header);
        setListAdapter(adapter);
    }
...
« Ultima modifica: 29 Marzo 2010, 10:48:51 CEST da Pinaz »

Offline JD

  • Amministratore
  • Utente storico
  • *****
  • Post: 1600
  • Respect: +232
    • leinardi
    • Mostra profilo
  • Dispositivo Android:
    LG Nexus 5
  • Sistema operativo:
    L'ultima Ubuntu
Beh, che dire, questo è il più bel post di presentazione che abbia mai visto :D
Seguissero tutti il tuo esempio saremmo a cavallo ;)

Battute a parte, complimenti per il Tutorial. Sarà sicuramente molto utile e l'hai realizzato in maniera impeccabile.

Un saluto
È stata trovata una soluzione al tuo problema?
Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato ;).
E se hai aperto tu il thread marcalo come risolto cliccando !

Offline Pinabello

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: +12
    • Mostra profilo
  • Dispositivo Android:
    Htc Dream Tim
  • Sistema operativo:
    Mac os X
 :D Grazie,
tempo permettendo, mi piacerebbe diventare un membro moto attivo di questa community   ;)

Offline ciso

  • Utente junior
  • **
  • Post: 71
  • Respect: +4
    • Mostra profilo
    • AndroidWorld.it
  • Dispositivo Android:
    Motorola Milestone
  • Sistema operativo:
    Ubuntu 10.04
Complimenti davvero, una guida davvero ben scritta :)

Offline Vytek

  • Translate Team
  • Utente junior
  • **
  • Post: 125
  • Respect: +6
    • Mostra profilo
  • Dispositivo Android:
    Samsung S5
  • Sistema operativo:
    Windows 8.1
0
Salve a tutti,
partendo da questo tutorial sto cercando di creare una piccola applicazione d'esempio da condividere con tutti voi.
Se ad esempio volessi fare in modo che al click con il singolo item venga fuori un messaggui con il numero selezionato posso aggiungere questo codice (preso da: http://www.tutorialforandroid.com/2009/01/clicking-items-in-listactivity.html):

Codice (Java): [Seleziona]
@Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
                alertDialog = new AlertDialog.Builder(this).create();
            alertDialog.setTitle("Alert 1");
            alertDialog.setMessage("You just clicked an item position #" + String.valueOf(position));
            alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                return;
            } });
        alertDialog.show();
                super.onListItemClick(l, v, position, id);
        }

Dico bene? E se volessi ricavare il contenuto dell'item selezionato?

Un saluto e grazie di tutto...

Offline Pinabello

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: +12
    • Mostra profilo
  • Dispositivo Android:
    Htc Dream Tim
  • Sistema operativo:
    Mac os X
0
Esatto.
Per aggiornare la ListActivity a seguito, ad esempio, di una modifica sul database, è sufficiente utilizzare il metodo requery() del cursor utilizzato dall'adapter dell'activity.
Per quanto riguarda la gestione delle dialog ti consiglio di utilizzare i metodi onPrepareDialog(int id, Dialog dialog), onCreateDialog(int id)
che permettono di ottenere una getione più ordinata e efficente  ;-)

http://developer.android.com/intl/it/guide/topics/ui/dialogs.html

Offline cotrariello

  • Nuovo arrivato
  • *
  • Post: 2
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu
0
Ciao a tutti

in riferimento agli eventi collegabili alla listview, come faccio a catturarli? come scateno l'evento onListItemClick?

Offline Pinabello

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: +12
    • Mostra profilo
  • Dispositivo Android:
    Htc Dream Tim
  • Sistema operativo:
    Mac os X
0
Dipende dal layout dell'item e da quello che  vuoi ottenere, nel caso più semplice puoi usare il metodo setOnItemClickListener della classe ListView.

Offline cotrariello

  • Nuovo arrivato
  • *
  • Post: 2
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu
0
si avevo capito di utilizzare il metodo setOnItemClickListener(), ma non avevo capito come utilizzarlo, adesso l'ho capito e lo posto nel caso possa servire


Codice (Java): [Seleziona]
l.setOnItemClickListener(new AdapterView.OnItemClickListener() {

                        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                                        long arg3) {
                                Toast t = Toast.makeText(getApplicationContext(), "Cliccato", Toast.LENGTH_LONG);
                t.show();
                               
                        }
                });
« Ultima modifica: 11 Luglio 2010, 17:16:54 CEST da JD, Reason: Usate i bbcode java e xml!! xD »

Offline giumazzi

  • Utente junior
  • **
  • Post: 54
  • Respect: +5
    • Mostra profilo
  • Dispositivo Android:
    acer liquid - android 2.1
  • Sistema operativo:
    windows xp - linux xubuntu - easy peasy
0
Una domanda da neofita.
Se volessi visualizzare semplicemente l'intera tabella (quindi senza utilizzare HashMap), ed eventualmente fare delle query sulla stessa, come dovrei agire?

Offline giumazzi

  • Utente junior
  • **
  • Post: 54
  • Respect: +5
    • Mostra profilo
  • Dispositivo Android:
    acer liquid - android 2.1
  • Sistema operativo:
    windows xp - linux xubuntu - easy peasy
Re:[facile] Visualizzare gli oggetti di una ListView in gruppi omogenei con titolo
« Risposta #10 il: 01 Settembre 2010, 21:56:01 CEST »
0
Ritorno sulla domanda del precedente post allegando l'adattamento effettuato sul progetto di Pinabello.
L'adattamento consiste nell'aver inserito una tabella db contenente più campi.
La mia esigenza è quella di visualizzare nella list view TUTTI i CAMPI senza riferimento alla HashMap.
Ho provato, da neofita, a smanettare sul SimpleHeaderCursorAdapter e sulla SimpleListActivity per trovare la soluzione, senza successo.
Nessuno è in grado di suggerirmi la saluzione?
Grazie

Offline Pinabello

  • Nuovo arrivato
  • *
  • Post: 22
  • Respect: +12
    • Mostra profilo
  • Dispositivo Android:
    Htc Dream Tim
  • Sistema operativo:
    Mac os X
Re:[facile] Visualizzare gli oggetti di una ListView in gruppi omogenei con titolo
« Risposta #11 il: 02 Settembre 2010, 11:37:08 CEST »
0
Se ho capito bene tu vorresti, ad esempio, visualizzare accanto al tipo do frutta altri campi tipo prezzo e provenienza?

Mele 1 Italia
Pere 2 Francia

così?

Offline giumazzi

  • Utente junior
  • **
  • Post: 54
  • Respect: +5
    • Mostra profilo
  • Dispositivo Android:
    acer liquid - android 2.1
  • Sistema operativo:
    windows xp - linux xubuntu - easy peasy
Re:[facile] Visualizzare gli oggetti di una ListView in gruppi omogenei con tito
« Risposta #12 il: 02 Settembre 2010, 12:00:53 CEST »
0
Non proprio.
Mi spiego meglio. La mia esigenza è quella di visualizzare tutti i campi della tabella.
Ero partito dal tuo tutorial per importare i dati di un file csv, in quanto mi trovavo nella stessa situazione. Ma non mi interessa visualizzarli in gruppi omogenei con la hashmap ma con una più semplice ListView contenente tutti i campi senza riferimento al tipo di "verdura".

Per questo ho tentato di "modificare" SimpleHeaderCursorAdapter e SimpleListActivity per vedere se ero capace di eleminare il riferimento alla hashmap, senza successo.

Probabilmente va creato un CursorAdapter diverso ma le mie conoscenze java/android non mi consento di realizzare la modifica.

Offline Trigun

  • Utente normale
  • ***
  • Post: 183
  • Respect: +4
    • Mostra profilo
Re:[facile] Visualizzare gli oggetti di una ListView in gruppi omogenei con titolo
« Risposta #13 il: 13 Maggio 2011, 20:30:46 CEST »
0
ritiro su questo topic dal limbo :)

io ho fatto 1 cosa molto simile solo che ho usato una expandedlistview che praticamente fa questo lavoro di default :)
ora pero' stavo cercando di fare un'altra listview che conteneva tutti i vari gruppi e usar questa lista come 1 specie di bookmark
in modo che se premo su Verdure mi scrolli automaticamente su verdure nella lista principale (ho messo che quando e' in landscape escono le 2 liste affiancate e quindi ha senso sta cosa :P)

il problema e' dovuto dal fatto che non so come far a sapere la posizione del tab Verdure...

praticamente la mia situazione attuale e' quella nell'img

e se premo sulla lista laterale ovviamente non funziona...


l'ordine sballato e' dovuto ad 1 query sql... non fateci caso :)

Offline mrfalco

  • Utente junior
  • **
  • Post: 81
  • Respect: +2
    • Mostra profilo
    • www.falcodomingo.it
  • Dispositivo Android:
    Samsung Galazy S
  • Sistema operativo:
    Windows Vista
Re:[facile] Visualizzare gli oggetti di una ListView in gruppi omogenei con titolo
« Risposta #14 il: 24 Giugno 2011, 18:56:01 CEST »
0
Ciao bello il tuo tutorial, ma volevo chiederti se era possibile farlo su di un SimpleAdapter e non SimpleCursorAdapter io utilizzo il SimpleAdapter già per le liste e volevo implementare le section.

Grazie