Autore Topic: [medio] Utilizzo dei Button nelle ListView con layout personalizzato  (Letto 13003 volte)

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 665
  • Respect: +143
    • Github
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Huawei P9 Lite
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
+8
Livello di difficoltà: medio
Versione SDK utilizzata: 2.1
Link al file compresso del progetto eclipse: file in allegato


E' indubbio che le ListView ricoprano un ruolo fondamentale nello sviluppo di applicazioni Android, tuttavia chi si cimenta nella realizzazione di ListView con layout personalizzato ed intende inserire elementi cliccabili al suo interno è destinato ad incorrere in numerosi grattacapi. A meno che non faccia tesoro della mia esperienza, che vado a proporre in questo tutorial.

Infatti le ListView soffrono di un bug "by design" che si presenta qualora le righe della lista contengano elementi cliccabili o selezionabili. Di seguito spiegherò come aggirare questo bug, applicando lo stesso metodo che gli sviluppatori di Android hanno applicato per realizzare l'elenco delle ultima chiamate nella rubrica relefonica.

Questo tutorial è basato sul lavoro di Ricky`, per cui se trovate riferimento ad alcune classi delle quali non specifico il codice, sicuramente le trovate nel suo ottimo tutorial.

Come da topic, supponiamo di voler aggiungere agli elementi della ListView definita nel succitato tutorial un bottone. Modifichiamo row_item.xml aggiungendo in coda la nuova View:

Codice (XML): [Seleziona]
        <Button
                android:id="@+id/showToast"
                android:layout_width="50dip"
                android:layout_height="50dip"
               
                android:layout_alignParentRight="true"
               
                android:drawableTop="@drawable/light"
        />

Modifichiamo anche PersonViewCache in modo che ci permetta di accedere a questo nuovo elemento:

Codice (Java): [Seleziona]
        private Button          buttonShowToast;

        public Button getButtonShowToast () {
                if ( buttonShowToast == null ) {
                        buttonShowToast = ( Button ) baseView.findViewById( R.id.showToast );
                }
                return buttonShowToast;
        }

Naturalmente modifichiamo anche PersonAdapter in modo da agganciare il bottone all'evento onClick, aggiungendo il seguente codice in fondo alla getView (ovviamente prima di return convertView;):

Codice (Java): [Seleziona]
                Button btShowToast = viewCache.getButtonShowToast();
                // Per praticità metto un riferimento a "Person" nel tag del bottone
                btShowToast.setTag(person);
                btShowToast.setOnClickListener(new OnClickListener() {
                        public void onClick(View view) {
                                Toast.makeText(
                                                view.getContext(),
                                                "Click sul bottone nella riga " + ((Person)view.getTag()).getFullName(),
                                                Toast.LENGTH_SHORT
                                                ).show();
                        }
                });

Per complicare le cose, e per comprendere meglio l'entità del bug citato, aggiungiamo un gestore di onClick sulla riga. In pratica vogliamo che succeda qualcosa se si clicca sul tasto inserito nella riga, qualcos'altro se si clicca sulla riga fuori dal tasto (proprio come avviene nell'elenco delle chiamate, nella rubrica di Android). Modifichiamo dunque Listview e nel metodo onCreate aggiungiamo, prima di new BackgroundWorker().execute();, il seguente codice:

Codice (Java): [Seleziona]
                listView.setOnItemClickListener(new OnItemClickListener() {
                        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                                Person p = (Person) parent.getItemAtPosition(position);
                                Toast.makeText(
                                                view.getContext(),
                                                "Click sulla riga " + p.getFullName(),
                                                Toast.LENGTH_SHORT
                                                ).show();
                        }
                });

Quello che dovremmo ottenere ora, secondo logica, è il seguente comportamento: al click sulla riga, otteniamo un Toast che riporta Click sulla riga -nome-; al click sul bottone contenuto nella riga otteniamo invece un Toast che recita Click sul bottone nella riga -nome-.

Falso

A causa del succitato bug, il click sul bottone viene gestito correttamente, il click sulla riga non viene gestito affatto, perché la riga contiene un elemento selezionabile (focusable).

Per ovviare a questo problema potremmo aggiungere l'attributo android:focusable="false" nell'xml di definizione della riga. Risolveremmo il problema del click sulla riga, ma il bottone si comporterebbe in modo strano. Questo accade perché quando la riga viene cliccata, lo status (inteso come aspetto, non come evento) di "premuto" viene inoltrato a tutti gli elementi in essa contenuti, e quindi tutti vengono renderizzati come fossero premuti.

Tutti questi problemi possono essere aggirati creando una classe specifica da utilizzare nelle ListView:

Codice (Java): [Seleziona]
package it.anddev.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;

/**
 * Un bottone definito per essere utilizzato in una {@link ListView}.
 */

public class ListButton extends Button {

        /**
         * Costruttore di default, inizializza il bottone in base ad un contesto e ad un insieme di attributi.
         * @param context Contesto.
         * @param attrs Attributi della vista.
         */

        public ListButton(Context context, AttributeSet attrs) {
                super(context, attrs);
                //Rendo forzatamente il bottone non selezionabile.
                this.setFocusable(false);
        }

        /**
         * Imposta lo stato di pressione del bottone.
         * @param pressed Stato di pressione del bottone.
         */

        @Override
    public void setPressed(boolean pressed) {
            //Se il contenitore del bottone è premuto, allora non imposto anche il bottone a premuto
            if (pressed && ((View) getParent()).isPressed()) {
                    return;
            }
            //Se il contenitore non è premuto, allora è stato toccato il bottone. Procedo normalmente.
            super.setPressed(pressed);
    }
}

La nostra classe fa due cose: rende il bottone non selezionabile (quindi non è necessario specificare l'attributo android:focusable="false" nell'xml di definizione della riga), e fa assumere l'aspetto di "premuto" solo se è stato effettivamente premuto (e non se è stato premuto il suo genitore).

Una volta aggiunta questa classe è sufficiente cambiare il layout della riga e specificare il widget appena definito al posto di Button:

Codice (XML): [Seleziona]
        <it.anddev.widget.ListButton
                android:id="@+id/showToast"
                android:layout_width="50dip"
                android:layout_height="50dip"
               
                android:layout_alignParentRight="true"
               
                android:drawableTop="@drawable/light"
        />

Non è necessario modificare il resto del codice sostituendo ListButton a Button: essendo il primo derivato dal secondo possiamo trattarlo come un normale bottone senza preoccupazioni.

Lo stesso concetto può essere applicato ad ImageView e TextView, qualora si intenda renderle cliccabili.
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #1 il: 10 Ottobre 2010, 21:07:39 CEST »
0
Buono a sapersi!

grazie per il tutorial :)

Offline io_droid

  • Nuovo arrivato
  • *
  • Post: 13
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    wildFire
  • Sistema operativo:
    Win XP su macchina virtuale Vbox
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #2 il: 05 Dicembre 2010, 18:35:38 CET »
0
Bellissimo e Fantastico questo Tutorial ma come faccio ad andare ad una pagina di dettaglio ?
Ci sto provando in tutti i modi e non ne esco. ora tutto funziona premo il pulsante e so quale ID devo esplodere nel Layout di dettaglio.
ma non riesco ad adare. se uso

     setContentView(R.layout.dett);

non me lo fa fare dice che devo implementare un metodo ma che vuol dire?

Offline Nicola_D

  • Utente storico
  • *****
  • Post: 2479
  • SBAGLIATO!
  • Respect: +323
    • Github
    • Google+
    • nicoladorigatti
    • Mostra profilo
  • Dispositivo Android:
    Nexus 6p, Nexus 4, Nexus S, Nexus 7(2012)
  • Sistema operativo:
    Windows 7
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #3 il: 20 Dicembre 2010, 17:23:35 CET »
0
avevo letto il post ma non l'avevo sfruttato,oggi dovevo fare una list view con elementi stile facebook o twitter, quindi con parti cliccabili e mi è servito,sapevo che c'era! grande!
IMPORTANTE:NON RISPONDO A PROBLEMI VIA MESSAGGIO PRIVATO
LOGCAT: Non sai cos'è? -> Android Debug Bridge | Android Developers
               Dov'è in Eclipse? -> Window -> Open Prospective -> DDMS e guarda in basso!
[Obbligatorio] Logcat, questo sconosciuto! (Gruppo AndDev.it LOGTFO) - Android Developers Italia

Offline dottorm

  • Nuovo arrivato
  • *
  • Post: 18
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    LG Optimus One
  • Sistema operativo:
    Mac OS X
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #4 il: 01 Aprile 2011, 14:36:14 CEST »
0
Salve, e se volessi cambiare l'immagine al click sulla riga? ho usato questo tutorial ma non riesco in nessun modo...

[EDIT]
Ho risolto bastava usare

Codice (Java): [Seleziona]
adapter.notifyDataSetChanged();
« Ultima modifica: 01 Aprile 2011, 14:55:04 CEST da dottorm »

Offline slasher

  • Nuovo arrivato
  • *
  • Post: 37
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Emulatore ADT :-D
  • Sistema operativo:
    Windows Vista (purtroppo) \ Linux Ubuntu
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #5 il: 30 Aprile 2011, 18:51:06 CEST »
0
è possibile che ci sia un bug anche se nella mia listview è contenuto un CheckedTextView?
Mi spiego: la mia list view è composta da elementi che contengono un checkedtextview e 3 textview....
io vorrei che al clic della mia riga si "checki" la checkedtextview che è contenuta in essa....solo che ci clicco e non succede niente!!

Offline dom4

  • Utente normale
  • ***
  • Post: 158
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Vodafone Ideos, Nexus 5
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #6 il: 13 Giugno 2011, 17:07:41 CEST »
0
Ottimo tutorial!!Se il mio Button dovesse cancellare un oggetto contenuto in un arrayList come potrei fare?So che arrayList ha un metodo removeObject(Object),ma a questo punto i miei problemi sono 2:

1. come faccio a passare l'arrayList alla classe PersonAdapter(nel mio caso ProdottiAdapter)
2. risolto il primo punto,cliccando sul bottone verrebbe cancellato proprio quell'oggetto su quella riga?

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #7 il: 21 Luglio 2011, 11:15:12 CEST »
0
Ottimo tutorial!!! Grazie mille  :-)

Offline AlessioElia

  • Nuovo arrivato
  • *
  • Post: 29
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Windows XP
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #8 il: 26 Aprile 2012, 15:51:55 CEST »
0
Salve OTTIMO TUTORIAL  :D
ho riadattato una mia app seguendo questo tutorial.
Questo lo snippet in cui prendo i riferimenti alle imageview che voglio rendere cliccabili:
Codice (Java): [Seleziona]
View v=adapter.getView(0, null, null);
              ImageView img=(ImageView)v.findViewById(R.id.appletImage);
              img.setOnClickListener(new OnClickListener(){

                                        @Override
                                        public void onClick(View v) {
                                                // TODO Auto-generated method stub
                                                Intent i=new Intent(GraficaActivity.this,LoyaltyActivity.class);
                                                startActivity(i);
                                        }});
              v=adapter.getView(1, null, null);
              img=(ImageView)v.findViewById(R.id.appletImage);
              img.setOnClickListener(new OnClickListener(){

                                        @Override
                                        public void onClick(View v) {
                                                // TODO Auto-generated method stub
                                                Intent i=new Intent(GraficaActivity.this,Loyalty2Activity.class);
                                                startActivity(i);
                                        }});
                       
Questo il file Xml della riga:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="fill_parent"
       android:layout_height="60dip"
       android:padding="5dip"
      >


        <my.prova.grafica.ListImageView
               android:id="@+id/appletImage"
               android:layout_width="50dip"
               android:layout_height="50dip"
             
       />
       


        <TextView
               android:text="Nome"
               android:layout_marginLeft="5dip"
               android:textAppearance="?android:attr/textAppearanceLarge"
               android:id="@+id/appletName"
               android:layout_toRightOf="@id/appletImage"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               ></TextView>
        <CheckBox android:id="@+id/isActive"
           android:layout_width="wrap_content"
               android:layout_height="wrap_content"
                 android:layout_alignParentRight="true"
                 
                 />
</RelativeLayout>
Questa invece la classe che estende ImageView
Codice (Java): [Seleziona]
package my.prova.grafica;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

public class ListImageView extends ImageView{
        public ListImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setFocusable(false);
}
        @Override
    public void setPressed(boolean pressed) {
           
            if (pressed && ((View) getParent()).isPressed()) {
                    return;
            }
         
            super.setPressed(pressed);
    }
}
Ora il mio problema è che non parte l'evento onClick quando clicco sull'immagine anche se facendo il debug step by step vedo che effettivamente i riferimenti alle imageView vengono presi.  o_O
Chiedo AIUTO!!!
Grazie!
Alessio.

Offline Alet

  • Nuovo arrivato
  • *
  • Post: 19
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Acer Iconia Tab A501
  • Sistema operativo:
    Windows 7,Windows Xp
Re:[medio] Utilizzo dei Button nelle ListView con layout personalizzato
« Risposta #9 il: 05 Settembre 2012, 17:34:08 CEST »
0
Anch'io ho lo stesso problema solo che invece di usare un Button uso delle Edit Text.

Praticamente ho una listview composta da:

1 TextView
5 EditText

In teoria quello che voglio far eseguire alla mia applicazione è:

Clicco sulla tv che mi va ad abilitare le sue et.
Poi da li l'utente ha la possibilità d'inserire dei valori su quelle edittext

il problema però è che,seguendo il tutorial riesco facilmente ad ottenere il focus sulla TextView ma una volta disabilitato non riesco ad ottenerlo se clicco su una delle 5 EditText.

Help me ..  ??? pls

« Ultima modifica: 06 Settembre 2012, 11:50:38 CEST da Alet »