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.



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 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=".MainActivity">

    <Button
        android:id="@+id/button"
        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/button2"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/button2"
        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/button3"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/button" />

    <Button
        android:id="@+id/button3"
        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/button2" />

    <EditText
        android:id="@+id/txvTextoElemLista"
        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_lista_spinner">
        <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.
Para iso temos que facer uso dun Adapter, concretamente imos a empregar o ArrayAdapter

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).