Spinner a través de adaptador

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

Introdución


  • Xa vimos na unidade 2 como crear un Spinner, pero o enlace dos datos co Spinner facíase a través dun atributo XML na definición do Spinner no Layout: android:entries.
  • Agora imos facer o mesmo pero usando un adaptador.
  • Colleremos os datos dende un array no código e logo dende un recurso XML.


  • Se queremos cargar ditos recursos por programación:
  • Deberemos facer uso dun obxecto da clase ArrayAdapter. Este obxecto serve coma ponte entre os datos e o spinner. A modificacións dos datos da lista sempre se fai no adaptador asociado á mesma e nunca sobre o spinner. Engadir, borrar, modificar, limpar,...todas esas operacións se teñen que facer sobre o adaptador.
  • Deberemos de obter os datos para encher o spinner. Estes datos poden vir dun array gardado en /res/ e para referencialo teremos que facer uso do método getResources().
  • Deberemos de establecer o aspecto (layout) que terá cada un dos 'views' que conforman cada elemento da lista (si, cada elemento da lista ten que ser un view :) )
  • Deberemos establecer o aspecto (layout) de cada elemento do spinner cando prememos na lista e se despregan todos eles.


  • Para crear un ArrayAdapter podemos seguir este código:
1         Spinner lista = findViewById(R.id.id_spinner);
2 
3         ArrayAdapter<CharSequence>adaptador = ArrayAdapter.createFromResource(this,R.array.planetas_UD02_01_spinner,android.R.layout.simple_list_item_1);
4         adaptador.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
5         lista.setAdapter(adaptador);


  • O contexto
  • Os datos do ArrayAdapter, que neste caso veñen dun recurso xa creado previamente noutro exercicio.
  • O Layout que vai ter asociado cada view (elemento da lista, que dependendo do layout serán views como TextViews).
Os layout que se poden empregar se poden consultar en http://wiki.cifprodolfoucha.es/index.php?title=Spinner#Caso_pr.C3.A1ctico
O código fonte de cada layout se pode consultar en https://github.com/aosp-mirror/platform_frameworks_base/tree/master/core/res/res/layout


  • Na liña 4, establecemos o layout que vai ter o spinner cando prememos sobre el e aparezan todos os elementos da lista. Podemos consultar o código fonte de dito layout.
  • Na liña 5 asociamos o ArrayAdapter ao Spinner.




  • Podemos facer o mesmo desta outra forma:
1 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,getResources().getStringArray(R.array.planetas_UD02_01_spinner));
Para referenciar o array teríamos que facer uso da clase getResource() e obter o tipo correspondente (array).


  • O ArrayAdapter está 'cheo' de métodos para engadir, borrar e modificar elementos da lista.
Por exemplo:
  • Importante: Calquera modificación que se faga sobre os elementos da lista facendo uso do adaptador, leva consigo a 'obriga' de informar ao view dos cambios para que os reflicta.
Isto se consigo chamando a un destes dous métodos:
O segundo normalmente se chama cando hai algún cambio estrutural na lista (remove, add,...). En teoría chamarano o primeiro método automaticamente pero algunhas veces non funciona e é necesario chamalo explicitamente. Se debe chamar a un dos dous, non os dous.


  • Por exemplo, o código anterior se podería cambiar por:
1 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item);
2 adapter.addAll(getResources().getStringArray(R.array.planetas_UD02_01_spinner));
3 adapter.setNotifyOnChange(true);



Ao facelo, deberá implementarse estes dous método:
1             @Override
2             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
3 
4             }
5 
6             @Override
7             public void onNothingSelected(AdapterView<?> parent) {
8 
9             }
O segundo método se chama cando non se selecciona ningún elemento e desaparece a lista.
O primeiro cando se selecciona un elemento da lista.
Leva como parámetros:
  • O adaptador asociado á lista.
  • O view que foi seleccionado (lembrar que cando se crea o adaptador se establece un layout para cada compoñente da lista)
  • A posición do elemento seleccionado na lista (empeza en 0)
  • Un identificador para a fila que foi seleccionada.

Caso práctico 1

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




  • Cada ítem do Spinner é tratado como unha View, neste caso de tipo TextView.

O XML do layout

  • Observar como a vista Spinner non ten a entrada android:entries.
 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_01_Spinners">
 8 
 9     <Spinner
10         android:id="@+id/spnLista_UD02_02_spinner"
11         android:layout_width="0dp"
12         android:layout_height="wrap_content"
13         android:layout_marginEnd="8dp"
14         android:layout_marginStart="8dp"
15         android:layout_marginTop="8dp"
16         android:dropDownWidth="match_parent"
17         app:layout_constraintEnd_toEndOf="parent"
18         app:layout_constraintStart_toStartOf="parent"
19         app:layout_constraintTop_toTopOf="parent" />
20 </android.support.constraint.ConstraintLayout>


  • Partimos do exercicio de spinner feito anteriormente, no que está definido un array de datos, nun arquivo de /res/values:
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3 
 4     <string-array name="planetas_UD02_01_spinner">
 5         <item>Mercurio</item>
 6         <item>Venus</item>
 7         <item>Terra</item>
 8         <item>Marte</item>
 9         <item>Xúpiter</item>
10         <item>Saturno</item>
11         <item>Urano</item>
12         <item>Neptuno</item>
13     </string-array>
14 
15 </resources>


  • O 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.view.View;
 6 import android.widget.AdapterView;
 7 import android.widget.ArrayAdapter;
 8 import android.widget.Spinner;
 9 import android.widget.TextView;
10 import android.widget.Toast;
11 
12 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
13 
14 public class UD04_01_Spinners extends Activity {
15 
16     private void cargarLista() {
17 
18         Spinner lista = findViewById(R.id.spnLista_UD02_02_spinner);
19 
20         ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
21         adapter.addAll(getResources().getStringArray(R.array.planetas_UD02_01_spinner));
22         lista.setAdapter(adapter);
23 
24         lista.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
25             @Override
26             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
27                 //String datoLista = (String)parent.getItemAtPosition(position);    // Poderíamos obter o dato desta forma (fixarse no Cast)
28                 String datoLista = ((TextView) view).getText().toString();          // ou desta outra forma
29                 Toast.makeText(getApplicationContext(),"Elemento seleccionado:" + datoLista + " na posición "+ String.valueOf(position),Toast.LENGTH_LONG).show();
30             }
31 
32             @Override
33             public void onNothingSelected(AdapterView<?> parent) {
34 
35             }
36         });
37 
38     }
39     @Override
40     protected void onCreate(Bundle savedInstanceState) {
41         super.onCreate(savedInstanceState);
42         setContentView(R.layout.activity_ud04_01__spinners);
43 
44         cargarLista();
45 
46     }
47 }
  • Liñas 20-22: Usamos unha das formas vistas para crear o adaptador e asocialo ao spinner.
  • Liña 24: Rexistramos a interface que xestiona o evento de selección dun elemento da lista.
  • Liñas 26-30: Facemos uso do método onItemSelected() e os seus parámetros para obter o dato da lista seleccionado.

Caso práctico 2: Usando un array estático en Java

  • Dentro do paquete Adaptadores crear unha nova 'Empty Activity' de nome: UD04_02_Spinners 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.



  • Neste caso cargaremos a fonte de datos dun array local.
PDM adaptador 3.jpg



O código XML da activity é:

 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_Spinners">
 8 
 9     <Spinner
10         android:id="@+id/spnLista_UD04_02_Spinner"
11         android:layout_width="wrap_content"
12         android:layout_height="wrap_content"
13         android:layout_marginBottom="8dp"
14         android:layout_marginEnd="8dp"
15         android:layout_marginStart="8dp"
16         android:layout_marginTop="8dp"
17         app:layout_constraintBottom_toBottomOf="parent"
18         app:layout_constraintEnd_toEndOf="parent"
19         app:layout_constraintStart_toStartOf="parent"
20         app:layout_constraintTop_toTopOf="parent" />
21 </android.support.constraint.ConstraintLayout>



  • Ao final do código explícase como se enlaza a fonte de datos co adaptador e este co spinner.
  • Observar as liñas marcadas.
 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.Spinner;
 9 import android.widget.TextView;
10 import android.widget.Toast;
11 
12 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
13 
14 public class UD04_02_Spinners extends Activity {
15 
16     private void cargarLista(){
17 
18             Spinner spinFroitas = (Spinner) findViewById(R.id.spnLista_UD04_02_Spinner);
19 
20             // Fonte de datos
21             String[] froitas = new String[] { "Pera", "Mazá", "Plátano" };
22 
23             // Enlace do adaptador cos datos
24             ArrayAdapter<String> adaptador = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, froitas);
25 
26             // Opcional: layout usuado para representar os datos no Spinner
27             adaptador.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
28 
29             // Enlace do adaptador co Spinner do Layout.
30             spinFroitas.setAdapter(adaptador);
31 
32             // Escoitador
33             spinFroitas.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
34                 @Override
35                 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
36                     //Toast.makeText(getBaseContext(), "Seleccionaches: " + parent.getItemAtPosition(pos), Toast.LENGTH_LONG).show();
37                     Toast.makeText(getBaseContext(), "Seleccionaches: " + ((TextView) view).getText(), Toast.LENGTH_LONG).show();
38 
39                 }
40 
41                 @Override
42                 public void onNothingSelected(AdapterView<?> arg0) {
43                     // TODO Auto-generated method stub
44                 }
45             }); // Fin da clase anónima
46 
47     }
48 
49 
50     @Override
51     protected void onCreate(Bundle savedInstanceState) {
52         super.onCreate(savedInstanceState);
53         setContentView(R.layout.activity_ud04_02__spinners);
54 
55         cargarLista();
56     }
57 }
  • Liña 23: Definimos a fonte de datos, neste caso un array estático.
  • Liña 26: Definimos o adaptador de tipo ArrayAdapter. Pasámoslle como parámetros:
    • Contexto,
    • O int, identificador de recurso de layout que se vai usar para representar a Vista de Selección, neste caso usamos un predefinido. O usuario pode experimentar con outros tipos (android.R.layout.... CTRL+ESPAZO) e ver que outras formas hai de presentar os datos nun Spinner.
    • Un array de obxectos, neste caso de Strings.
    • Para ver outros construtores e métodos: http://developer.android.com/reference/android/widget/ArrayAdapter.html
  • Liña 29: setDropDownViewResource(int), indica como se vai representar cada un dos ítems do Spinner. Usamos un layout xa predefinido. O usuario pode experimentar usando outros distintos, predefinidos ou propios.
  • Liña 32: establece o adaptador que subministra os datos ao Spinner.
  • Liña 35: O escoitador é o mesmo que cando non se usaba un adaptador.
  • Liñas 38,39: As dúas liñas fan o mesmo, pero no segundo caso observar como recollemos a vista (view) que nos devolve o evento ao premer nun ítem do Spinner. Esa view é do tipo TextView e por iso facemos un cast e logo xa lle podemos aplicar métodos da clase TextView, como getText().

Caso práctico 3: Uso dun array dinámico

  • Dentro do paquete Adaptadores crear unha nova 'Empty Activity' de nome: UD04_03_Spinners 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.



  • Neste caso cargaremos a fonte de datos dun array ArrayList. Cando vexamos o acceso a base de datos, os datos virán dunha táboa.
PDM adaptador 4.jpg



O código XML da activity é:

 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_Spinners">
 8 
 9     <Spinner
10         android:id="@+id/spnLista_UD04_03_Spinner"
11         android:layout_width="wrap_content"
12         android:layout_height="wrap_content"
13         android:layout_marginBottom="8dp"
14         android:layout_marginEnd="8dp"
15         android:layout_marginStart="8dp"
16         android:layout_marginTop="8dp"
17         app:layout_constraintBottom_toBottomOf="parent"
18         app:layout_constraintEnd_toEndOf="parent"
19         app:layout_constraintStart_toStartOf="parent"
20         app:layout_constraintTop_toTopOf="parent" />
21 </android.support.constraint.ConstraintLayout>


  • Nos dous casos anteriores o contido do Spinner é estático e defínese en tempo de compilación, non de execución.
  • Se usamos arrays dinámicos podemos crear a fonte de datos en tempo de execución antes de pasarlla ao adaptador.
  • Así imos poder usar datos de ficheiros, bases de datos, etc e crear unha fonte de datos para un Spinner en tempo de execución.
 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.Spinner;
 9 import android.widget.TextView;
10 import android.widget.Toast;
11 
12 import java.util.ArrayList;
13 
14 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
15 
16 public class UD04_03_Spinners extends Activity {
17 
18     private void cargarLista(){
19 
20         Spinner spinFroitas = (Spinner) findViewById(R.id.spnLista_UD04_03_Spinner);
21 
22         ArrayList<String> animais = new ArrayList<String>();
23 
24         animais.add("CABALLOS");
25         animais.add("GATITOS");
26         animais.add("PERROS");
27         animais.add("RATONES");
28 
29         // Enlace do adaptador cos datos
30         ArrayAdapter<String> adaptador = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, animais);
31 
32         // Enlace do adaptador co Spinner do Layout.
33         spinFroitas.setAdapter(adaptador);
34 
35         // Escoitador
36         spinFroitas.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
37             @Override
38             public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
39                 //Toast.makeText(getBaseContext(), "Seleccionaches: " + parent.getItemAtPosition(pos), Toast.LENGTH_LONG).show();
40                 Toast.makeText(getBaseContext(), "Seleccionaches: " + ((TextView) view).getText(), Toast.LENGTH_LONG).show();
41 
42             }
43 
44             @Override
45             public void onNothingSelected(AdapterView<?> arg0) {
46                 // TODO Auto-generated method stub
47             }
48         }); // Fin da clase anónima
49 
50     }
51 
52 
53     @Override
54     protected void onCreate(Bundle savedInstanceState) {
55         super.onCreate(savedInstanceState);
56         setContentView(R.layout.activity_ud04_03__spinners);
57 
58         cargarLista();
59     }
60 }
  • Liñas 23-28: Neste caso os elementos do array engádense en tempo de execución ao array animais, que neste caso e do tipo ArrayList.
  • O resto é exactamente igual.



Caso práctico 4: Personalizando o deseño

  • Dentro do paquete Adaptadores crear unha nova 'Empty Activity' de nome: UD04_04_Spinners 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 indicamos dende o inicio, cando creamos o Spinner tamén creamos un adaptador sobre o cal indicamos o layout de cada compoñente da lista.
Podemos crear nos mesmos o deseño que queiramos cos datos que queiramos que aparezan na lista.
  • Para facer o anterior, temos que crear o noso propio adaptador.
  • Nesta práctica creamos un layout formado por unha imaxe e un texto para cada elemento da lista do spinner.
PDM adaptador 10.jpg



  • Como o que queremos é facer un deseño específico para cada 'fila' de elementos da lista / spinner, imos crear o Layout que vai representar cada fila:

Nome Arquivo do layout: spinner_ud04_04.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent">
 6 
 7     <ImageView
 8         android:id="@+id/imgVImaxen_UD04_04_Spinners"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:layout_weight="1"
12          />
13 
14     <TextView
15         android:id="@+id/txtTexto_UD04_04_Spinners"
16         android:layout_width="wrap_content"
17         android:layout_height="wrap_content"
18         android:layout_weight="2"
19         android:text="TextView" />
20 </LinearLayout>
Como vemos, estamos a definir un layout cun Imageview e un TextView. Pero poderíamos poñer calquera lauyout con calquera número de elementos e deseño.


Partimos dun exercicio anterior onte xa tiñamos definidos nun array os datos do exercicio:
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3 
 4     <string-array name="planetas_UD02_01_spinner">
 5         <item>Mercurio</item>
 6         <item>Venus</item>
 7         <item>Terra</item>
 8         <item>Marte</item>
 9         <item>Xúpiter</item>
10         <item>Saturno</item>
11         <item>Urano</item>
12         <item>Neptuno</item>
13     </string-array>
14 
15 </resources>



  • Agora crearemos o Adaptador.
Despois de facer o anterior teremos un código coma isto:
Arquivo: Adaptador_UD04_04_Spinners
1 public class Adaptador_UD04_04_Spinners extends ArrayAdapter {
2     
3     public AdaptadorPrueba(@NonNull Context context, int resource) {
4         super(context, resource);
5     }
6 }


  • Agora debemos ter claro que o que imos a 'enviar' a este adaptador van ser os datos necesarios para que 'constrúa' a lista de elementos.
No noso caso, queremos que faga unha lista de elementos no que cada elemento vai ser un texto (TextView) e unha imaxe (ImageView).
Polo tanto teremos que enviar un array de cadeas (para o texto) e algo para facer unha imaxe. No exemplo imos a enviarlle imaxes gardadas en /res/drawable/ e polo tanto serán un array de enteiros (lembrar que en /res/ todo se referencia por números facendo uso da clase R). Poderíamos enviar URL e que o ImageView cargase a imaxe de dita URL.
Modificamos o construtor para que recolla estes datos. Como teremos que facer uso deles noutros métodos, crearemos propiedades locais na clase:
 1 public class Adaptador_UD04_04_Spinners extends ArrayAdapter {
 2 
 3     int[] imaxes;   // Lista de imaxes a cargar. Lembra que en res todo son números  Se foran url poderíamos cambialo por String[]
 4     String[] textos;
 5     Context mContext;
 6 
 7     public Adaptador_UD04_04_Spinners(@NonNull Context context, int[]imaxes,String[]textos) {
 8         super(context, R.layout.spinner_ud04_04);       // Enviamos o layout que imos utilizar
 9 
10         this.imaxes = imaxes;
11         this.textos = textos;
12         this.mContext = context;
13     }
14     
15 }
Fixarse na liña 8, que enviamos ao construtor da clase ArrayAdapter o layout que vai 'gardar' cada elemento da lista no spinner.


  • Agora, para que funcione o adaptador temos que sobreescribir a lo menos tres métodos:
  • int getCount(): Devolve o número de elementos da lista. Fixarse que no noso caso, o número de elementos da lista o sabemos por calquera dos dous arrays onde gardamos os datos. Se creamos catro elementos, a propiedade textos.length será catro.
  • View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent): Este método ten que devolver o view de cada elemento da fila. Fixarse que este 'view' no noso caso será un LinearLayout formado por dos Views (un TextView e un ImageView). E o view que visualiza o elemento escollida da lista.
  • View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent): Devolve o view que conforma cada elemento da lista cando prememos sobre a mesma (a lista despregable). Pero aquí amosamos o view de cada elemento da lista despregable.
Fixarse que no caso dos métodos que devolven un view, podemos facer nos un dinamicamente e devolvelo.
Por exemplo, se fago isto:
1     @Override
2     public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
3         TextView proba = new TextView(mContext);
4         proba.setText("OOOOOOOO");
5         return proba;
6     }
Aparece isto:
PDM adaptador 11.jpg
Isto sería útil se queremos que cando escollamos un elemento apareza unha información e cando estea escollido apareza outra. Máis información neste enlace.


No noso caso queremos que visualmente apareza o mesmo cando temos un elemento escollido da lista, como cando aparece o despregable da mesma.
Como o view que representa o elemento seleccionado da lista o obtemos do método getView (como comentamos antes) o código quedaría así:
 1     @Override
 2     public int getCount() {
 3         return textos.length;       // Poderíamos poñer tamén o número de imaxes.
 4     }
 5 
 6     @Override
 7     public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
 8         return getView(position,convertView,parent);
 9     }
10 
11     @NonNull
12     @Override
13     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
14         return super.getView(position, convertView, parent);
15     }
  • Liña 3: Devolvemos o número de textos enviados como parámetro do constructor.
  • Liña 8: Devolvemos o view do método getView que imos a modificar.



  • Agora ven o máis importante, que é crear o view que conforma o elemento seleccionado no Spinner:
PDM adaptador 12.jpg
O proceso para facelo é o seguinte:
Fixámonos na definición do método getView: public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Ese obxecto da clase View vai 'levar' o layout que temos definido para cada elemento da fila.
E os datos cos que vai encher dito layout van ir no método setTag xa comentado nesta wiki.
  • O primeiro é cargar o layout no 'convertView'.
Para iso temos que pasar o layout dun arquivo XML a un obxecto que poida ser empregado en programación.
Iso se fai facendo un 'inflate':
1             LayoutInflater mInflater = (LayoutInflater) mContext.
2                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
3             convertView = mInflater.inflate(R.layout.spinner_ud04_04, parent, false);
Xa analicaremos en detalle este código. Por agora quedade coa idea de que estas liñas fan que o LinearLayout co botón e a imaxe pasa a ser un obxecto que se garda en convertView.
  • Segundo, debemos de pasar os datos de cada elemento por medio do método setTag.
Non perdades a idea de que a este método se chama 'por cada elemento da lista', sendo o parámetro 'position' o que nos di a posición do elemento que se vai amosar.
Para facer isto, crearemos una clase na que gardaremos os views que se van pasar ao layout. Os views son un ImageView e un TextView:
1     private static class ViewHolder {
2         ImageView mImaxe;
3         TextView mTexto;
4     }
Agora creamos un obxecto de dita clase e indicamos a que se corresponde cada un dese views no arquivo de Layout. Ademáis gardamos dita información no convertView, na propiedade Tag:
1             Adaptador_UD04_04_Spinners.ViewHolder viewFila = new Adaptador_UD04_04_Spinners.ViewHolder();
2 
3             viewFila.mImaxe = (ImageView) convertView.findViewById(R.id.imgVImaxen_UD04_04_Spinners);
4             viewFila.mTexto = (TextView) convertView.findViewById(R.id.txtTexto_UD04_04_Spinners);
5             convertView.setTag(viewFila);
  • Terceiro, asociamos os datos concretos de cada elemento aos datos de cada view e devolvemos o 'convertView':
1         viewFila.mImaxe.setImageResource(imaxes[position]);
2         viewFila.mTexto.setText(textos[position]);
3 
4         return convertView;


  • Cuarto, como a este método se pode chamar varias veces (é dicir, despregar a lista), a segunda ou sucesivas veces que se chame, os datos xa non cambian, polo que non é necesario volver a crear o 'converView' que xa virá con datos, incluído o tag, polo que podemos recuperar ditos datos có método getTag.
Agora se entende por que é necesario chamar ao método setNotifyOnChange(boolean notifyOnChange) ?
O código completo:

Arquivo: Adaptador_UD04_04_Spinners

 1 public class Adaptador_UD04_04_Spinners extends ArrayAdapter {
 2 
 3     int[] imaxes;   // Lista de imaxes a cargar. Lembra que en res todo son números  Se foran url poderíamos cambialo por String[]
 4     String[] textos;
 5     Context mContext;
 6 
 7     public Adaptador_UD04_04_Spinners(@NonNull Context context, int[]imaxes,String[]textos) {
 8         super(context, R.layout.spinner_ud04_04);       // Enviamos o layout que imos utilizar
 9 
10         this.imaxes = imaxes;
11         this.textos = textos;
12         this.mContext = context;
13     }
14 
15     @Override
16     public int getCount() {
17         //return super.getCount();
18         return textos.length;       // Poderíamos poñer tamén o número de imaxes.
19     }
20 
21     @Override
22     public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
23         return getView(position,convertView,parent);
24     }
25 
26     private static class ViewHolder {
27         ImageView mImaxe;
28         TextView mTexto;
29     }
30     @NonNull
31     @Override
32     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
33         //return super.getView(position, convertView, parent);
34 
35         Adaptador_UD04_04_Spinners.ViewHolder viewFila = new Adaptador_UD04_04_Spinners.ViewHolder();
36         if (convertView == null) {
37             LayoutInflater mInflater = (LayoutInflater) mContext.
38                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
39             convertView = mInflater.inflate(R.layout.spinner_ud04_04, parent, false);
40             viewFila.mImaxe = (ImageView) convertView.findViewById(R.id.imgVImaxen_UD04_04_Spinners);
41             viewFila.mTexto = (TextView) convertView.findViewById(R.id.txtTexto_UD04_04_Spinners);
42             convertView.setTag(viewFila);
43         } else {
44             viewFila = (Adaptador_UD04_04_Spinners.ViewHolder) convertView.getTag();
45         }
46         viewFila.mImaxe.setImageResource(imaxes[position]);
47         viewFila.mTexto.setText(textos[position]);
48 
49         return convertView;
50     }
51 
52 }



  • Unha vez que temos o adaptador creado teremos que ir á activity e asociar o spinner a dito adaptador.
Como non temos moitas imaxes en /res/ imos asociar a mesma imaxe a todos os elementos da lista. Partimos que temos unha imaxe en res de nome: R.drawable.ok (se non é o caso poñede outra calquera).

Arquivo: UD04_04_Spinners

 1 package es.cursoandroid.cifprodolfoucha.aprendiendo.Adaptadores;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.widget.Spinner;
 6 
 7 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
 8 
 9 public class UD04_04_Spinners extends Activity {
10 
11     private void cargarLista(){
12         String datos[] = getResources().getStringArray(R.array.planetas_UD02_01_spinner);
13         int[] imaxes = new int[datos.length];
14        for(int cont=0;cont<datos.length;cont++){
15            imaxes[cont]=R.drawable.ok;
16        }
17 
18         Spinner lista = findViewById(R.id.spnLista_UD04_04_Spinners);
19 
20         Adaptador_UD04_04_Spinners meuAdaptador = new Adaptador_UD04_04_Spinners(this,imaxes,datos);
21         lista.setAdapter(meuAdaptador);
22 
23     }
24 
25     @Override
26     protected void onCreate(Bundle savedInstanceState) {
27         super.onCreate(savedInstanceState);
28         setContentView(R.layout.activity_ud04_04__spinners);
29 
30         cargarLista();
31     }
32 }






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