Diferencia entre revisiones de «Spinner con Adaptadores»

De MediaWiki
Ir a la navegación Ir a la búsqueda
 
(No se muestran 17 ediciones intermedias de 2 usuarios)
Línea 30: Línea 30:
  
  
* Dentro do paquete '''Adaptadores''' crear unha nova 'Empty Activity' de nome: '''UD04_01_spinner_base''' de tipo Launcher.
+
* Dentro do paquete '''Adaptadores''' (crea un se non o tes creado antes) imos crear unha nova 'Empty Activity' de nome: '''UD04_01_spinner_base''' de tipo Launcher.
 
: Modifica o arquivo '''AndroidManifiest.xml''' e engade unha label á activity como [http://wiki.cifprodolfoucha.es/index.php?title=PDM_Creando_proxecto_base xa vimos na creación do proxecto base].
 
: Modifica o arquivo '''AndroidManifiest.xml''' e engade unha label á activity como [http://wiki.cifprodolfoucha.es/index.php?title=PDM_Creando_proxecto_base xa vimos na creación do proxecto base].
  
Línea 36: Línea 36:
  
 
<br />
 
<br />
===Código do Layoout===
+
 
 +
===Código do Layout===
  
 
<syntaxhighlight lang="xml" enclose="div" highlight="">
 
<syntaxhighlight lang="xml" enclose="div" highlight="">
Línea 273: Línea 274:
  
 
<br />
 
<br />
===Xestionando o evento Click sobre un elemento do spinner===
+
===Xestionando o evento SELECT sobre un elemento do spinner===
* Para xestionar o evento Click debemos empregar a [https://developer.android.com/reference/android/widget/AdapterView.OnItemClickListener?hl=en interface OnItemClickListener].
+
 
 +
* A diferenza do ListView neste view si que podemos capturar o evento de selección.
 +
: Para xestionar o evento Click debemos empregar a [https://developer.android.com/reference/android/widget/AdapterView.OnItemSelectedListener interface OnItemSelectedListener].
  
: '''<u>NOTA IMPORTANTE:</u>''' Cando prememos nun elemento da lista este non queda seleccionado polo que non podemos empregar ningún método o interface que indique 'selected'.
 
  
<syntaxhighlight lang="java" enclose="div" highlight="3,5,22-24">
+
<syntaxhighlight lang="java" enclose="div" highlight="3,5,24">
 
     private void xestionarEventos() {
 
     private void xestionarEventos() {
 
         Spinner spinner = findViewById(R.id.spnSpinner);
 
         Spinner spinner = findViewById(R.id.spnSpinner);
Línea 301: Línea 303:
 
         setContentView(R.layout.activity_u_d04_01__spinner_base);
 
         setContentView(R.layout.activity_u_d04_01__spinner_base);
  
         cargarDatos();
+
         cargarDatos(); /* Xa feito anteriormente */
 
         xestionarEventos();
 
         xestionarEventos();
 
     }
 
     }
Línea 333: Línea 335:
 
             @Override
 
             @Override
 
             public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
 
             public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                 Toast.makeText(getApplicationContext(), datos.get(i).toString(),Toast.LENGTH_SHORT).show();
+
                 Toast.makeText(getApplicationContext(), datos.get(i),Toast.LENGTH_SHORT).show();
 
                 EditText et = findViewById(R.id.edtEditElemLista);
 
                 EditText et = findViewById(R.id.edtEditElemLista);
                 et.setText(adapterView.getItemAtPosition(i).toString());
+
                 et.setText(datos.get(i));
 
             }
 
             }
  
Línea 383: Línea 385:
  
 
<br />
 
<br />
 +
 
===Accedendo ao elemento seleccionado da lista===
 
===Accedendo ao elemento seleccionado da lista===
  
Línea 409: Línea 412:
  
 
<br/>
 
<br/>
===Operacións sobre a lista===
+
===Operacións sobre o spinner===
  
 
* Podemos facer alta, baixas e modificacións.
 
* 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 ao Spinner.
 
: 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 ao Spinner.
 +
 +
 +
* O ArrayAdapter está 'cheo' de métodos para engadir, borrar e modificar elementos da lista.
 +
: Por exemplo:
 +
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#add(T) add(T object)]: Engade un elemento á lista de elementos.
 +
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#addAll(java.util.Collection%3C?%20extends%20T%3E) addAll(Collection<? extends T> collection)]: Engade unha colección.
 +
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#getCount() getCount()]: Devolve o número de elementos da lista.
 +
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#getItem(int) getItem(int position)]: Devolve o elemento da lista en base a súa posición (empeza en 0)
 +
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#insert(T,%20int) insert(T object, int index) ]: Engade un elemento na posición indicada.
 +
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#remove(T) remove(T object)]: Elimina un elemento da lista.
 +
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#clear() clear()]: Borra todos os elementos da lista.
 +
 +
 
: Imos velos das dúas formas:
 
: Imos velos das dúas formas:
  
Línea 543: Línea 559:
 
: Liña 10: Engadimos o novo elemento.
 
: Liña 10: Engadimos o novo elemento.
  
 +
 +
: '''NOTA IMPORTANTE:''' Se estamos a traballar con obxectos (é dicir, o que gardamos como elemento da lista é un obxecto dunha clase definida por nos, no que sobrescribimos o método toString para indicar o que queremos visualizar), podemos non borrar e crear un novo obxecto, xa que cando obtemos o elemento seleccionado estamos a obter a dirección de memoria onde ese obxecto está aloxado.
 +
: Polo tanto se modificamos o obxecto seleccionado é como si modificaramos o elemento do array de datos e podemos empregar a segunda forma (ven explicado a continuación) informando ao adaptador que houbo un cambio nos datos.
  
 
<br />
 
<br />
 +
 
=====Accedendo ao array de datos=====
 
=====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).
 
* 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).
<syntaxhighlight lang="java" enclose="div" highlight="10,11">
+
<syntaxhighlight lang="java" enclose="div" highlight="10,11,12">
 
         findViewById(R.id.btnModificar).setOnClickListener(new View.OnClickListener() {
 
         findViewById(R.id.btnModificar).setOnClickListener(new View.OnClickListener() {
 
             @Override
 
             @Override
Línea 558: Línea 578:
  
 
                 ArrayAdapter adaptador = (ArrayAdapter)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
 
                 ArrayAdapter adaptador = (ArrayAdapter)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
                 datos.remove(spinner.getSelectedItemPosition());
+
                 datos.set(spinner.getSelectedItemPosition(),et.getText().toString());
                datos.add(spinner.getSelectedItemPosition(),et.getText().toString());
 
 
                 adaptador.notifyDataSetChanged();
 
                 adaptador.notifyDataSetChanged();
  
Línea 567: Línea 586:
 
: Liña 10,11: Borramos e engadimos o elemento ao array de datos.
 
: Liña 10,11: Borramos e engadimos o elemento ao array de datos.
  
 +
 +
* Se estamos a traballar con obxectos (é dicir, o que gardamos como elemento da lista é un obxecto dunha clase definida por nos, no que sobrescribimos o método toString para indicar o que queremos visualizar), podemos non borrar e crear un novo obxecto, xa que cando obtemos o elemento seleccionado estamos a obter a dirección de memoria onde ese obxecto está aloxado.
 +
: Polo tanto se modificamos o obxecto seleccionado é como si modificaramos o elemento do array de datos e podemos empregar a segunda forma (ven explicado a continuación) informando ao adaptador que houbo un cambio nos datos.
 +
: Quedaría o seguinte código supoñendo que temos engadido ao spinner un ArrayList<Clase> sendo <Clase> unha clase creada por nos e có método toString implementado.
 +
<syntaxhighlight lang="java" enclose="div" highlight="10,11,12">
 +
        findViewById(R.id.btnModificar).setOnClickListener(new View.OnClickListener() {
 +
            @Override
 +
            public void onClick(View view) {
 +
                EditText et = findViewById(R.id.edtEditElemLista);
 +
                Spinner spinner = findViewById(R.id.spnSpinner);
 +
 +
                if ((et.getText().toString().length()==0) || (spinner.getSelectedItemPosition()==AdapterView.INVALID_POSITION)) return;
 +
 +
                ArrayAdapter<Clase> adaptador = (ArrayAdapter)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
 +
                Clase clase = spinner.getSelectedItemPosition());
 +
                clase.propiedade = 'Valor modificado'
 +
                adaptador.notifyDataSetChanged();
 +
 +
            }
 +
        });
 +
</syntaxhighlight>
 +
 +
 +
 +
<br />
 +
 +
====Operación Seleccionar====
 +
 +
* No spinner podemos seleccionar un elemento da lista por programación facilmente chamando ao [https://developer.android.com/reference/android/widget/AbsSpinner#setSelection(int) método setSelection(int pos)], indicando no parámetro a posición do elemento a seleccionar, tendo en conta que a posición do primeiro elemento empeza en 0.
  
  
Línea 572: Línea 620:
  
 
<br />
 
<br />
 +
 
==Outros métodos==
 
==Outros métodos==
 
* Para crear un ArrayAdapter tamén podemos empregar este código:
 
* Para crear un ArrayAdapter tamén podemos empregar este código:
Línea 608: Línea 657:
  
  
* O ArrayAdapter está 'cheo' de métodos para engadir, borrar e modificar elementos da lista.
 
: Por exemplo:
 
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#add(T) add(T object)]: Engade un elemento á lista de elementos.
 
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#addAll(java.util.Collection%3C?%20extends%20T%3E) addAll(Collection<? extends T> collection)]: Engade unha colección.
 
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#getCount() getCount()]: Devolve o número de elementos da lista.
 
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#getItem(int) getItem(int position)]: Devolve o elemento da lista en base a súa posición (empeza en 0)
 
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#insert(T,%20int) insert(T object, int index) ]: Engade un elemento na posición indicada.
 
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#remove(T) remove(T object)]: Elimina un elemento da lista.
 
:* [https://developer.android.com/reference/android/widget/ArrayAdapter.html#clear() clear()]: Borra todos os elementos da lista.
 
  
 
* '''<u>Importante:</u>'''  
 
* '''<u>Importante:</u>'''  
Línea 1012: Línea 1052:
  
 
<br />
 
<br />
==Caso práctico 4: Personalizando o deseño==
+
==Caso práctico 4: Creando un adaptador propio==
  
 
* Dentro do paquete '''Adaptadores''' crear unha nova 'Empty Activity' de nome: '''UD04_04_Spinners''' de tipo Launcher e sen compatibilidade.
 
* Dentro do paquete '''Adaptadores''' crear unha nova 'Empty Activity' de nome: '''UD04_04_Spinners''' de tipo Launcher e sen compatibilidade.
Línea 1287: Línea 1327:
  
  
 +
 +
<br />
 +
'''[https://wiki.cifprodolfoucha.es/index.php?title=Programaci%C3%B3n_de_dispositivos_m%C3%B3biles#UNIDADE_4:_Adaptadores Enlace a la página principal de la UD4]'''
 +
<br />
 +
<br />
 +
'''[https://wiki.cifprodolfoucha.es/index.php?title=Programación_de_dispositivos_móbiles Enlace a la página principal del curso]'''
 +
<br />
  
  

Revisión actual del 18:52 6 abr 2021

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 dende recursos gardados en /res/values de tipo array.



Caso Base

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



Preparando a Activity

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


  • Dentro do paquete Adaptadores (crea un se non o tes creado antes) imos crear unha nova 'Empty Activity' de nome: UD04_01_spinner_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 Layout

<?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_Spinner_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" />

    <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" />

    <Spinner
        android:id="@+id/spnSpinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:popupBackground="@android:color/holo_orange_light"
        android:spinnerMode="dropdown"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</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



Cargando datos dende o código

  • Nota: Quitamos a carga de datos se a temos na propiedade 'entries' do Spinner como vimos no punto anterior.
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3 
 4     <string-array name="array_datos_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>



  • Imos agora a ver o código necesario para cargar de datos ao spinner por programación.
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 do spinner. 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_spinner_item (podedes ver o deseño dende Android Studio premendo a tecla Control e premendo có rato sobre simple_spinner_item).
Por outra banda necesitamos algún tipo de array que garde os datos.
Nota: Indicar que o spinner ten dos views diferentes:
  • O view que visualiza o elemento que se atopa no spinner.
  • O view que aparece cando prememos no spinner e se despregan todos os elementos. Cada un deles está nun view.


public class UD04_01_Spinner_base extends AppCompatActivity {

    private void cargarDatos(){

        String[]datos = getResources().getStringArray(R.array.array_datos_spinner);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item,datos);

        // adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);  // View que aparece cando prememos no spinner

        Spinner spinner = findViewById(R.id.spnSpinner);
        spinner.setAdapter(adapter);
    }

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

        cargarDatos();
    }
}
Liña 5: Como vemos obtemos os datos do string-array creado en /res/values no punto anterior.
Liña 6: Creamos un adaptador empregando un layout do S.O. e mandamos os datos.
Liña 8: Podemos cambiar o layout que se vai cargar ao despregar os elementos do spinner.
Liña 11: Asociamos o adaptador ao spinner(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_spinner_item,datos);

        // adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);  // View que aparece cando prememos no spinner

        Spinner spinner = findViewById(R.id.spnSpinner);
        spinner.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_spinnerview

::<?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/txvElemSpinner"
        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_spinnerview,R.id.txvElemSpinner,datos);

        Spinner spinner = findViewById(R.id.spnSpinner);
        spinner.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 SELECT sobre un elemento do spinner

  • A diferenza do ListView neste view si que podemos capturar o evento de selección.
Para xestionar o evento Click debemos empregar a interface OnItemSelectedListener.


    private void xestionarEventos() {
        Spinner spinner = findViewById(R.id.spnSpinner);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                Toast.makeText(getApplicationContext(), adapterView.getItemAtPosition(i).toString(),Toast.LENGTH_SHORT).show();
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText(adapterView.getItemAtPosition(i).toString());
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });

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

        cargarDatos();  /* Xa feito anteriormente */
        xestionarEventos();
    }
Liña 5: No método da interface OnItemSelectedListener 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_Spinner_base extends AppCompatActivity {

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

    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_spinnerview,R.id.txvElemSpinner,datos);

        Spinner spinner = findViewById(R.id.spnSpinner);
        spinner.setAdapter(adapter);
    }

    private void xestionarEventos() {
        Spinner spinner = findViewById(R.id.spnSpinner);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                Toast.makeText(getApplicationContext(), datos.get(i),Toast.LENGTH_SHORT).show();
                EditText et = findViewById(R.id.edtEditElemLista);
                et.setText(datos.get(i));
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });

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

        cargarDatos();
        xestionarEventos();
    }
}
Liña 3: Definimos o array a nivel de Activity para poder acceder a el dende calquera parte.
Liña 18: 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:
    private void xestionarEventos() {
        Spinner spinner = findViewById(R.id.spnSpinner);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                TextView tv = view.findViewById(R.id.txvElemSpinner);
                tv.setTextColor(Color.RED);

                Toast.makeText(getApplicationContext(), datos.get(i).toString(),Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });

    }



Accedendo ao elemento seleccionado da lista

Como curiosidade, indicar que tamén podemos acceder ao View seleccionado chamando ao método getSelectedView().
  • No exemplo, imos facer que cando prememos sobre o EditText, apareza a posición e o texto do elemento seleccionado no Spinner:
        findViewById(R.id.edtEditElemLista).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Spinner spinner = findViewById(R.id.spnSpinner);
                if (spinner.getSelectedItem()==null) return;    // Nada seleccionado
                Toast.makeText(getApplicationContext(), spinner.getSelectedItem().toString() + " NA POSICION " + String.valueOf(spinner.getSelectedItemPosition()),Toast.LENGTH_SHORT).show();

                View layout_elem_spinner = spinner.getSelectedView();   // Podemos acceder ao view seleccionado
                TextView tv = layout_elem_spinner.findViewById(R.id.txvElemSpinner);
                tv.setTextColor(Color.RED);
            }
        });



Operacións sobre o spinner

  • 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 ao Spinner.


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


Imos velos das dúas formas:


Operación de baixa

  • Sabemos, como vimos no exemplo anterior, como obter a posición do elemento seleccionado.



Accedendo ao adaptador
Supoñemos que non temos acceso ao array de datos (por estar definido localmente, por exemplo).
        findViewById(R.id.btnBaixa).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Spinner spinner = findViewById(R.id.spnSpinner);
                if (spinner.getSelectedItemPosition()==AdapterView.INVALID_POSITION) return;

                ArrayAdapter<String> adapter = (ArrayAdapter)spinner.getAdapter();
                String datoEliminar = (String)spinner.getSelectedItem();
                adapter.remove(datoEliminar);
                
            }
        });
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).
Revisar que a carga de datos empreguedes o array definido a nivel de Activity.
        findViewById(R.id.btnBaixa).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Spinner spinner = findViewById(R.id.spnSpinner);
                if (spinner.getSelectedItemPosition()==AdapterView.INVALID_POSITION) return;

                ArrayAdapter<String> adapter = (ArrayAdapter)spinner.getAdapter();
                datos.remove(spinner.getSelectedItemPosition());
                adapter.notifyDataSetChanged();

            }
        });
Liña 8: Eliminamos o elemento do 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 Alta


Accedendo ao adaptador
Supoñemos que non temos acceso ao array de datos (por estar definido localmente, por exemplo).
        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)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
                adaptador.add(et.getText().toString());

            }
        });
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 do Spinner.
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)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
                datos.add(et.getText().toString());
                adaptador.notifyDataSetChanged();

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





Operación de Modificar


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

                Spinner spinner = findViewById(R.id.spnSpinner);
                ArrayAdapter adaptador = (ArrayAdapter)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
                adaptador.remove(spinner.getSelectedItem());
                adaptador.insert(et.getText().toString(),spinner.getSelectedItemPosition());
                
            }
        });
Liña 9: Borramos o elemento seleccionado.
Liña 10: Engadimos o novo elemento.


NOTA IMPORTANTE: Se estamos a traballar con obxectos (é dicir, o que gardamos como elemento da lista é un obxecto dunha clase definida por nos, no que sobrescribimos o método toString para indicar o que queremos visualizar), podemos non borrar e crear un novo obxecto, xa que cando obtemos o elemento seleccionado estamos a obter a dirección de memoria onde ese obxecto está aloxado.
Polo tanto se modificamos o obxecto seleccionado é como si modificaramos o elemento do array de datos e podemos empregar a segunda forma (ven explicado a continuación) informando ao adaptador que houbo un cambio nos datos.


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);
                Spinner spinner = findViewById(R.id.spnSpinner);

                if ((et.getText().toString().length()==0) || (spinner.getSelectedItemPosition()==AdapterView.INVALID_POSITION)) return;

                ArrayAdapter adaptador = (ArrayAdapter)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
                datos.set(spinner.getSelectedItemPosition(),et.getText().toString());
                adaptador.notifyDataSetChanged();

            }
        });
Liña 10,11: Borramos e engadimos o elemento ao array de datos.


  • Se estamos a traballar con obxectos (é dicir, o que gardamos como elemento da lista é un obxecto dunha clase definida por nos, no que sobrescribimos o método toString para indicar o que queremos visualizar), podemos non borrar e crear un novo obxecto, xa que cando obtemos o elemento seleccionado estamos a obter a dirección de memoria onde ese obxecto está aloxado.
Polo tanto se modificamos o obxecto seleccionado é como si modificaramos o elemento do array de datos e podemos empregar a segunda forma (ven explicado a continuación) informando ao adaptador que houbo un cambio nos datos.
Quedaría o seguinte código supoñendo que temos engadido ao spinner un ArrayList<Clase> sendo <Clase> unha clase creada por nos e có método toString implementado.
        findViewById(R.id.btnModificar).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EditText et = findViewById(R.id.edtEditElemLista);
                Spinner spinner = findViewById(R.id.spnSpinner);

                if ((et.getText().toString().length()==0) || (spinner.getSelectedItemPosition()==AdapterView.INVALID_POSITION)) return;

                ArrayAdapter<Clase> adaptador = (ArrayAdapter)((Spinner)findViewById(R.id.spnSpinner)).getAdapter();
                Clase clase = spinner.getSelectedItemPosition());
                clase.propiedade = 'Valor modificado'
                adaptador.notifyDataSetChanged();

            }
        });



Operación Seleccionar

  • No spinner podemos seleccionar un elemento da lista por programación facilmente chamando ao método setSelection(int pos), indicando no parámetro a posición do elemento a seleccionar, tendo en conta que a posición do primeiro elemento empeza en 0.




Outros métodos

  • Para crear un ArrayAdapter tamén podemos empregar 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).


  • Importante:
  • Se o array que se lle envía ao adaptador coma fonte de datos non é dinámico (por exemplo, un Array de Strings) non poderemos facer ningunha operación sobre os datos.
  • 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: Creando un adaptador propio

  • 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 escollido 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). No exemplo imos amosar o mesmo que en getView (pero non tería por qué ser así).
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.
Ese obxecto que aparece como parámetro (convertView) representa o conxunto de elementos gráficos que conforman cada fila do spinner.
A primeira vez que se chama, ese obxecto vale 'null' e debemos de facer que sexa igual ao layout no que definimos a imaxe e o texto que conforman cada elemento da lista.
A segunga e seguintes veces, ese dato xa virá co layout asociado.


  • 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.
Como dixemos antes, isto soamente hai que facelo se o convertView ven cun valor null.
 1     @Override
 2     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
 3 
 4         if (convertView == null) {
 5 
 6             LayoutInflater mInflater = (LayoutInflater) mContext.
 7                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 8             convertView = mInflater.inflate(R.layout.spinner_ud04_04, parent, false);
 9         }
10         .............
11     }


  • Segundo, debemos pasar os datos a cada un dos elementos gráficos que se atopan no obxecto 'convertView'.
Para iso podemos empregar o método findViewById no convertView. O único que temos que facer é buscar cada compoñente gráfico e asociarlle o dato que sabemos cal é polo parámetro 'int position', que indica a posición do elemento da lista seleccionado.
 1     @Override
 2     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
 3 
 4         if (convertView == null) {
 5 
 6             LayoutInflater mInflater = (LayoutInflater) mContext.
 7                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 8             convertView = mInflater.inflate(R.layout.spinner_ud04_04, parent, false);
 9         }
10         ((ImageView)convertView.findViewById(R.id.imgVImaxen_UD04_04_Spinners)).setImageResource(imaxes[position]);
11         ((TextView) convertView.findViewById(R.id.txtTexto_UD04_04_Spinners)).setText(textos[position]);
12 
13         return convertView;
14 
15     }


Nota: Lembrar que como xa vimos no caso do ImageView, podemos empregar os métodos getTag() e setTag() para enviar datos e asocialos a un view.




  • 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_Spinner);
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 }




Enlace a la página principal de la UD4

Enlace a la página principal del curso




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