ListView

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introdución

  • Un ListView é unha view de que lle permite ao usuario seleccionar un elemento.
  • A diferencia dun Spinner unha lista amosa todos os seus elementos na pantalla.

Listview.png

  • A imaxe recolle un exemplo de lista, cada item pode conter un só dato, ou múltiples datos. O primeiro caso é o que se vai ver neste curso.



  • Ao igual que no Spinner usaremos unha fonte de datos (array estático, array dinñamico, recurso xml, etc) para enlazalo co adaptador e este co ListView.


  • Tamén podemos facer uso de layouts 'predefinidos'.



  • Todo o visto có Spinner no uso de adaptadores pódese empregar neste view tendo en conta estas diferenzas:
  • Os layout 'predefinidos' son de tipo listview, non spinner.
  • A xestión de eventos sobre un elemento da lista (seleccionalo) se fai coa interface OnItemClickListener.



  • Se queremos que ao premer sobre a lista, visualmente apareza seleccionado o elemento (digo visualmente, non queda seleccionado a nivel de programación) podemos cambiar estas propiedades no deseñador de Layout sobre o ListView:
        android:choiceMode="singleChoice"
        android:listSelector="@color/colorPrimary"
Nota: @color/colorPrimary é un recurso de tipo 'color' cunha cor definida.



  • Nota Importante: Na actualidade recoméndase empregar o RecyclerView en vez do ListView, xa que consume moita menos memoria.
Un ListView vai 'xerar' todos os compoñentes gráficos que conforman cada fila para todos os elementos da lista, mentres que o RecyclerView soamente xera aqueles compoñentes que se visualizan.



Caso Base

  • O que imos aprender en primeiro lugar é o funcionamento básico da lista.
  • Como cargar datos na lista.
  • Como modificar visualmente o aspecto de cada elemento da lista.
  • Como xestionar o evento de click sobre un elemento da lista.
  • Como acceder ao elemento seleccionado da lista.
  • Como borrar, modificar ou engadir novos elementos á lista.
  • Posteriormente veremos outras posibilidades que nos brinda este control


Preparando a Activity

  • Imos crear unha Activity que visualmente teña este aspecto.
PDM listview base 1.jpg


  • Dentro do paquete Adaptadores crear unha nova 'Empty Activity' de nome: UD04_01_ListViews_base de tipo Launcher.
Modifica o arquivo AndroidManifiest.xml e engade unha label á activity como xa vimos na creación do proxecto base.



Código do Layoout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".adaptadores.UD04_01_ListViews_base">

    <Button
        android:id="@+id/btnAlta"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:text="Alta"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btnBaixa"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/btnBaixa"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:text="Baixa"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btnModificar"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnAlta" />

    <Button
        android:id="@+id/btnModificar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:text="Modificar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnBaixa" />

    <EditText
        android:id="@+id/edtEditElemLista"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="Texto Elemento Lista"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline" />

    <ListView
        android:id="@+id/lsvLista"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_dark"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>



Cargando datos

  • O primeiro que temos que lograr facer é cargar os datos na lista.
Isto o podemos facer de dúas formas:
  • Graficamente, a través da propiedade entreis do ListView.
  • Por código, facendo uso dun ArrayAdapter.


Cargando datos dende o Layout

  • Primeiro debemos crear un recurso nun dos arquivos /res/values de tipo 'array'.
No exemplo está creado no arquivo /res/values/strings.xml.
    <array name="array_datos_lista">
        <item>Valor 1</item>
        <item>Valor 2</item>
        <item>Valor 3</item>
    </array>
Estes son valores que vai cargar a lista.
Nota: Fixarse que estes valores poden estar traducidos a diferentes idiomas coma xa vimos na sección de internacionalización.



Cargando datos dende o código

  • Nota: Quitamos a carga de datos se a temos na propiedade 'entries' do ListView como vimos no punto anterior.


Para iso temos que facer uso dun Adapter, concretamente imos a empregar o ArrayAdapter
Lembrar que xa expliquei o que era un adaptador.
  • Se nos fixamos nos contructores do adaptador:
PDM listview base 5.jpg
Nos imos a empregar o terceiro constructor.
Necesitimos indicarlle o layout que vai empregar para visualizar os elementos da lista. Podemos empregar calquera sempre que teña a lo menos un TextView.
Android proporciona xa uns predeterminados que podemos empregar, concretamente : android.R.layout.simple_list_item_1 (podedes ver o deseño dende Android Studio premendo a tecla Control e premendo có rato sobre simple_list_item_1).
Por outra banda necesitamos algún tipo de array que garde os datos.
public class UD04_01_ListViews_base extends AppCompatActivity {

    private void cargarDatos(){

        String[]datos = getResources().getStringArray(R.array.array_datos_lista);

        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,datos);
        ListView listView = findViewById(R.id.lsvLista);
        listView.setAdapter(adapter);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_u_d04_01__list_views_base);

        cargarDatos();
    }
}
Liña 5: Como vemos obtemos os datos do string-array creado en /res/values no punto anterior.
Liña 7: Creamos un adaptador empregando un layout do S.O. e mandamos os datos.
Liña 9: Asociamos o adaptador á lista (se non facemos isto non se cargará ningún dato).


  • Podemos facer unha variante deste código e facer que os datos veñan dun ArrrayList:
    private void cargarDatos(){

        ArrayList<String>datos = new ArrayList<>(Arrays.asList("Valor 1","Valor 2", "Valor 3"));
        
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,datos);
        ListView listView = findViewById(R.id.lsvLista);
        listView.setAdapter(adapter);
    }



Personalizando o deseño

  • Antes vimos que no segundo parámetro temos que enviarlle o id do layout que vai representar cada un dos elementos da lista.
Nos podemos crear o noso propio Layout.
Por exemplo:

Layout: elemento_listview

::<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="VALOR:"
        android:textColor="@android:color/holo_purple"
        android:textStyle="bold"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txvElemLista"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:text="TextView"
        app:layout_constraintStart_toEndOf="@+id/textView"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Agora imos decirlle ao adaptador que cargue este deseño, pero como dentro do mesmo hai dous TextView, temos que decirlle cal deles debe empregar para pasar o dato da fonte de datos ao view.
Para iso empregamos outro dos contructores vistos anteriormente:
    private void cargarDatos(){
        ArrayList<String>datos = new ArrayList<>(Arrays.asList("Valor 1","Valor 2", "Valor 3"));

        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.elemento_listview,R.id.txvElemLista,datos);
        ListView listView = findViewById(R.id.lsvLista);
        listView.setAdapter(adapter);
    }
Liña 3: Fixarse en dúas cousas: Cargamos o layout deseñado por nos e enviamos como tecer parámetro o id do textview que se atopa dentro do layout.



Xestionando o evento Click sobre un elemento da lista

NOTA IMPORTANTE: Cando prememos nun elemento da lista este non queda seleccionado polo que non podemos empregar ningún método o interface que indique 'selected'.
    private void xestionarEventos(){
        ListView lsvLista = findViewById(R.id.lsvLista);
        lsvLista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Toast.makeText(getApplicationContext(), adapterView.getItemAtPosition(i).toString(),Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_u_d04_01__list_views_base);

        cargarDatos();
        xestionarEventos();
    }
Liña 5: No método da interface onItemClick vai vir como parámetros:
  • O AdapterView, a través do cal
  • Podemos acceder aos elementos que están cargados na lista.
  • Podemos recuperar a referencia ao ArrayAdapter.
  • O View que foi premido (lembrar que se estamos a facer o exemplo, o view agora mesmo é o layout que fixemos con dous textview.
  • i é a posición do elemento seleccionado.
  • l é un identificador de fila (que non imos empregar).

O anterior é equivalente e isto se temos acceso aos array de datos:

public class UD04_01_ListViews_base extends AppCompatActivity {

    private ArrayList<String>datos = new ArrayList<>(Arrays.asList("Valor 1","Valor 2", "Valor 3"));

    private void cargarDatos(){

        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.elemento_listview,R.id.txvElemLista,datos);
        ListView listView = findViewById(R.id.lsvLista);
        listView.setAdapter(adapter);
    }

    private void xestionarEventos(){
        ListView lsvLista = findViewById(R.id.lsvLista);
        lsvLista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Toast.makeText(getApplicationContext(), datos.get(i).toString(),Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_u_d04_01__list_views_base);

        cargarDatos();
        xestionarEventos();
    }
}
Liña 3: Definimos o array a nivel de Activity para poder acceder a el dende calquera parte.
Liña 17: Accedemos á posición do array.
Curiosidade: O parámetro 'view' nos permite acceder aos views que están dentro do layout que deseñamos.
Por exemplo:
        lsvLista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                TextView tv = view.findViewById(R.id.txvElemLista);
                tv.setTextColor(Color.RED);
                Toast.makeText(getApplicationContext(), datos.get(i).toString(),Toast.LENGTH_SHORT).show();
            }
        });



Accedendo ao elemento seleccionado da lista

  • O ListView non selecciona ningún elemento cando prememos nela, polo que non podemos empregar os métodos como: lista.getSelectedItem()
Este tipo de chamadas devolverán sempre null no caso do ListView (si funcionan cun spinner).
A única forma de saber cal é o elemento que premeu o usuario é gardando a posición nunha propiedade da Activity, no método onItemClick que vimos anteiormente. Veremos o seu uso na operación de baixa.




Operacións sobre a lista

  • Podemos facer alta, baixas e modificacións.
Ditas operacións as podemos facer sobre o Array onde temos gardados os datos (sempre que estea definido a nivel de Activity ou de forma global) ou sobre o adaptador asociado á ListView.
Imos velos das dúas formas:


Operación de baixa

  • Para dar de baixa un elemento necesitamos gardar a posición clickeada polo usuario.



Accedendo ao adaptador
Supoñemos que non temos acceso ao array de datos (por estar definido localmente, por exemplo).
    private void xestionarEventos(){
        findViewById(R.id.btnBaixa).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (posElemListaSelec==-1) return;

                ArrayAdapter<String> adapter = (ArrayAdapter)((ListView)findViewById(R.id.lsvLista)).getAdapter();
                String objeto = adapter.getItem(posElemListaSelec);
                adapter.remove(objeto);
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText("");
            }
        });

        ListView lsvLista = findViewById(R.id.lsvLista);
        lsvLista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                posElemListaSelec = i;
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText(datos.get(i));
            }
        });


    }
Liña 7: Ao non ter acceso ao array de datos, calquera operación a temos que facer a través do adaptador. Polo tanto necesitamos obter unha referencia ao adaptador da ListView.
Liña 8: A través do adaptador podemos acceder ao elemento seleccionado pola posición que temos gardada previamente no método onItemClick.
Liña 9: Eliminamos o elemento do adaptador. Automaticamente xa se refresca o ListView e desaparece o elemento.


Accedendo ao array de datos
  • Neste caso podemos acceder ao array de datos que está cargado no adaptador (polo telo definido a nivel de Activity, non localmente nun método).
        findViewById(R.id.btnBaixa).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (posElemListaSelec==-1) return;

                datos.remove(posElemListaSelec);
                ArrayAdapter adaptador = (ArrayAdapter)((ListView)findViewById(R.id.lsvLista)).getAdapter();
                adaptador.notifyDataSetChanged();
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText("");
            }
        });
Liña 6: Eliminamos o elemento do array de datos.
Liña 8-9: Necesitamos informar ao ListView que ten que recargar a lista, chamando ao método notifyDataSetChanged() do adaptador.




Operación de Alta


Accedendo ao adaptador
Supoñemos que non temos acceso ao array de datos (por estar definido localmente, por exemplo).
    private void xestionarEventos(){
        findViewById(R.id.btnAlta).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EditText et = findViewById(R.id.edtEditElemLista);
                if (et.getText().toString().length()==0) return;

                ArrayAdapter adaptador = (ArrayAdapter)((ListView)findViewById(R.id.lsvLista)).getAdapter();
                adaptador.add(et.getText().toString());
                

            }
        });

        findViewById(R.id.btnBaixa).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (posElemListaSelec==-1) return;

                ArrayAdapter<String> adapter = (ArrayAdapter)((ListView)findViewById(R.id.lsvLista)).getAdapter();
                String objeto = adapter.getItem(posElemListaSelec);
                adapter.remove(objeto);
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText("");
            }
        });

        ListView lsvLista = findViewById(R.id.lsvLista);
        lsvLista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                posElemListaSelec = i;
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText(datos.get(i));
            }
        });


    }
Liña 8: Ao non ter acceso ao array de datos, calquera operación a temos que facer a través do adaptador. Polo tanto necesitamos obter unha referencia ao adaptador da ListView.
Liña 9: A través do adaptador podemos engadir o novo texto.


Accedendo ao array de datos
  • Neste caso podemos acceder ao array de datos que está cargado no adaptador (polo telo definido a nivel de Activity, non localmente nun método).
        findViewById(R.id.btnAlta).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EditText et = findViewById(R.id.edtEditElemLista);
                if (et.getText().toString().length()==0) return;

                ArrayAdapter adaptador = (ArrayAdapter)((ListView)findViewById(R.id.lsvLista)).getAdapter();
                datos.add(et.getText().toString());
                adaptador.notifyDataSetChanged();

                et.setText("");
            }
        });
Liña 8: Añadimos o elemento ao array de datos.
Liña 9: Necesitamos informar ao ListView que ten que recargar a lista, chamando ao método notifyDataSetChanged() do adaptador.





Operación de Modificar

  • Necesitamos ter gardado nun atributo da clase, a posición do elemento seleccionado ao premer sobre a lista (como xa vimos nun exemplo anterior)


Accedendo ao adaptador
Supoñemos que non temos acceso ao array de datos (por estar definido localmente, por exemplo).
O máis doado neste caso é borrar e dar de alta o novo elemento.
    private void xestionarEventos(){
        findViewById(R.id.btnModificar).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EditText et = findViewById(R.id.edtEditElemLista);
                if (et.getText().toString().length()==0) return;

                ArrayAdapter adaptador = (ArrayAdapter)((ListView)findViewById(R.id.lsvLista)).getAdapter();

                String datoSel = (String)adaptador.getItem(posElemListaSelec);
                adaptador.remove(datoSel);
                String datoNuevo = et.getText().toString();
                adaptador.insert(datoNuevo,posElemListaSelec);

            }
        });

        ListView lsvLista = findViewById(R.id.lsvLista);
        lsvLista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                posElemListaSelec = i;
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText(datos.get(i));
            }
        });


    }
Liña 11: Borramos o elemento seleccionado.
Liña 13: Engadimos o novo elemento.



Accedendo ao array de datos
  • Neste caso podemos acceder ao array de datos que está cargado no adaptador (polo telo definido a nivel de Activity, non localmente nun método).
        findViewById(R.id.btnModificar).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EditText et = findViewById(R.id.edtEditElemLista);
                if (et.getText().toString().length()==0) return;

                ArrayAdapter adaptador = (ArrayAdapter)((ListView)findViewById(R.id.lsvLista)).getAdapter();
                datos.set(posElemListaSelec,et.getText().toString());
                adaptador.notifyDataSetChanged();
            }
        });
Liña 8: Modificamos o elemento do array de datos.


Curiosidade: Podemos acceder ao view seleccionado mediante esta forma:
                View elemview = adaptador.getView(posElemListaSelec,View.inflate(getApplicationContext(),R.layout.elemento_listview,null),null);
                ((TextView) elemview.findViewById(R.id.txvElemLista)).setText(et.getText().toString());




Operacións sobre o Adaptador

  • O ArrayAdapter está 'cheo' de métodos para engadir, borrar e modificar elementos da lista.
Por exemplo:




Caso Práctico 1

Se non o temos creado antes, crearemos un novo paquete de nome: Adaptadores como un subpaquete do teu paquete principal.






XML do Layout

  • Definimos no Layout unha vista de tipo ListView (Liñas 14-17).
 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <TextView
 8         android:layout_width="match_parent"
 9         android:textSize="20sp"
10         android:layout_height="wrap_content"
11         android:text="Selecciona unha froita" />
12 
13     <ListView
14         android:id="@+id/lvFroitas_UD04_01_ListViews"
15         android:layout_width="match_parent"
16         android:layout_height="wrap_content" />
17 
18 </LinearLayout>



Código da Activity

  • A filosofía do código Java é moi semellante ao do Spinner.
 1 package es.cursoandroid.cifprodolfoucha.aprendiendo.Adaptadores;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.view.View;
 6 import android.widget.AdapterView;
 7 import android.widget.ArrayAdapter;
 8 import android.widget.ListView;
 9 import android.widget.TextView;
10 import android.widget.Toast;
11 
12 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
13 
14 public class UD04_01_ListViews extends Activity {
15 
16     private void cargarListas(){
17         //Fonte de datos
18         String[] froitas = new String[] { "Pera", "Mazá", "Plátano" };
19 
20         //Enlace do adaptador coa fonte de datos
21         ArrayAdapter<String> adaptador = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, froitas);
22 
23         ListView lvFroitas = findViewById(R.id.lvFroitas_UD04_01_ListViews);
24         //Enlace do adaptador co ListView
25         lvFroitas.setAdapter(adaptador);
26     }
27     private void xestionarEventos(){
28 
29         ListView lvFroitas = findViewById(R.id.lvFroitas_UD04_01_ListViews);
30         //Escoitador
31         lvFroitas.setOnItemClickListener(new AdapterView.OnItemClickListener() {
32 
33             @Override
34             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
35                 // TODO Auto-generated method stub
36                 //Toast.makeText(getBaseContext(), "Seleccionaches: " + parent.getItemAtPosition(position), Toast.LENGTH_SHORT).show();
37                 Toast.makeText(getApplicationContext(), "Seleccionaches: " + ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
38 
39             }
40         });
41     }
42 
43     @Override
44     protected void onCreate(Bundle savedInstanceState) {
45         super.onCreate(savedInstanceState);
46         setContentView(R.layout.activity_ud04_01__list_views);
47 
48         cargarListas();
49         xestionarEventos();
50     }
51 }
  • Liña 18: Ao igual que no Spinner, creamos unha fonte de datos, neste caso cun array estático.
  • Liña 21: Do mesmo xeito creamos o adaptador
  • Liña 25: E finalmente, vinculamos o adaptador ao ListView
  • Liña 31: O Escoitador asociado ao ListView.
  • Liñas 36,37: Ao igual que no spinner, neste caso a vista que recibimos cando facemos click nun ítem da view é do tipo TextView.



ListView usando un recurso XML ou un array dinámico

  • Neste caso funciona igual que o visto no Spinner que fai uso dun adaptador.



Caso Práctico 2: Utilizando un Layout con Ckeckboxes

Se non o temos creado antes, crearemos un novo paquete de nome: Adaptadores como un subpaquete do teu paquete principal.



  • Dentro do paquete Adaptadores crear unha nova 'Empty Activity' de nome: UD04_02_ListViews de tipo Launcher e sen compatibilidade.
Modifica o arquivo AndroidManifiest.xml e engade unha label á activity como xa vimos na creación do proxecto base.


  • Como comentamos ao inicio, ao crear o adaptador podemos facer uso de varios layouts xa definidos polo S.O. Android.
Un destes layouts permite que cada elemento da lista tenga un 'checkbox' asociado.
Para que funcione, debemos de:
  • Poñer no layout o atributo: android:choiceMode="multipleChoice"
  • Ou ben ter unha referencia ao ListView e cambia dito atributo por programación: refLista.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);



  • Se necesitamos programar algo cando se preme un elemento da lista non ten problema ningún, se fería como no Spinner. A única diferenza é que o View que ven no método 'onItemClick' da interface e de tipo 'CheckedTextView'. Facendo un cast a dito tipo, accederemos ao texto e se está checked ou non.
  • O máis complicado será no caso que necesitemos, cada vez que se pulsa un elemento, percorrer toda a lista checkeando cales dos elementos están marcados.
Para facer isto temos varias aproximacións:
  • Opción a)
Definir un ArrayList<String> e gardar nel os elementos que imos seleccionando ou quitando da lista os elementos que xa non están seleccionados.
Para saber cando un elemento está seleccionado ou non:
 1             private ArrayList<String>datos = new ArrayList<>();
 2             ...............
 3             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 4                 // TODO Auto-generated method stub
 5                 CheckedTextView c = (CheckedTextView)view;
 6                 if (c.isChecked()){
 7                     datos.add(c.getText().toString());
 8                 }
 9                 else {
10                     datos.remove(c.getText().toString());
11                 }
12            }
  • Opción b)
Facer uso da clase SparseBooleanArray e do método getCheckedItemPositions o cal devolve un obxecto da clase SparseBooleanArray coas posicións dos elementos seleccionados.
Para facer iso disto temos:
1                 SparseBooleanArray elems = referenciaLista.getCheckedItemPositions();
2                 for(int cont=0; cont < referenciaLista.getCount();cont++){
3                     if (elems.get(cont)){
4                        // Está seleccionado
5                     }
6                 }






XML do Layout

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     tools:context=".Adaptadores.UD04_02_ListViews">
 8 
 9     <LinearLayout
10         android:layout_width="0dp"
11         android:layout_height="149dp"
12         android:layout_marginEnd="32dp"
13         android:layout_marginStart="32dp"
14         android:layout_marginTop="8dp"
15         android:orientation="vertical"
16         app:layout_constraintEnd_toEndOf="parent"
17         app:layout_constraintStart_toStartOf="parent"
18         app:layout_constraintTop_toTopOf="parent">
19 
20         <ListView
21             android:id="@+id/lvCoches_UD04_02_ListViews"
22             android:layout_width="match_parent"
23             android:layout_height="0dp"
24             android:layout_marginEnd="8dp"
25             android:layout_marginStart="8dp"
26             android:layout_marginTop="8dp"
27             android:layout_weight="1"
28             android:choiceMode="multipleChoice">
29 
30         </ListView>
31     </LinearLayout>
32 
33     <TextView
34         android:id="@+id/tvSeleccionados_UD04_02_ListViews"
35         android:layout_width="0dp"
36         android:layout_height="wrap_content"
37         android:layout_marginBottom="8dp"
38         android:layout_marginEnd="8dp"
39         android:layout_marginStart="8dp"
40         android:textSize="24sp"
41         app:layout_constraintBottom_toBottomOf="parent"
42         app:layout_constraintEnd_toEndOf="parent"
43         app:layout_constraintStart_toStartOf="parent"
44         tools:text="Elementos seleccionados" />
45 
46 </android.support.constraint.ConstraintLayout>



Código da Activity

 1 package es.cursoandroid.cifprodolfoucha.aprendiendo.Adaptadores;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.text.Html;
 6 import android.util.SparseBooleanArray;
 7 import android.view.View;
 8 import android.widget.AdapterView;
 9 import android.widget.ArrayAdapter;
10 import android.widget.CheckedTextView;
11 import android.widget.ListView;
12 import android.widget.TextView;
13 import android.widget.Toast;
14 
15 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
16 
17 public class UD04_02_ListViews extends Activity {
18 
19     private void cargarListas(){
20         //Fonte de datos
21         String[] coches = new String[] { "FORD", "OPEL", "SEAT","CITROEN","VOLKSWAGEN","ONDA" };
22 
23         //Enlace do adaptador coa fonte de datos
24         ArrayAdapter<String> adaptador = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, coches);
25 
26         ListView lvCoches = findViewById(R.id.lvCoches_UD04_02_ListViews);
27         //Enlace do adaptador co ListView
28         lvCoches.setAdapter(adaptador);
29     }
30 
31     private void xestionarEventos(){
32 
33         final ListView lvCoches = findViewById(R.id.lvCoches_UD04_02_ListViews);
34 
35         //Escoitador
36         lvCoches.setOnItemClickListener(new AdapterView.OnItemClickListener() {
37 
38             @Override
39             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
40                 // TODO Auto-generated method stub
41                 CheckedTextView c = (CheckedTextView)view;
42                 Toast.makeText(getApplicationContext(),c.getText(),Toast.LENGTH_LONG).show();
43 
44                 TextView mensaxe = findViewById(R.id.tvSeleccionados_UD04_02_ListViews);
45                 mensaxe.setText("");
46                 SparseBooleanArray elems = lvCoches.getCheckedItemPositions();
47                 for(int cont=0; cont < lvCoches.getCount();cont++){
48                     if (elems.get(cont)){
49                        mensaxe.append(lvCoches.getItemAtPosition(cont).toString()+ Html.fromHtml("<br />"));
50                     }
51                 }
52 
53             }
54         });
55     }
56 
57 
58     @Override
59     protected void onCreate(Bundle savedInstanceState) {
60         super.onCreate(savedInstanceState);
61         setContentView(R.layout.activity_ud04_02__list_views);
62 
63         cargarListas();
64         xestionarEventos();
65     }
66 }




Operacións especiais

  • CASO PRÁCTICO:
  • Temos uns datos locais nun array baseados nunha clase na que existe un campo booleano que nos indica se temos ou non seleccionado o elemento.
  • Temos una lista de selección múltiple de tipo checkbox no seu layout (android.R.layout.simple_list_item_checked | android:choiceMode="multipleChoice")
  • Queremos que ao cargarse a lista, aparezan seleccionados os elementos do array que teñen a propiedade boolean a true.
Posible Solución:
 1         ListView lvLista = findViewById(R.id.id_lista);
 2         ArrayList<MiClase> datos = new ArrayList<>();
 3 
 4         // CARGAMOS O ARRAY CON DATOS. PODEN VIR DUNHA BASE DE DATOS OU CALQUERA OUTRA FONTE
 5 
 6         ArrayAdapter<MiClase> arrayAdapter = new ArrayAdapter<MiClase>(this,android.R.layout.simple_list_item_checked,datos);
 7         lvLista.setAdapter(arrayAdapter);
 8 
 9         for (int i=0;i<datos.size();i++) {
10             lvLista.setItemChecked(i,datos.get(i).getPropiedadeBoolean());
11         }



-- Ángel D. Fernández González e Carlos Carrión Álvarez -- (2018).