Android/Layouts predefinits pels adapters

Introducció

modifica

Què és un Layout?

modifica

Un Layout és una vista (View) que en pot contenir d'altres, que controla el comportament i la posició de les Views que conté. Els Layouts es poden declarar de dues formes:

  • Declarar els elements UI en fitxers XML. Android proveeix un vocabulari XML que correspon a les classes i subclasses View.
  • Crear elements Layout en temps d'execució. Una aplicació pot crear objectes View i ViewGroup (i manipular les seves propietats) a través de la programació Java.

Què és una ListView?

modifica

Per a mostrar una llista en Android es fa ús d'una View anomenada ListView. És un contenidor de Views anònimes, disposades en forma de llistat, on cada fila és una View anònima.




El poblament del ListView significa generar tantes Views anònimes com files tingui la llista a visualitzar. El poblament pot ser estàtic o dinàmic.

El poblament estàtic és aquell que es realitza amb les dades estàtiques del XML, quan fem la construcció de l'arbre de views (procés d'inflat).

Per a realitzar un poblament dinàmic en una ListView cal:

  • Font de dades. Es necessari un mecanisme homogeni que permeti alimentar les ListViews amb diferents fonts de dades.
  • Generació de fila. Es necessari un mecanisme que es preocupi de construir una View anònima per cada fila, i que la pobli dinàmicament a partir de les dades de la font de dades.

Els Adapters resolen ambdues necessitats!

Què és un Adapter?

modifica
 
Imatge 1. Esquema dels adapters

Un Adapter actua com a pont entre un AdapterView (ex. ListView) i la capa de dades que hi ha per sota de la View. L'Adapter proveeix accés als items de dades. També és responsable de crear una View per a cada item en el conjunt de dades.
En la part esquerra del diagrama es pot observar com hi ha una font de dades (DataSource) la qual pot provenir d'una ArrayList o d'un Cursor que apunta a una base de dades.
A la dreta apareix una AdapterView (abstract class), una View en què els seus fills són determinats per l'Adapter que tingui assignat. Alguns exemples d'especificació d'un AdapterView són: ListView, GridView i Spinner.
Finalment es pot veure com l'Adapter fa de pont entre els dos elements del diagrama, construeix les Views filles per a l'AdapterView amb la informació de la font de dades.


Exemple ArrayAdapter:




L'Adapter pren la font de dades proporcionada i l'utilitza per a poblar la ListView usant el layout indicat. En aquest cas, un layout predefinit pel framework Android.

Constructor de l'ArrayAdapter

modifica



  • context: El context actual
  • resource: La id d'un arxiu layout que conté un TextView que, quan es creïn les instàncies a les views, es poblarà amb la informació de la font de dades. Si no s'indica textViewResourceId l'identificador d'aquest TextView és text1
  • textViewResourceId: La id del TextView que ha de ser poblat
  • objects: Els objectes que s'han de representar a la ListView; és la font de dades


Constructor del SimpleCursorAdapter

modifica



  • context: El context on la ListView associada amb el SimpleCursorAdapter està executant-se.
  • layout: Identificador del recurs d'un fitxer layout el qual defineix les Views per aquest item de la llista. El fitxer hauria de contindre almenys les Views anomenades en "to".
  • c: El cursor de la base de dades. Pot ser null si el cursor encara no està disponible.
  • from: Una llista de noms de columna representant les dades unides a la UI.
  • to: Les Views que s'haurien de mostrar en les columnes definides en el paràmetre "from". Haurien de ser totes TextViews.
  • flags: Flags utilitzades per determinar el comportament de l'adapter. Veure FLAG_AUTO_REQUERY i FLAG_REGISTER_CONTENT_OBSERVER.


Layouts predefinits pels adapters

modifica

El SDK d'Android proporciona una col·lecció de layouts fila predefinits (declaració XML) per a ser utilitzats en els Adapters. Aquests layouts corresponent a una fila de la llista.
Els podem trobar en: android.R.layout.xxxx on xxxx és el nom del layout predefinit.
O en android.jar   res\layout\ si accedim des de l'arbre de directoris del SDK.

Fitxer strings.xml

modifica

Aquí s'ofereixen unes mostres que s'han fet servir en tots els exemples per poder comprovar ràpidament el funcionament dels adapters. Tens total llibertat per modificar aquest fitxer.




simple_list_item_1.xml

modifica

Layout fila compost per un TextView.

 
Exemple d'ús del layout simple_list_item_1.

ArrayAdapter

modifica

Per a inflar la ListView s'utilitzarà l'Adapter predefinit ArrayAdapter. Aquest serà l'encarregat de generar les diferents files (Views) amb la font de dades corresponent (Array).

ExampleSimpleListItem1.java
modifica




Comentaris sobre el codi
modifica

En aquest metode es carreguen les dades que s'han mostrat a la llista:

carregaDades();


Per fer-ho, cal accedir a les dades de tipus string desades com a recurs XML. Per defecte l'arxiu XML on s'ha definit aquest recurs és l'strings.xml:

dadesEstatiques = getResources().getStringArray(R.array.contingut_llista_1);


En aquest metode s'obté i es prepara la ListView per què s'adapti segons les necessitats:

inicialitza();


En primer lloc s'obté la ListView des de la layout:

llistaContenidora = (ListView) findViewById(R.id.llista);


I tot seguit s'enllaça la ListView obtinguda amb l'adapter:

llistaContenidora.setAdapter(adaptador);

simple_list_item_2.xml

modifica

Layout fila compost per dos TextView, cada un representat per una mida i colors diferents.
En aquest tipus de layout amb dos TextView, l'ArrayAdapter no funciona correctament, ja que ell espera un layout amb un sol TextView. Per solucionar aquest problema existeixen dues possibles solucions:

  • Crear un custom Adapter. Adapter personalitzat.
  • Utilitzar un SimpleCursorAdapter.
 
Exemple d'ús del layout "simple_list_item_2".

Custom Adapter

modifica

A partir de l'ArrayAdapter es crearà un Adapter personalitzat (Custom) que suporti un layout amb dues TextViews.
Caldrà fer un constructor nou i Override del mètode getView.

CustomAdapter.java
modifica



Classe Person.java:




Comentaris sobre el codi
modifica

Si es el primer cop i no hi ha cap fila creada, s'infla la view amb el layout predefinit

if (row == null) {
        row = inflater.inflate(layoutId, parent, false);
}


Pobem els dos TextView amb les dades guardades a la llista de persones

name.setText(this.persons.get(position).getName());
email.setText(this.persons.get(position).getEmail());


ExempleSimpleListItem2.java
modifica




Comentaris sobre el codi
modifica

Per emmagatzemar les dades del XML es farà ús d'un ArrayList de Persona. Aquesta classe contindrà els atributs nom i e-mail.

for (int i = 0; i < dadesName.length; i++) {
    dadesEstatiques.add(new Person(dadesName[i], dadesEmail[i]));
}


SimpleCursorAdapter

modifica

Per a la utilització d'un SimpleCursorAdapter, s'haurà d'emprar un cursor, la qual cosa obliga a fer ús d'una base de dades.

Amb l'ArrayAdapter si no dèiem res cada String de la font de dades poblava el TextView del layout anomenat text1. Ara però cal fer el mapeig de les columnes del Cursor a les Views del layout de fila de la ListView manualment.

AndroidManifest.xml
modifica

En el següent exemple s'utilitzarà la base de dades dels contactes que incorpora Android, per tant caldrà sol·licitar el permís READ_CONTACTS en el AndroidManifest.xml:

<uses-permission
        android:name="android.permission.READ_CONTACTS" />
ExampleSimpleListItem2SimpleCursorAdapter.java
modifica

Fent ús de la mateixa base de dades dels contactes del dispositiu Android, facilitarà la representació de l'exemple:




Comentaris sobre el codi
modifica

Creació d'un array d'Strings que especifica la llista de columnes que retornarà la consulta de la base de dades.

String[] projection = new String[]{
                Data._ID,
                Data.DISPLAY_NAME,
                Phone.TYPE,
                Phone.NUMBER};


Creació d'un Cursor a partir de la consulta a la base de dades.

        Cursor cursor = getContentResolver().query(
                Data.CONTENT_URI,
                projection,
                Data.MIMETYPE + "= '" + Phone.CONTENT_ITEM_TYPE + "'"
                        + " AND " + Phone.NUMBER + " IS NOT NULL",
                null,
                null);

L'equivalència a una consulta SQL seria la següent:

  • Data.CONTENT_URI   la font de les dades (FROM)
  • projection   la llista de les columnes a retornar (SELECT)
  • Data.MIMETYPE + [...] + "IS NOT NULL"   filtre que declara quines columnes seran retornades (WHERE)
  • null   reemplaça qualsevol interrogant (?) en la selecció anterior (complementari al WHERE)
  • null   ordena les files retornades (ORDER BY)


Llista de les columnes que utilitzarà l'adapter per a la creació de les files.

String[] columns = new String[]{Data.DISPLAY_NAME, Phone.NUMBER};


Per a la creació del SimpleCursorAdapter es necessitarà el Context, el layout predefinit, un Cursor de la BD, columnes que utilitzarà l'adapter, els layouts enllaçats als TextViews i un flag que determina el comportament de l'adapter.

adaptador = new SimpleCursorAdapter(
                this,
                android.R.layout.simple_list_item_2,
                cursor,
                columns,
                new int[] {android.R.id.text1, android.R.id.text2},
                1);


simple_list_item_single_choice.xml

modifica

Layout fila compost per un TextView i un RadioButton.

ArrayAdapter

modifica

Per a inflar la ListView s'utilitzarà l'Adapter predefinit ArrayAdapter. Aquest serà l'encarregat de generar les diferents files (Views) amb la font de dades corresponent (Array).

ExempleSimpleListItemSingleChoice.java
modifica




Comentaris sobre el codi
modifica

Defineix quin tipus de selecció s'aplica a la llista. En aquest cas es podrà escollir únicament una opció.

llistaContenidora.setChoiceMode(ListView.CHOICE_MODE_SINGLE);


Recuperació i marcació a través de codi

modifica

Per recuperar la posició marcada es fa servir aquest metode sobre la ListView:

int posicio = llistaContenidora.getCheckedItemPosition();


De la mateixa manera per marcar una de les posicions només se li ha d'especificar quina d'elles.

int posicio = 4;
llistaContenidora.setItemChecked(posicio, true);


A continuació, es mostra una altra forma de recuperar i marcar a través de codi fen servir un custom adapter. Al fer servir un custom adapter alliberem responsabilitats de l'activity i es delega a l'adapter la gestió del marcatge i de la recuperació de les dades.





I aquest és el custom adapter:





simple_list_item_multiple_choice.xml

modifica

Layout fila compost per un TextView i un CheckBox.

ArrayAdapter

modifica

Per a inflar la ListView s'utilitzarà l'Adapter predefinit ArrayAdapter. Aquest serà l'encarregat de generar les diferents files (Views) amb la font de dades corresponent (Array).

ExampleSimpleListItemMultipleChoice.java
modifica




Comentaris sobre el codi
modifica

Defineix quin tipus de selecció s'aplica a la llista. En aquest cas es podrà escollir més d'una opció.

llistaContenidora.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);


Recuperació i marcació a través de codi

modifica

Per recuperar quines posicions estan marcades es fa servir aquest mètode sobre la ListView.

SparseBooleanArray posicio = llistaContenidora.getCheckedItemPositions();

Retornarà un Array de booleans amb les posicions marcades (true) i no marcades (false).
Exemple per recórrer totes les posicions i saber si han estat marcades o no.

for (int i = 0; i < adaptador.getCount(); i++){
            Log.i("checked?", String.valueOf(positions.get(i)));
        }


De la mateixa manera per marcar una de les posicions només se li ha d'especificar quina d'elles.

int posicio = 4;
llistaContenidora.setItemChecked(posicio, true);


A continuació, es mostra una altra forma de recuperar i marcar a través de codi fen servir un custom adapter. Al fer servir un custom adapter alliberem responsabilitats de l'activity i es delega al adapter la gestió del marcatge i de la recuperació de les dades.





I aquest és el custom adapter:




Referències

modifica