Está en la página 1de 20

CURSO DE DESARROLLO DE APLICACIONES ANDROID

Tema 4

Layouts
TEMA 4. LAYOUTS

Introducción

En Android, todos los elementos visuales extienden de la clase View. Los layouts,
concretamente, extienden de la subclase ViewGroup.

Los layouts definen estructuras visuales, contenedores, en la interfaz de usuario y pueden ser,
al igual que el resto de subclases de View, o bien declarados como elementos en archivos
XML, o bien instanciados en tiempo de ejecución.

En general, es conveniente declarar el máximo de elementos visuales a través de XML, en


archivos con extensión .xml almacenados en la carpeta res/layout/. De este modo se consigue
una mejor separación entre la presentación y el código que controla el comportamiento de la
interfaz de usuario. Al definir la interfaz de usuario en archivos XML, se podrán añadir o
modificar los mismos sin necesidad de modificar el código fuente y recompilar la aplicación.
Además, se podrán definir diferentes archivos XML para diferentes tipos de pantalla y para
diferentes orientaciones. El plugin ADT en Eclipse permite previsualizar y modificar este tipo de
archivos XML a través de la pestaña Graphical Layout. También está disponible la herramienta
Hierachy Viewer, que muestra los valores de las propiedades del layout, permite visualizar los
márgenes y padding y renderiza las vistas mientras se está depurando la aplicación, y la
herramienta layoutopt que analiza la estructura de herencia del layout para encontrar
ineficiencias y otros problemas.

El vocabulario que se utiliza en los archivos XML para declarar los elementos coincide con los
nombres de las clases (elementos XML) y métodos (atributos de los elementos XML) 1. Como
todo archivo XML, los archivos de layout contienen un solo elemento raíz, que podrá ser un
objeto de tipo View o ViewGroup. Este elemento contendrá distintos objetos y widgets 2 que
también podrán estar agrupados en diferentes layouts anidados.

Cuando la aplicación es compilada, se creará un recurso de tipo View por cada archivo XML de
layout. Cuando una actividad es creada, tal y como se verá en un próximo tema, se invoca al
método onCreate(), responsable de inicializar la interfaz de usuario cargando el recurso de
tipo View asociado a la actividad a través del método setContentView(). Este método recibe
como parámetro la referencia al recurso R.layout.my_layout, donde my_layout coincide
con el nombre del archivo XML del layout que ha creado dicho recurso de tipo View:

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

1
Es necesario tener en cuenta que Java distingue entre mayúsculas y minúsculas, de forma que la declaración de los
elementos en XML ha de respetar las mayúsculas y minúsculas que contengan los nombres de la clases Java que
definan dichos elementos.
2
Este concepto de widget, en el contexto de un layout, se refiere a objetos con comportamiento ya definido como,
por ejemplo, una barra de progreso (ProgressBar). Un widget, fuera de este contexto, representa una aplicación
que se instala en el escritorio del dispositivo.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 2


TEMA 4. LAYOUTS

Atributos

Los objetos de tipo View y ViewGroup contienen diversos atributos, específicos o heredados,
que pueden ser inicializados a través de XML. Existen atributos comunes a todos los
elementos, puesto que son heredados de View, como por ejemplo el atributo ID (android:id),
así como otro conjunto de atributos referentes al posicionamiento del elemento dentro del
ViewGroup padre que lo contiene (atributos layout: android:layout_***).

• ID. Todos los elementos de los layout (objetos de tipo View) deben tener asociado un
valor entero que los identifique unívocamente dentro de la estructura jerárquica a la
que pertenezcan. Cuando la aplicación es compilada, se referencia cada elemento a
través del valor entero asociado a su ID, definido en la clase R. No obstante, dicho
valor entero es generado automáticamente asignando un string al atributo:

android:id="@+id/textView". 3

Para referenciar a los recursos desde la aplicación, se utilizará el método de contexto


findViewByID() que recibe como parámetro la referencia de la clase R con el
identificador del recurso. Este método devuelve un objeto de tipo View, por lo que se
deberá hacer el cast al tipo de objeto adecuado:

TextView myTextView = (TextView) findViewById(R.id.textView);

Tal y como se ha mencionado, el valor del identificador ha de ser único dentro de la


parte del árbol donde se busque el recurso a través del método findViewByID(). En
la práctica, este método se suele utilizar en las actividades (y en otros tipos de
componentes de la aplicación) y, al invocarlo, se buscará desde el elemento raíz del
layout, por lo que es recomendable utilizar identificadores únicos al menos dentro de
cada layout.

• Atributos layout. Cada elemento View de un layout ha de definir los parámetros


necesarios para posicionarse dentro del ViewGroup en el que reside. Estos
parámetros están implementados en la clase anidada ViewGroup.LayoutParams que
contendrá diversas propiedades de la View asociada como, por ejemplo, su tamaño y
posición. Para cada View contenida en un ViewGroup, existe un objeto
ViewGroup.LayoutParams con sus propiedades.

3
El símbolo @ indica al analizador XML que procese el resto de la cadena de texto como el identificador del recurso.
Al añadir el símbolo +, dicho identificador define un nuevo recurso, por lo que es añadido a la clase R.
También se pueden reutilizar recursos que ya ofrece Android, de forma que estos no sean añadidos a la clase R de la
aplicación. Para ello, se referenciarán sin el símbolo +, pero añadiendo el namespace android:
android:id="@android:id/text1". En las actividades, se deberá importar la clase android.R.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 3


TEMA 4. LAYOUTS

Todas las View han de declarar su anchura y altura a través de los atributos
obligatorios android:layout_width y android:layout_height. El valor de estas
propiedades será establecido, en general, de forma relativa en vez de absoluta. En vez
de, por ejemplo, asignar una anchura específica (android:layout_width="15dp") 4,
se establecerá:

 wrap_content: la anchura (o altura) se ajustará al contenido de la View.


 fill_parent: (o match_parent desde Android 2.2) la anchura (o altura) será
tan grande como permita la ViewGroup contenedora.

• Posición, tamaño, padding (espacio de relleno) y márgenes. Las View son rectángulos
definidos por las coordenadas de su esquina superior izquierda (left -X, top -Y), así
como por su anchura y altura (weight, height). El valor de estas propiedades podrá
obtenerse a través de sus métodos accesores getLeft(), getTop(), etc. Además,
existen métodos adicionales que proporcionarán las coordenadas del resto de
esquinas del rectángulo (getRight(), getBottom()).
El tamaño de una View dentro de su contenedor padre (que incluye el espacio de
relleno −padding) puede ser modificado en el momento de ser mostrado en
diferentes pantallas. Existen, por lo tanto, diferentes métodos para obtener el ancho y
alto asignados inicialmente a una View (getMeasuredWidth() y
getMeasuredHeight()) y para el ancho y alto que finalmente tiene al ser dibujada en
la pantalla (getWidth() y getHeight()).
Los márgenes son asignados solamente en elementos ViewGroup, a través de los
atributos definidos en la clase anidada ViewGroup.MarginParams (accesibles también
en XML), y afectan a los elementos hijos que contenga dicho ViewGroup.

4
No es recomendable establecer valores absolutos en las propiedades layout (posición y tamaño), ya que la interfaz
de usuario no se adaptará correctamente a las diferentes resoluciones de pantalla.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 4


TEMA 4. LAYOUTS

Tipos de layout

Android proporciona un conjunto de subclases de ViewGroup que definen diferentes


esquemas de posicionamiento para las View contenidas. Estos layouts pueden ser anidados
para generar diferentes esquemas, aunque se deberá mantener siempre la mayor simplicidad
posible en el diseño del layout compuesto (anidando los mínimos layouts posibles), de forma
que el dibujado del mismo sea rápido y eficiente.

Entre los diferentes layouts destacan LinearLayout, que organiza sus elementos vertical u
horizontalmente, RelativeLayout, que permite a sus elementos posicionarse de forma
relativa unos respecto a otros, FrameLayout, que superpone todos sus elementos en la
esquina superior izquierda, WebView (subclase de AbsoluteLayout), especializado en mostrar
páginas web 5, y ListView y GridView, que muestran contenido dinámico. Estos dos últimos
layouts son subclases de AdapterView, clase que utiliza un adaptador para enlazar registros
obtenidos de una fuente de datos (como por ejemplo una base de datos), con su layout. El
adaptador actúa como intermediario entre la fuente de datos y la presentación de los mismos,
convirtiendo cada registro obtenido en una View que puede añadirse al layout de tipo
AdapterView, tal y como se verá en la última sección del tema.

El siguiente diagrama de clases muestra las relaciones de herencia de las distintas vistas:

5
El uso de WebView y la creación de aplicaciones web para dispositivos móviles se trata en profundidad en el Curso
Avanzado de Desarrollo de Aplicaciones Android.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 5


TEMA 4. LAYOUTS

LinearLayout

Este layout organiza las vistas que contenga vertical u horizontalmente en función del atributo
android:orientation, que puede adoptar los valores vertical y horizontal. Los
LinearLayout respetan los márgenes entre los elementos hijos así como su gravedad
(android:layout_gravity −alineación derecha, central en la vertical u horizontal, izquierda…
respecto a su padre; android:gravity −alineación derecha, central, izquierda… del
contenido de la vista hija, respecto al espacio que ocupa la propia vista).

Los diseños de los layouts de las aplicaciones han de estar optimizados y deben generar
pantallas cuyos elementos estén dispuestos de forma ordenada y elegante. Es fundamental
que las pantallas de las aplicaciones tengan un diseño perfecto en cualquier resolución y
orientación. Para ayudar a conseguir este objetivo, LinearLayout incluye el atributo
android:layout_weight, el cual asigna peso o importancia a cada elemento del layout,
relativo al espacio que ocupa dentro mismo. Al especificar este atributo 6, el espacio que
ocupan las vistas hijas que lo implementen será ampliado o reducido en función del espacio
disponible en pantalla. Por ejemplo, si un LinearLayout contiene cuatro vistas hijas de las
cuales las dos primeras tienen android:layout_weight="1", la tercera tiene
android:layout_weight="2" y la cuarta no tiene peso asignado, al cambiar el espacio
disponible, la última vista no se expandirá, ocupando únicamente el espacio asignado a través
de su atributo android:layout_width, mientras que el espacio restante disponible será
asignado de forma que las dos primeras vistas ocupen un 25% mientras que la tercera vista
ocupe un 50%, tal y como puede verse en el siguiente ejemplo 7:

<LinearLayout
android:id="@+id/myVerticalLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textViewLinearLayoutExample"
android:orientation="horizontal"
android:scrollbars="horizontal" >

<TextView
android:id="@+id/textViewChild1"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="@string/child_weight1"
android:gravity="center_vertical" />

6
Dicho atributo deberá ser acompañado por android:layout_width="0dp" en LinearLayout con
android:orientation="vertical", o por android:layout_height="0dp" en LinearLayout con
android:orientation="horizontal".
7
Esta porción de código es parte de un layout de tipo RelativeLayout, tal y como puede verse en el código de
ejemplo adjunto a este tema.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 6


TEMA 4. LAYOUTS

<TextView
android:id="@+id/textViewChild2"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="@string/child_weight1"
android:gravity="center_vertical" />

<EditText
android:id="@+id/editTextChild3"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="2"
android:hint="@string/child_weight2"
android:textSize="12sp"
android:gravity="center_vertical" />

<TextView
android:id="@+id/textViewChild4"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:text="@string/child_weight_wrap" />

</LinearLayout>

El aspecto de este layout será el siguiente:

RelativeLayout

A diferencia de LinearLayout, este layout permite a sus elementos hijos que se posicionen de
forma relativa. La posición de cada elemento podrá especificarse de forma relativa a sus
hermanos, o de forma relativa al área que ocupe el RelativeLayout padre.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 7


TEMA 4. LAYOUTS

Este layout permite diseñar interfaces complejas sin necesidad de anidar distintos layouts,
manteniéndose así una estructura jerárquica más plana y, por lo tanto, más eficiente. (Cuantos
más anidamientos existan, más ineficiente será el dibujado de la interfaz de usuario.)

Por defecto, todas las vistas hijas se posicionan en el ángulo superior izquierdo del
RelativeLayout. Por un lado, se podrán posicionar de forma relativa al padre, al especificar
alguno de los siguientes atributos (con valores true o false):

• android:layout_alignParentTop, android:layout_alignParentBottom,
android:layout_alignParentLeft, android:layout_alignParentRight
• android:layout_centerVertical, android:layout_centerHorizontal,
android:layout_centerInParent

Por otro lado, el posicionamiento relativo de las vistas hijas también se realiza referenciando a
otras a través de su identificador (el valor del atributo será una referencia de tipo @+id/):

• android:layout_above, android:layout_below
• android:layout_alignBaseline, android:layout_alignBottom,
android:layout_alignLeft, android:layout_alignRight,
android:layout_alignTop
• android:layout_toRightOf, android:layout_toRightOf

Al utilizar estos atributos, las dependencias de una vistas respecto a otras pueden declararse
en cualquier orden.

En el siguiente ejemplo pueden observarse parte de los atributos mencionados:

<RelativeLayout
android:id="@+id/myRelativeLayoutExample"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/textViewRelativeLayoutExample">

<TextView
android:id="@+id/textViewRelative1"
android:layout_width="wrap_content"
android:layout_height="80dp"
android:text="@string/write_name"
android:layout_alignParentLeft="true" />

<EditText
android:id="@+id/editTextRelative1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:textSize="12sp"
android:hint="@string/name"
android:layout_alignParentRight="true" />

CURSO DE DESARROLLO DE APLICACIONES ANDROID 8


TEMA 4. LAYOUTS

<TextView
android:id="@+id/textViewRelative2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textViewRelative1"
android:layout_toRightOf="@+id/textViewRelative1"
android:text="@string/text_to_right_of_below" />

<EditText
android:id="@+id/editTextRelative2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/textViewRelative2"
android:layout_below="@+id/textViewRelative1"
android:textSize="12sp"
android:hint="@string/another_edittext"
android:layout_alignParentRight="true" />

</RelativeLayout>

El aspecto de este layout será el siguiente:

FrameLayout

Este layout está diseñado para establecer un marco rectangular donde en general solo se
mostrará un ítem. Todos los elementos que contenga este layout serán superpuestos por
defecto en la esquina superior izquierda (siendo el elemento superior, visible, el último
añadido), aunque a través del atributo android:layout_gravity, se podrán posicionar en
distintas zonas 8.

8
La inclusión de varios elementos en distintas posiciones en un FrameLayout está desaconsejada puesto que al
redimensionar el mismo, los elementos podrán solaparse. No obstante, se podrán establecer distintos niveles de
transparencia en el background de dichos elementos, con el objetivo de conseguir diferentes efectos visuales.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 9


TEMA 4. LAYOUTS

En general, este layout es usado para mostrar un solo elemento que es sustituido con
frecuencia (como, por ejemplo, una ImageView).

El tamaño del layout será establecido por el tamaño del hijo más grande, incluyendo su
padding, y en función del valor del atributo android:measureAllChildren, que establecerá si
se tienen en cuenta los elementos hijos invisibles o no.

TableLayout

Layout que organiza sus elementos hijos en filas y columnas (en una tabla, sin bordes).
Contiene un conjunto de elementos TableRow, que tienen cero, una o varias celdas. Cada
celda es una única View. El número de columnas de la tabla queda determinado por la fila que
contenga más columnas. Las celdas de una fila pueden fusionarse, o expandirse (span), para
ocupar varias columnas. Para ello, se establecerá el número de columnas que se expandirá la
View que actúa como celda, a través del atributo android:layout_span.

El ancho de cada columna queda determinado por su celda más ancha, y el ancho de la celda
es siempre el espacio que ocupa la View que actúa como celda, ajustado a su contenido
(wrap_content). No obstante, las columnas pueden ser estiradas o encogidas en función del
espacio disponible, asignando los índices de las columnas (separados por comas, la primera
columna tiene índice 0), a los atributos android:stretchColumns y android:shrinkColumns,
respectivamente, en <TableLayout> 9. Las columnas también pueden ser ocultadas a través de
android:collapseColumns.

El ancho de una tabla siempre es match_parent y no puede ser modificado. Una tabla,
además de contener elementos TableRow, puede contener directamente otras vistas, que
ocuparán, cada una, una fila. La anchura de estas filas se ajustará siempre a la anchura de la
tabla, mientras que la altura sí podrá ser modificada, siendo su valor por defecto
wrap_content (la altura de los elementos TableRow es siempre wrap_content).

El siguiente código aplica los atributos y reglas mencionadas:

9
Estos atributos tienen, al igual que todos los atributos XML, sus correspondientes métodos accesores.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 10


TEMA 4. LAYOUTS

<TableLayout
android:id="@+id/myTableLayoutExample"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/textViewTableLayoutExample"
android:shrinkColumns="2,3"
android:stretchColumns="2,3" >

<!--
Debido a que el ancho y alto está predeterminado (se ajusta al
contenido o al padre) este es uno de los pocos casos en los
cuales no es necesario añadir los atributos layout_width y
layout_height
-->
<TableRow>

<TextView android:text="@string/cell11" />

<TextView android:text="@string/cell12" />

<TextView
android:gravity="center_horizontal"
android:text="@string/cell13" />

<TextView
android:gravity="center_horizontal"
android:text="@string/cell14" />

</TableRow>
<!—
Vista que ocupa una fila completa (único caso en el que se puede
especificar la altura)
-->
<TextView
android:layout_height="40dp"
android:gravity="center_horizontal|center_vertical"
android:text="@string/cell21" />

<!-- Fila donde la primera celda ocupa dos columnas -->


<TableRow>

<TextView
android:layout_span="2"
android:gravity="center_horizontal"
android:text="@string/cell31" />

<TextView
android:gravity="center_horizontal"
android:text="@string/cell33" />

<TextView
android:gravity="center_horizontal"
android:text="@string/cell34" />

</TableRow>

</TableLayout>

CURSO DE DESARROLLO DE APLICACIONES ANDROID 11


TEMA 4. LAYOUTS

La tabla resultante tendrá el siguiente aspecto:

ListView

ListView es una ViewGroup que muestra una lista de ítems con scroll. Los ítems de la lista son
insertados automáticamente en la misma utilizando un Adapter, que extrae contenido de
alguna fuente de datos para a continuación convertir cada registro obtenido en una View que
será mostrada como ítem en la lista, tal y como podrá verse en la próxima sección.

Para añadir una ListView a un layout, se incluirá código XML similar al siguiente:

<ListView
android:id="@+id/myListViewExample"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textViewListViewExample"
tools:listitem="@android:layout/simple_list_item_1" />

Con el atributo tools:listitem se especifica el aspecto de cada ítem de la lista. En la pestaña


Graphical Layout, la ListView será mostrada así:

En la última sección del tema se muestra cómo añadir datos en los ítems de la ListView.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 12


TEMA 4. LAYOUTS

GridView

GridView es una ViewGroup que muestra los ítems en una cuadrícula con scroll. Los ítems de
la lista son insertados automáticamente en la misma utilizando un Adapter.

La organización de los ítems se realiza automáticamente, en función del número de columnas


que se establezcan a través del atributo android:numColumns. A dicho atributo se le podrá
asociar un número entero, o el valor auto_fit con el cual el número de columnas será
optimizado en función del espacio disponible. El ancho inicial de las columnas también podrá
ser predeterminado con android:columnWidth y con android:stretchMode="columnWidth"
se permite que el ancho de las columnas se expanda para aprovechar el espacio disponible.
Otros valores posibles de este atributo son spacingWidth, que hará que se expanda el espacio
entre columnas en vez de las columnas, y none.

Los atributos android:horizontalSpacing y android:verticalSpacing establecen el


espacio horizontal y vertical entre las filas y las columnas, respectivamente.

Por ejemplo, para mostrar un conjunto de imágenes que, al ser pulsadas, muestren un
mensaje, se declarará una GridView en el layout:

<GridView
android:id="@+id/myGridViewExample"
android:layout_width="fill_parent"
android:layout_height="120dp"
android:layout_below="@+id/textViewGridViewExample"
android:columnWidth="60dp"
android:gravity="center"
android:horizontalSpacing="5dp"
android:numColumns="3"
android:stretchMode="columnWidth"
android:verticalSpacing="5dp />

Para inicializar los datos que contendrá la GridView, se podrá que crear un adaptador
específico (que, por ejemplo, podrá extender de BaseAdapter), tal y como se verá en la
próxima sección. A dicho adaptador, se le deberá pasar la fuente de los datos que, en este
caso, será un TypedArray con referencias de imágenes almacenadas como recursos de la
aplicación en la carpeta res/drawable/. Para cargar dicho array, se creará un archivo XML,
array.xml, ubicado en la carpeta res/values/.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 13


TEMA 4. LAYOUTS

El contenido de dicho archivo será:

<?xml version="1.0" encoding="utf-8"?>

<resources>
<string-array name="my_image_array">
<item>@drawable/image_0</item>
<item>@drawable/image_1</item>
<item>@drawable/image_2</item>
<item>@drawable/image_3</item>
<item>@drawable/image_4</item>
<item>@drawable/image_5</item>
<item>@drawable/image_6</item>
<item>@drawable/image_7</item>
</string-array>
</resources>

En la carpeta res/drawable/ deberán ubicarse los recursos image_1.png a image_8.png.

Para inicializar el TypedArray, se implementará el siguiente código:

TypedArray myDrawableReferencesArray =
getResources().obtainTypedArray(R.array.my_image_array);

Dicho array será pasado al adaptador, que lo utilizará como fuente de datos, y el adaptador
será asociado a la GridView, a través del método setAdapter():

GridView gridview = (GridView) findViewById(R.id.myGridViewExample);


gridview.setAdapter(new MyImageAdapter(this, myDrawableReferencesArray));

Para que la GridView muestre un mensaje cuando se pulse sobre uno de sus ítems, se deberá
añadir un listener del evento on-click:

gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
Toast.makeText(MainActivity.this,
String.format(getString(R.string.image_position),
position), Toast.LENGTH_SHORT).show();
}
});

En el código anterior puede observarse que el método onItemClick() recibe como primer
parámetro la AdapterView padre, que en este caso será la GridView. Como segundo
parámetro, recibe la View sobre la que se ha pulsado. El tercer y cuarto parámetros son,

CURSO DE DESARROLLO DE APLICACIONES ANDROID 14


TEMA 4. LAYOUTS

respectivamente, la posición del ítem pulsado en la lista (siendo 0 la posición del primer ítem)
y el identificador del ítem pulsado.

El método muestra una Toast (mensaje flotante) con un recurso String formateado con la
posición del ítem pulsado.

El aspecto final de la GridView, al ejecutar el código de ejemplo, será el siguiente:

Las vistas, en general lanzan, más tipos de eventos en función de la acción que se realiza sobre
ellas. Por ejemplo, en el caso de GridView y ListView, así como en el caso del control de
entrada Spinner (tal y como se verá en el próximo tema), se puede añadir otro handler para el
evento item-selected. Este evento es gestionado por OnItemSelectedListener. No obstante,
se ha de tener especial cuidado con este evento, ya que solo se lanza cuando se selecciona un
ítem a través de un teclado físico u otro dispositivo de entrada (como, por ejemplo, la trackball
del Google Nexus One, o el optical pad del HTC Desire). Al pulsar sobre un ítem, este evento no
es lanzado.

En general, como los nuevos dispositivos no suelen llevar teclado físico, ni ningún trackball o
similar, es mejor no usar dicho evento y usar el evento item-click.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 15


TEMA 4. LAYOUTS

Uso de AdapterView y de Adapter

Inicialización de los datos de una AdapterView

Para inicializar los datos de una AdapterView como, por ejemplo, una ListView o una
GridView, se deberá asociar un adaptador a la misma (clase que extenderá de Adapter o de
alguna de las subclases de esta), el cual obtendrá los datos de una fuente externa y creará
objetos View para mostrar cada registro. Existen diferentes subclases de Adapter,
especializadas en mostrar los datos de una u otra fuente. Entre los más comunes, destacan
ArrayAdapter y SimpleCursorAdapter:

• ArrayAdapter. Adaptador especializado en mostrar datos obtenidos de un array. Por


defecto, ArrayAdapter crea cada View invocando al método toString() de cada
ítem del array, y creando una TextView con el contenido de dicho ítem.
Por ejemplo, para inicializar un ArrayAdapter con los datos de un String[]
(declarado como recurso en el arcivo strings.xml), se utilizará un código similar a este:

// 1º: Inicialización del String[] desde strings.xml (la referencia


está contenida en R.array, aunque sea un recurso de strings.xml)
String[] myStringArray =
getResources().getStringArray(R.array.myStringArray);

// 2º: Obtención de la ListView del layout


ListView myListView =
(ListView) findViewById(R.id.myListViewExample);

// 3º: Inicialización de los datos del Adapter


ArrayAdapter<String> myAdapter =
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, myStringArray);

// 4º: Se asocia el adaptador a la ListView


myListView.setAdapter(myAdapter);

Como se puede observar en el código de ejemplo anterior, el constructor del


adaptador recibe el contexto de la aplicación como primer parámetro 10. Como
segundo parámetro recibe una referencia a un layout que, en este caso, está
predefinido en la plataforma (android.R.layout.simple_list_item_1) y que
contiene únicamente una TextView para mostrar cada ítem. También se podrá pasar
una referencia a un layout personalizado que pertenezca a la propia aplicación. Como
tercer parámetro, el adaptador recibe la fuente de los datos que, en este caso, es un
String[] declarado como recurso en strings.xml así:

10
El código mostrado estará ubicado en general en el método onCreate() de una actividad. Tal y como se verá más
adelante, las actividades extienden del contexto de la aplicación (Context) de forma que this, primer parámetro
en el constructor del adaptador, equivale al contexto de la aplicación.

CURSO DE DESARROLLO DE APLICACIONES ANDROID 16


TEMA 4. LAYOUTS

<string-array name="myStringArray">
<item>Option 1</item>
<item>Option 2</item>
<item>Option 3</item>
<item>Option 4</item>
</string-array>

Tal y como se ha mencionado, se puede personalizar la información mostrada en cada


ítem del AdapterView sobrescribiendo el método toString() de los objetos del
array. También se puede personalizar el aspecto de cada ítem utilizando layouts
específicos de la aplicación, para lo cual será necesario extender AdapterView y
sobrescribir su método getView() que devolverá el tipo de View deseado para cada
ítem, inicializada con el valor (o valores) correspondiente(s). Al final de esta sección se
muestra cómo crear un adaptador específico.

• SimpleCursorAdapter. La interfaz Cursor provee acceso aleatorio de lectura y


escritura sobre el conjunto de resultados devueltos tras una consulta a una base de
datos. Para mostrar los datos de un Cursor, se puede utilizar un
SimpleCursorAdapter que utilizará el layout que sea especificado y en cuyas View
mostrará las columnas deseadas del Cursor.
Por ejemplo, y tal y como se verá en un próximo tema, para extraer información de los
archivos multimedia del dispositivo, se debe hacer una consulta al proveedor de
contenido MediaStore, el cual contiene los metadatos de todos los elementos
multimedia del dispositivo (tanto del almacén interno, como del externo –
generalmente una tarjeta SD). La consulta devolverá una Cursor que contendrá una
fila por cada elemento multimedia encontrado, así como las columnas que sean
especificadas. El adaptador necesitará un String[] que especifique qué columnas del
Cursor van a ser mostradas, así como un int[] con las referencias a las View del
layout sobre las que se mostrarán los datos de las columnas especificadas:

String[] fromColumns = new String[] {


MediaStore.MediaColumns.TITLE,
MediaStore.MediaColumns.DATE_ADDED,
MediaStore.MediaColumns.SIZE };

int[] toViews = new int[] {


R.id.textTitle,
R.id.textDate,
R.id.textSize };

Al instanciar el SimpleCursorAdapter, se pasarán los siguientes parámetros: contexto


de la aplicación, layout que será utilizado para mostrar los datos de cada fila del
Cursor, Cursor con los resultados, así como los dos arrays:

CURSO DE DESARROLLO DE APLICACIONES ANDROID 17


TEMA 4. LAYOUTS

// 1º: Inicialización del Cursor


// (se verá más adelante)
// 2º: Inicialización del SimpleCursorAdapter
SimpleCursorAdapter myAdaptader = new SimpleCursorAdapter(this,
R.layout.my_list_item, cursor, fromColumns, toViews, 0);

// 2º: Obtención de la ListView


ListView myListView = (ListView) findViewById(R.id.myListView);

// 3º: Se asocia el adaptador a la ListView


myListView.setAdapter(myAdapter);

La clase SimpleCursorAdapter se encarga de crear una vista para cada fila del Cursor
utilizando el layout proporcionado, e insertando cada fromColumns en cada toViews.
Si los datos del Cursor fuesen modificados con posterioridad, se podrá actualizar
automáticamente la información mostrada al invocar al método del adaptador,
notifyDataSetChanged().

Gestión de eventos de los ítems de una AdapterView

Se pueden gestionar las pulsaciones realizadas sobre los ítems de una AdapterView
implementando la interfaz AdapterView.OnItemClickListener:

// Listener de eventos on-click para la ListView


AdapterView.OnItemClickListener myItemClickListener =
new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position,
long id) {
// Se realizará alguna acción al pulsar el ítem.
// El segundo parámetro, "View v", el ítem pulsado.
}
};

myListView.setOnItemClickListener(myItemClickListener);

Creación de un Adapter específico

En ciertas ocasiones es necesario crear adaptadores específicos, que se ajusten a las


necesidades de la aplicación. Para ello, es posible extender alguno de los adaptadores ya
incluidos en la SDK como, por ejemplo, BaseAdapter (implementación básica de Adapter).

Al extender de BaseAdapter, el adaptador estará obligado a implementar ciertos métodos


abstractos de la interfaz Adapter que implementa el padre (getCount(), getItem(int),
getItemId(int) y getView(…)).

CURSO DE DESARROLLO DE APLICACIONES ANDROID 18


TEMA 4. LAYOUTS

Continuando con el ejemplo de la GridView, para mostrar imágenes en cada celda, se deberá
implementar un adaptador similar al siguiente:

public class MyImageAdapter extends BaseAdapter {

private Context mContext;


private TypedArray mImageReferences;

// Constructor que permite pasar una referencia del contexto al


// adaptador
public MyImageAdapter(Context context, TypedArray imageReferences) {
this.mContext = context;
this.mImageReferences = imageReferences;
}

@Override
public int getCount() {
return mImageReferences.length();
}

@Override
public Object getItem(int position) {
return mContext.getResources().getDrawable(
mImageReferences.getResourceId(position, -1));
}

@Override
public long getItemId(int position) {
return mImageReferences.getResourceId(position, -1);
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ImageView imageView;
// Las View que dejen de mostrarse en la GridView serán
// recicladas. Si una View es nueva, se deberán inicializar ciertos
// atributos
if (convertView == null) {

imageView = new ImageView(mContext);


imageView.setLayoutParams(new GridView.LayoutParams(55, 55));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(1, 1, 1, 1);
}
else {
imageView = (ImageView) convertView;
}

imageView.setImageResource(
mImageReferences.getResourceId(position, -1));

return imageView;
}
}

CURSO DE DESARROLLO DE APLICACIONES ANDROID 19


TEMA 4. LAYOUTS

En el adaptador anterior se crea un constructor al cual se le pasa el contexto de la aplicación,


así como la fuente de datos (un TypedArray) como parámetros. El método getCount()
devolverá el número de ítems contenidos en el array, el método getItem(int) deberá
devolver el elemento que esté en una posición concreta del array (de la fuente de datos) y el
método getItemId(int) deberá devolver el identificador del ítem. (La implementación de
estos dos últimos métodos no siempre es necesaria.)

El método más importante es getView(), que está encargado de unir cada registro de la
fuente de datos con la View que lo mostrará, y devolver dicha View. Cuando este método es
invocado, recibe como parámetro una View que suele ser un objeto reciclado (una View
previamente mostrada y que, después de hacer scroll ya no es visible).

La inicialización de la ImageView se realiza, en el caso de que la View recibida sea nula, a través
de tres métodos:

• setLayoutParams(): método que recibe como parámetro un objeto


GridView.LayoutParams, inicializado a su vez con el ancho y alto de la View al cual
será redimensionado el recurso drawable (el archivo de imagen).
• setScaleType(): método que recibe como parámetro un objeto Image.ScaleType el
cual establece cómo se recortará la imagen, en caso de ser necesario.
• setPadding(): método que establece el espacio de relleno de la imagen contenida en
la ImageView.

Una vez inicializada (o reciclada) la ImageView, se asocia el identificador del recurso drawable,
extrayéndolo de la fuente de datos, en función de la posición recibida como primer parámetro
del método getView():

imageView.setImageResource(mImageReferences.getResourceId(position, -1));

Será este objeto ImageView el que devolverá el método getView().

CURSO DE DESARROLLO DE APLICACIONES ANDROID 20

También podría gustarte