Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
dispositivos móviles
IFC03CM12
Cuadernillo de prácticas
Julio, 2012
Documento maquetado con TEXiS v.1.0.
Cuadernillo de prácticas
Julio, 2012
Copyright
c Pedro Pablo Gómez Martín
Índice
Notas bibliográcas . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
En el próximo capítulo . . . . . . . . . . . . . . . . . . . . . . . . . 24
Notas bibliográcas . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
En el próximo capítulo . . . . . . . . . . . . . . . . . . . . . . . . . 38
Notas bibliográcas . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
En el próximo capítulo . . . . . . . . . . . . . . . . . . . . . . . . . 74
En el próximo capítulo . . . . . . . . . . . . . . . . . . . . . . . . . 84
public c l a s s HolaMundo {
};
$ javac HolaMundo.java
$ java HolaMundo
½Hola mundo!
1
2 Capítulo 1. Repaso de Java y Eclipse
Fíjate que ahora hemos usado java (no javac) que es el programa que
contiene la implementación de la JVM. Además, no hemos especicado la
extensión.
public c l a s s Main {
$ javac Main.java
$ java Main
$ javac Fecha.java
$ java Main
Compila el chero:
$ javac AppletBasico
<!DOCTYPE html>
<html>
<head>
< t i t l e >Mi a p p l e t</ t i t l e >
</ head>
<body>
<h1>Mi a p p l e t</ h1>
<applet code=" A p p l e t B a s i c o . c l a s s "
width=" 3 0 0 "
height=" 2 0 0 ">
</ applet>
</ body>
</ html>
$ appletviewer AppletBasico.html
Para probarlo, vamos a hacer un applet que escribirá por la salida están-
dar una cadena indicando que se ha invocado a cada uno de los métodos.
Ten en cuenta que esa salida estándar normalmente no se verá si el applet
se ejecuta directamente en el navegador.
javac CicloVidaApplet.java
} // AppletConBoton
add(new java.awt.Button("Pulsame"));
Lo hemos separado en tres líneas porque más adelante haremos uso del
botón.
// C l a s e que manejará e l e v e n t o de p u l s a c i ó n d e l b o t ó n .
// Observa que e s t a c l a s e no e s p ú b l i c a ( no comienza con
// p u b l i c ) .
class MiActionListener
implements j a v a . awt . e v e n t . A c t i o n L i s t e n e r {
} // c l a s s M i A c t i o n L i s t e n e r
} // c l a s s BotonClaseExterna
Compila la clase. Observa que aparecen dos cheros .class, uno para
cada una de las clases.
Con la solución anterior, la clase oyente está al mismo nivel que la del
applet, cosa que conceptualmente no es demasiado limpio. Además, podría-
mos querer acceder a atributos estáticos de la clase del applet desde el oyente,
y no lo podremos hacer a no ser que sean públicos.
Para solucionar las dos cosas, Java permite la denición anidada de clases.
En inglés se conoce como top-level nested classes and interfaces. A nosotros
sólo nos interesan aquí las clases.
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
// C l a s e anidada , d e n t r o de o t r a . Observa e l s t a t i c .
static class MiActionListener
implements j a v a . awt . e v e n t . A c t i o n L i s t e n e r {
public void a c t i o n P e r f o r m e d ( j a v a . awt . e v e n t . A c t i o n E v e n t e) {
// Accedemos a l a t r i b u t o p r o t e g i d o de l a c l a s e a l a
// que p er t en e ce m os .
System . o u t . p r i n t l n ( m e n s a j e ) ;
}
} // c l a s s M i A c t i o n L i s t e n e r
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
} // i n i t
} // c l a s s BotonClaseAnidada
Compila la clase. Observa que aparecen dos cheros .class, pero ahora
el del listener tiene un nombre cualicado con el nombre de la clase a
la que pertenece (BotonClaseAnidada$MiActionListener.class).
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
} // c l a s s BotonClaseAnidadaNoEstatica
Compila la clase. Observa que aparecen dos cheros .class, igual que
antes.
// C l a s e l o c a l , p r i v a d a d e l método i n i t ( ) . No s e r á
// v i s i b l e f u e r a .
class MiActionListener
implements j a v a . awt . e v e n t . A c t i o n L i s t e n e r {
public void a c t i o n P e r f o r m e d (
j a v a . awt . e v e n t . A c t i o n E v e n t e) {
// Accedemos a l a t r i b u t o dinámico ( no e s t á t i c o )
// p r o t e g i d o de l a c l a s e a l a que p e r te n ec em o s .
System . o u t . p r i n t l n ( m e n s a j e ) ;
}
} // c l a s s M i A c t i o n L i s t e n e r
} // B o t o n C l a s e L o c a l 1
En principio, parece que las clases locales no aportan nada respecto a las
clases internas no estáticas anteriores. La diferencia que las hace especiales es
que pueden acceder a los atributos y variables locales del método donde se han
denido y creado, siempre que éstos sean final (constantes). Esto supone
un acercamiento a los cierres (closures ) de la programación funcional. Es
decir, si el método init() anterior tuviera una variable local (final), el
método actionPerformed() de la clase local podría acceder a él.
Esto es útil especialmente con los parámetros. Veamos un ejemplo que lo
demuestra. Para eso, el botón lo crearemos en un método nuevo que recibirá
parámetros, y lo invocaremos varias veces para poner varios botones en el
applet.
class MiActionListener
implements j a v a . awt . e v e n t . A c t i o n L i s t e n e r {
public void a c t i o n P e r f o r m e d (
j a v a . awt . e v e n t . A c t i o n E v e n t e) {
// Accedemos t a n t o a l a t r i b u t o de l a c l a s e
// e x t e r n a , como a l parámetro ( g r a c i a s a que e s
// f i n a l ) .
System . o u t . p r i n t l n ( m e n s a j e + " : " + pulsado ) ;
}// a c t i o n P e r f o r m e d
} // c l a s s M i A c t i o n L i s t e n e r
} // ponBoton
} // c l a s s B o t o n C l a s e L o c a l 2
// En l a l l a m a d a a a d d A c t i o n L i s t e n e r hacemos e l
// new y d e f i n i m o s l a c l a s e d i r e c t a m e n t e . Por
// nombre de l a c l a s e ponemos e l nombre de l a
// " s u p e r c l a s e " ( i n t e r f a z ) que vamos a
// implementar , l u e g o e l método , y cerramos
// todo .
miBoton . a d d A c t i o n L i s t e n e r (
new j a v a . awt . e v e n t . A c t i o n L i s t e n e r ( ) {
public void a c t i o n P e r f o r m e d (
j a v a . awt . e v e n t . A c t i o n E v e n t e) {
System . o u t . p r i n t l n ( m e n s a j e + " : " +
pulsado ) ;
} // a c t i o n P e r f o r m e d
}); // Cerramos l a d e f i n i c i ó n de l a c l a s e
// y d e l método
add ( miBoton ) ;
} // ponBoton
} // c l a s s BotonClaseAnonima
// Método l l a m a d o cuando s e p u l s a e l b o t ó n d e l a p p l e t .
protected void botonPulsado ( ) {
System . o u t . p r i n t l n ( m e n s a j e ) ;
} // b o t o n P u l s a d o
} // BotonFinal
Tiene una carpeta JRE System Library donde se enumeran las libre-
rías disponibles en el JRE que se usará para lanzar la aplicación.
System.out.println("½Hola mundo!");
public c l a s s HolaMundo {
Haz doble click sobre el lateral izquierdo del código, para añadir un
punto de ruptura (breakpoint ). También puedes colocar el cursor so-
bre la línea y pulsar <Ctrl>-<Mays>-B. Verás aparecer un circulito
indicando la existencia del punto de ruptura.
pila de llamadas verás los dos métodos (main y saluda) que están a
mitad de su ejecución.
Notas bibliográcas
La cantidad de documentación disponible sobre Java es inmensa. Para
comenzar, quizá un buen punto de partida sean los tutoriales proporcionados
por Oracle (http://docs.oracle.com/javase/tutorial/index.html) o, si
se preeren los libros tradicionales, la última edición del conocido Piensa en
Java de Bruce Eckel.
Los applets vivieron su explendor a nales de la década de 1990 y prin-
cipios de la del 2000. Hoy su uso es minoritario, habiendo sido desplazados
por los modos de interacción con el navegador que han proporcionado las
1
Este comando apenas es útil en este ejemplo porque estábamos terminando, y no
habría diferencia con usar cualquiera de los demás comandos. Sin embargo en situaciones
más complejas es muy práctico para relanzar la ejecución hasta el siguiente punto de
ruptura.
En el próximo capítulo. . .
En el próximo capítulo daremos nuestros primeros pasos con Android,
instalando el software necesario para desarrollar aplicaciones para él, y ana-
lizando sus herramientas principales.
para él.
Código abierto
25
26 Capítulo 2. Primeros pasos con Android
Capacidades multimedia
Para eso:
$ ./android
1
En esencia, es similar a una máquina virtual , pero ejecutándose sobre
un hardware emulado particular (el de los teléfonos), y sobre él el software
de Android en la versión que elijamos.
1
De hecho, el emulador de las SDK está basado en QEmu
Vamos a crear nuestro primer teléfono. Para eso, lanza el gestor de AVDs
con:
$ android avd
Scale display to real size: a partir de los datos de tamaño del monitor,
intenta que la representación del móvil tenga un tamaño real similar
al dispositivo físico que se está emulando.
Wipe user data: elimina los datos de usuario que se hayan introducido
tras la creación del AVD. Es como reformatearlo.
Ve al AVD y descuelga.
gsm list
inbound from 555666777 : active
Con gsm list observa que la segunda llamada está en estado incoming.
gsm list
inbound from 555666777 : held
inbound from 444333222 : active
Desde el propio CLI podemos hacer muchas tareas. Por ejemplo, po-
demos terminar una llamada:
Lanza Eclipse
Reinicia Eclipse.
Con el primer botón se abre el gestor de las SDK que ya conocemos. Esto
demuestra que efectivamente el ADT es un envoltorio de las SDK, invocán-
dolas desde Eclipse. Lo que hagamos ahí afectará a nuestra instalación de
las SDK, igual que si lo hubiéramos hecho lanzando la aplicación a mano por
separado.
Por último, hay tres botones para asistentes que, ahora ya sí, forman
parte del ADT y facilitan la creación de proyectos de Android con Eclipse.
Notas bibliográcas
La documentación on-line de Android es muy rica y está bien escrita
(aunque en inglés):
Introducción a Android:
http://developer.android.com/guide/basics/what-is-android.html
http://developer.android.com/tools/help/index.html
http://developer.android.com/guide/appendix/api-levels.html
http://developer.android.com/guide/developing/devices/index.
html
Gestión de AVDs:
http://developer.android.com/guide/developing/devices/managing-avds.
html
Características del emulador que ejecuta los AVDs:
http://developer.android.com/guide/developing/devices/emulator.
html
Uso del emulador:
http://developer.android.com/guide/developing/tools/emulator.
html
En el próximo capítulo. . .
En el próximo capítulo haremos nuestras primeras aplicaciones para An-
droid. Para eso, analizaremos las actividades (Activity ) de Android, que
constituyen sus piezas fundamentales.
39
40 Capítulo 3. Actividades: las ventanas de Android
• Pulsa Next.
Si no los tienes ya, lanza los AVD de Android 1.5 y Android 2.3.3.
Recuerda desmarcar Save to snapshot.
3
Esta ejecución no es de depuración, por lo que no se modicará la perspectiva de
Eclipse
El punto negativo de tener los dos valores es que debemos ser cuidadosos
al desarrollar, para no utilizar características de la versión Build SDK que
no estén en Minimum Required SDK. Si lo hacemos, fallará en ejecución en
el dispositivo.
new android.view.animation.AnticipateInterpolator();
• Deshabilitar lint para que Eclipse no lo use. Para eso, nos va-
mos al menú Window - Preferences, y en Android - Lint Error
Checking deshabilitamos las dos casillas de vericación.
• Name: Púlsame
package l i b r o . a z u l . pulsame ;
import a n d r o i d . o s . Bundle ;
import a n d r o i d . app . A c t i v i t y ;
/∗ ∗
∗ A c t i v i d a d de Android . R e p r e s e n t a l a ventana
∗ p r i n c i p a l de l a a p l i c a c i ó n .
∗/
public c l a s s Pulsame extends Activity {
/∗ ∗
∗ Método l l a m a d o cuando s e c r e a l a a c t i v i d a d .
∗/
@Override
public void o n C r e a t e ( Bundle savedInstanceState ) {
// Llamamos a l método que estamos s o b r e e s c r i b i e n d o
// de l a c l a s e padre .
super . o n C r e a t e ( s a v e d I n s t a n c e S t a t e ) ;
} // onCreate
/∗ ∗
∗ Método l l a m a d o cuando s e p u l s a s o b r e e l b o t ó n
∗ de l a ventana . Es l l a m a d o a t r a v é s de l a c l a s e
∗ anónima d e l e v e n t o .
∗/
private void botonPulsado ( ) {
++_numVeces ;
_boton . s e t T e x t ( " P u l s a d o " + _numVeces + " veces " ) ;
} // b o t o n P u l s a d o
/∗ ∗
∗ Botón de l a ventana .
∗/
private Button _boton ;
/∗ ∗
∗ Número de v e c e s que s e ha p u l s a d o e l b o t ó n .
∗/
private int _numVeces ;
} // Pulsame
talar los drivers que se proporcionan con las SDK, en uno de los paquetes
6
de la sección Extra . En GNU/Linux normalmente no es necesario instalar
nada.
Cuando lo hagas, conecta el móvil por USB. Si todo va bien, las SDK
de Android deberían tener acceso a él. Para comprobarlo, puedes usar una
aplicación en consola:
$ cd $ANDROID_SDK_PATH
$ cd platform-tools
$ ./adb devices
List of devices attached
emulator-5554 device
emulator-5556 device
D32C76402AF837AE device
6
Dependiendo del móvil, podrían necesitarse controladores especícos del fabricante.
En este caso, vemos los dos AVDs lanzados, y un dispositivo físico conec-
tado por USB. Si no ves tu móvil, comprueba que tienes los controladores
necesarios.
Para lanzar la aplicación sobre el móvil, basta con que en Eclipse inicies
una ejecución normal. Eclipse te mostrará la lista de los dispositivos dis-
ponibles que pueden ejecutar tu programa, y es suciente con que elijas el
dispositivo físico. Verás tu aplicación en el móvil un instante después.
/**
* Constante con el "tag" usado para registro de sucesos
* en LogCat relativos a esta actividad.
*/
private static final String TAG = "Pulsame";
$ ./adb -e logcat
Ahora verás toda la información que nos daba eclipse. Las alternativas
son brief, process, tag, raw, time, threadtime y long. Pruébalas e
identica sus diferencias.
Abre un shell con algún AVD. Por ejemplo, si sólo tienes uno lanzado:
$ ./adb -e shell
# <<--- shell remoto en el móvil
Fíjate que el programa que hemos hecho no tiene método main(), igual
que ocurre en los applets. De hecho, nuestra aplicación hereda de la clase
android.app.Activity. Una actividad es un componente de una aplicación,
que muestra una ventana con la que interactuar (buscar en la agenda, hacer
una foto, escribir un mensaje, mirar los mensajes entrantes). Cada actividad
tiene una ventana que, normalmente, ocupará toda la pantalla.
Vamos a hacer una nueva aplicación que nos muestre un mensaje cuando
se invoque a los métodos que controlan el ciclo de vida.
import a n d r o i d . o s . Bundle ;
import a n d r o i d . app . A c t i v i t y ;
@Override
public void o n C r e a t e ( B u n d l e s a v e d I n s t a n c e S t a t e ) {
super . o n C r e a t e ( s a v e d I n s t a n c e S t a t e ) ;
s e t C o n t e n t V i e w (R . l a y o u t . a c t i v i t y _ c i c l o _ d e _ v i d a ) ;
a n d r o i d . u t i l . Log . i (TAG, " onCreate " ) ;
}
protected void o n S t a r t ( ) {
super . o n S t a r t ( ) ;
a n d r o i d . u t i l . Log . i (TAG, " onStart " ) ;
}
protected void o n R e s t a r t ( ) {
super . o n R e s t a r t ( ) ;
a n d r o i d . u t i l . Log . i (TAG, " onRestart " ) ;
}
protected void o n P a u s e ( ) {
super . o n P a u s e ( ) ;
a n d r o i d . u t i l . Log . i (TAG, " onPause " ) ;
}
protected void o n S t o p ( ) {
super . o n S t o p ( ) ;
a n d r o i d . u t i l . Log . i (TAG, " onStop " ) ;
}
protected void o n D e s t r o y ( ) {
super . o n D e s t r o y ( ) ;
a n d r o i d . u t i l . Log . i (TAG, " onDestroy " ) ;
}
} // c l a s s CicloDeVida
Desde Eclipse, haz una copia del proyecto Pulsame y ponle el nombre
PulsameLocalizado. Vamos a cambiarlo para hacer uso de recursos
de tipo cadena, y haciendo una copia mantenemos la versión inicial. No
obstante, como el nombre del paquete no lo cambiaremos, no podremos
tener en el mismo dispositivo las dos aplicaciones a la vez.
_boton.setText("½Púlsame!");
por:
_boton.setText(R.string.pulsameAdmiracion);
<plurals name="numPulsaciones">
<item quantity="one">Pulsado %d vez</item>
<item quantity="other">Pulsado %d veces</item>
</plurals>
<plurals name="numPulsaciones">
<item quantity="one">Pushed %d time</item>
<item quantity="other">Pushed %d times</item>
</plurals>
En la práctica 3.9 hemos hecho uso de recursos para las cadenas, que
ya no están cableadas en el código. Sin embargo, estamos construyendo el
interfaz de la ventana manualmente. El método onCreate() es:
} // onCreate
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/boton"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@string/pulsameAdmiracion"/>
setContentView(R.layout.activity_pulsame);
setContentView(_boton);
Ahora el campo _boton se está obteniendo del recurso (una vez que
ya se ha congurado la vista), en lugar de crearlo manualmente como
hacíamos antes con new. Además, ya no establecemos el contenido (lo
hace el propio layout ). Únicamente añadimos el listener.
// ...
public void onCreate(Bundle savedInstanceState) {
....
// Nos hacemos observadores de sus pulsaciones.
_boton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
botonPulsado(v);
}
}
);
} // onCreate
...
android:onClick="botonPulsado"
Prueba la aplicación.
Ser público
Para las pruebas que vamos a realizar, abre el chero del recurso de tipo
layout activity_pulsame.xml de dicha práctica usando, esta vez sí, la vista
Graphical Layout. Eclipse nos muestra un entorno gráco para diseñar la
ventana, en lugar de tener que utilizar directamente el XML. Nos permite así
hacer cambios y ver el resultado automáticamente, sin necesidad de compilar
la aplicación y probarla en un AVD o terminal físico.
Fíjate que los atributos que conocíamos con los nombres layout_width
y layout_height en el XML, están, en el editor de propiedades, dentro
de una sección Layout Parameters y se llaman, sencillamente, width y
height. Esto es debido a que son atributos heredados del contenedor
en el que está el botón, no del botón en sí mismo. En el XML tendremos
que especicar como nombre layout_*, pero en la vista gráca tenemos
que buscarlos dentro de esa sección de las propiedades.
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="@string/hello_world" />
Haz las mismas pruebas sobre ella que las que hiciste con el botón.
Ten en cuenta que con las etiquetas es más complicado hacerse una
idea de qué está pasando, porque son transparentes. Cuando se dise-
ña un layout, a menudo es interesante poner fondos temporales a los
controles transparentes, para tener una visión más clara de lo que está
ocurriendo. Para eso, puedes poner en el atributo android:background
un color con el formato #RRGGBB (por ejemplo, #FF0000 para rojo).
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/editText"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:inputType="text"
android:text="@string/escribeAlgo" />
Haz las mismas pruebas sobre ella que las que hiciste antes.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#E0E0E0"
android:orientation="vertical" >
Algunas pistas:
numeroAAdivinar = dado.nextInt(100) + 1;
Además, aquí van algunas ideas, algo más avanzadas, para mejorar el
programa:
et.setOnKeyListener(new android.view.View.OnKeyListener() {
public boolean onKey(View v,
int keyCode,
android.view.KeyEvent event) {
// Han pulsado (o soltado) una tecla...
if ((event.getAction() ==
android.view.KeyEvent.ACTION_DOWN) &&
(keyCode ==
android.view.KeyEvent.KEYCODE_ENTER)) {
// Ha sido una pulsación de "intro"
// PENDIENTE! HACER ALGO!!
return true;
}
else
return false;
} // onKey
});
Notas bibliográcas
A continuación se proporcionan algunas direcciones web con información
sobre cada uno de los temas tratados en este capítulo:
http://developer.android.com/training/basics/firstapp/index.
html
Introducción a los interfaces de usuario con Android:
http://developer.android.com/training/basics/firstapp/building-ui.
html
Herramienta lint de las SDK de Android:
http://tools.android.com/tips/lint/
Logcat, el sistema de log de Android:
http://developer.android.com/tools/debugging/debugging-log.
html
Clase para enviar mensajes de log:
http://developer.android.com/reference/android/util/Log.html
Ciclo de vida de las actividades:
http://developer.android.com/guide/components/activities.html
http://developer.android.com/training/basics/activity-lifecycle/
http://developer.android.com/guide/topics/resources/overview.
html
Cualicadores de los recursos:
http://developer.android.com/guide/topics/resources/providing-resources.
html
Introducción a la creación de interfaces de usuario:
http://developer.android.com/guide/topics/ui/overview.html
Introducción a los layout :
http://developer.android.com/guide/topics/ui/declaring-layout.
html
En el próximo capítulo. . .
En el próximo capítulo veremos algunas posibilidades más avanzadas de
los interfaces grácos de usuario sobre Android.
dades de Android.
Debemos tener cuidado cuando se utiliza este layout para evitar crear por
error referencias circulares que impidan la colocación de los componentes.
Además, en este caso suele ser más sencillo crear el layout directamente en
XML, porque la herramienta gráca de diseño es bastante difícil de domar.
75
76 Capítulo 4. Un poco más sobre UIs
ocasiones, sin embargo, son atributos booleanos. Para hacernos una idea cla-
ra de los atributos de este layout, es interesante organizarlos en función de
qué lado del control sitúan. Es necesario tener en cuenta que esos atributos
sólo colocan un lado. Por ejemplo, el atributo below coloca el control que
estamos especicando debajo de otro. Eso signica que el lado superior de
dicho control estará en la misma horizontal que el lado inferior del control al
que referenciamos. Pero no signica que estén exactamente debajo (tocándo-
se). La posición en horizontal (izquierda-derecha) se establece con atributos
diferentes.
Debe tenerse en cuenta que no hace falta jar todos los lados de
layout_width y
los controles, dado que aún se hará uso de los atributos
layout_height. Además, como se ha dicho, no se debe hacer un uso contra-
dictorio de los atributos (por ejemplo, no tiene sentido establecer al mismo
tiempo toLeftOf y centerHorizontal).
Para dar más posibilidades de colocación, recuerda que puedes también
modicar los atributos de margen de los controles, con lo que se consigue
primero alinear un control con respecto a otro, y luego desplazarlo gracias
al margen.
Por último, es importante evitar las referencias circulares, pero esto hace
referencia a referencias de lados iguales. Sí podremos, sin embargo, establecer
por ejemplo el lado izquierdo del control A en relación con el control B, y
a su vez el lado superior del B en relación con el del A. Cuando se hacen
construcciones de ese tipo, en el XML ¾qué control denimos antes? ¾El A o
el B? Dado que ambos referencian al otro, tendremos un potencial problema
por identicadores desconocidos. Si eso ocurre, se puede referenciar a un
control que aún no existe poniendo el + como en la creación de los controles.
A modo de ejemplo, para denir el layout de la gura:
usaríamos el código:
<TextView
android:id="@+id/tvNombre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/etNombre"
android:padding="@dimen/padding_medium"
android:text="@string/nombre"/>
<EditText
android:id="@+id/etNombre"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/tvNombre"
android:layout_alignParentTop="true"
android:ems="10"/>
seleccionar una hace que las demás se desmarquen. Los botones de radio se
agrupan en RadioGroup, que se encarga de garantizar que sólo uno de los
botones de radio esté activo en un determinado momento.
Para saber en código qué opción está seleccionada, tenemos dos opcio-
nes. La primera es recorrer todos los RadioButton y mirar cuál tiene el
atributo checked a cierto. La segunda, mucho más práctica, es preguntarle
al RadioGroup el identicador del RadioButton seleccionado.
Añade código al evento del botón para que escriba en la etiqueta in-
ferior un comentario en relación con la selección (por ejemplo Vaya
RadioGroup
con la manzanita...). Para eso, puedes usar el método del
getCheckedRadioButtonId() (que tendrás que haber conseguido con
findViewById()), y luego hacer un switch para compararlo con cada
uno de los identicadores de los RadioButton.
cada vez que se modica la selección. Para eso, puedes registrarte como
oyente del RadioGroup usando un código como:
RadioGroup rg = ...;
rg.setOnCheckedChangeListener(
new RadioGroup.OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group,
int checkedId) {
// Hacer algo...
}
}
);
Una casilla de vericación es un control tipo botón especial que tiene dos
posibles estados, marcado y desmarcado. Muestra un texto y una parte que
representa el estado. Para desarrollar su comportamiento, la clase CheckBox
hereda de Button.
CheckBox cb = ...
cb.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
// Hacer algo! Sabemos quién ha sido pulsado
// y su estado
} // onCheckedChanged
} // new
); // setOnCheckedChangeListener
En la parte inferior, coloca una etiqueta invisible con el texto ½No! ½El
domingo no!, que se haga visible cuando se seleccione el domingo.
tostador para avisar de que ya están listas, y luego vuelven hacia dentro y
desaparecen. En Android, son pequeños textos que se muestran brevemente
al usuario otando sobre la ventana principal, y desaparecen automática-
mente un instante después.
toast.show();
Crea un proyecto nuevo que muestre un botón que al ser pulsado muestre
un mensaje usando un Toast.
Algunos comentarios:
Vamos a probarlo.
if (result == DialogResult.Yes) {
...
}
En el próximo capítulo. . .
En el próximo capítulo nos preocuparemos por invocar a una actividad
desde otra. Esto nos llevará a los intents y al chero AndroidManifest.xml.
Crea una actividad nueva. Para eso utiliza, por ejemplo, la opción
del menú File - New - Other. En la ventana de diálogo que aparece,
despliega Android y selecciona Android Activity.
85
86 Capítulo 5. Intents, chero de maniesto y componentes
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="libro.azul.dosactivities"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="3"
android:targetSdkVersion="15" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
....
</application>
</manifest>
<activity
android:name=".MainActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondaryActivity"
android:label="@string/title_activity_secondary" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Mira la salida del logcat. Verás que ha saltado una excepción del tipo
ActivityNotFoundException. Sólo las actividades registradas en el
chero de maniesto pueden ser ejecutadas, incluso aunque la clase
exista realmente. Por tanto deben declararse todas.
El signicado de:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
startActivityForResult(i, SECONDARY_ACTIVITY_TAG);
String respuesta;
EditText et = (EditText) findViewById(R.id.etRespuesta);
respuesta = et.getText().toString();
@Override
protected void onActivityResult(int requestCode,
int resultCode,
Intent data) {
String respuesta;
if ((resultCode == RESULT_CANCELED) ||
(data.getStringExtra("respuesta").isEmpty()))
respuesta = getResources().getString(
R.string.antipatico);
else
respuesta = data.getStringExtra("respuesta");
} // onActivityResult
El objetivo, sin embargo, es conseguir que sea igual lanzar una actividad
implementada en nuestra aplicación, que una ventana de otra.
En esta práctica vamos a hacer una nueva aplicación que será una copia
de la anterior, pero que tendrá únicamente la primera actividad, e invocará
a la segunda de la original.
por:
<activity
android:name=".SecondaryActivity"
android:label="@string/title_activity_secondary" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
$ ./adb shell
# ps
# kill <pid de dosactivities>
# kill <pid de invocacionremota>
Usando el adb shell, asegúrate de que ninguna está lanzada (esto po-
drás hacerlo también sobre un dispositivo físico, aunque no seas root).
Deja abierta la conexión con el shell.
Sin embargo, los intents se han diseñado para usarlos de una manera
mucho más potente llamada uso implícito, en el que no se proporciona qué
actividad debe procesar la solicitud. El objetivo es que los intents creen un
lenguaje en el sistema para que una aplicación pueda decir quiero llamar
a casa, y el sistema sepa qué actividad es capaz de ejecutar ese deseo. El
nombre intent (intención) encaja con esa idea, en el que las aplicaciones
notican su deseo de que algo ocurra.
Con esta idea en mente, los intents poseen, además del nombre de la
clase que hemos usado y los datos extra (setClassName() y putExtra())
varios campos más. Ten en cuenta que en las invocaciones implícitas nunca
se establece el nombre de la clase, y el sistema hará uso del resto de campos
para deducir qué actividad es capaz de atender la solicitud. En concreto:
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="@dimen/padding_medium"
android:text="@string/ejecutarIntent"
android:onClick="onBotonEjecutar" />
Verás que la acción solicitada sirve para lanzar la actividad que muestra
el resumen de uso de la batería. Nos muestra una estimación del tiempo
que durará la batería, y los porcentajes de uso de batería de las diferentes
1
aplicaciones disponibles . Dado que los AVD no simulan la batería con tanta
exactitud, en este caso el resultado en el emulador será mucho más pobre.
1
El resultado podría variar dependiendo del teléfono concreto.
startActivity(i);
}
Esto nos demuestra que en algunas ocasiones Android hace uso única-
mente de la acción para decidir qué aplicación lanzar (como ocurría en el
ejemplo de las estadísticas de uso de la batería), pero a veces necesita anali-
zar también la URI. Precisamente eso es lo que ocurre aquí; como la acción
ACTION_VIEW es muy genérica, Android necesita analizar la URI y al ver que
hace referencia a mensajes cortos lanza la aplicación adecuada.
i.setAction(Intent.ACTION_VIEW);
i.setType("vnd.android-dir/mms-sms");
i.putExtra("address", "5554433");
En este sentido se debe tener en cuenta que las aplicaciones pueden crear
2
sus propias acciones para que otras las utilicen . Incluso, pueden reutilizar
las acciones existentes, y diferenciarse de las demás a través de la URI o del
tipo MIME. Por ejemplo, la aplicación Skype informa a Android de que es
capaz de procesar intents con la acción VIEW_ACTION, pero con URIs del tipo
skype:<usuario>.
La conclusión de esto es que la información más importante para una
acción irá siempre en la URI, y es el uso que se recomienda. Los datos extra
deben utilizarse para información adicional que no afecte a la elección de la
actividad destino.
i.putExtra("sms_body",
"Hola! Estoy aprendiendo Android :-)");
2
Para eso, necesitarán conocerla cadena de la acción. Fíjate que nosotros aqui hemos
estado usando las constantes denidas en la clase Intent, que serán cadenas.
Establece el siguiente:
i.setAction(Intent.ACTION_GET_CONTENT);
i.setType("image/*");
i.setAction(Intent.ACTION_DIAL);
i.setData(Uri.parse("tel:5554433"));
La aplicación falla.
<uses-permission
android:name="android.permission.CALL_PHONE"/>
• Desinstala la aplicación.
• Borra el .apk.
• Desactiva la opción Fuentes desconocidas.
El proyecto creado estará vacío. Crea una clase nueva con el menú File
- New - Class.
El código fuente que nos crea Eclipse tiene denido un método llamado
onReceive(), sin código, que es abstracto en la clase padre. Ese método
será llamado automáticamente por Android cuando ocurra un suceso de
nuestro interés. Observa que recibe como segundo parámetro un intent
con la información que ha ocasionado la invocación. Esto demuestra
que los mensajes del sistema también se envían a través de intents. En
este caso, la acción del intent llevará la información sobre el suceso
ocurrido.
if (i.getAction().equals(
Intent.ACTION_POWER_CONNECTED))
android.util.Log.i(TAG, "Cargador conectado");
else if (i.getAction().equals(
Intent.ACTION_POWER_DISCONNECTED))
android.util.Log.i(TAG, "Cargador desconectado");
<receiver android:name=".DeteccionEnchufado">
</receiver>
Nos falta denir en qué sucesos estamos interesados. Para eso tenemos
que hacer uso de un elemento <intent-filter>, que establece ltros
a los intents que deberían llegarnos. Dentro del elemento <receiver>
que acabas de crear, añade:
<intent-filter>
<action android:name=
"android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name=
"android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
Esto indica que nos interesan los mensajes relacionados con la conexión
y desconexión del cargador. Fíjate que en el código fuente utilizamos
la constante Intent.ACTION_POWER_DISCONNECTED. En el chero de
maniesto debemos usar su valor (que siempre es una cadena).
Para estar seguros del estado de partida del AVD, usa el interfaz en
consola y desconecta el cable de alimentación. Para eso, conéctate por
telnet con el puerto par en el que está escuchando el AVD (recuerda
que puedes saber cual es mirándolo en el título de la ventana del AVD):
En una ventana nueva, abre una sesión con el shell y lista los procesos.
$ ./adb
# ps
# logcat
power ac on
OK
Notas bibliográcas
A continuación se proporcionan algunas direcciones web con información
sobre cada uno de los temas tratados en este capítulo:
Archivo de maniesto
http://developer.android.com/guide/topics/manifest/manifest-intro.
html
http://developer.android.com/guide/components/intents-filters.html
Firma de aplicaciones
http://developer.android.com/tools/publishing/app-signing.html
http://developer.android.com/reference/android/content/BroadcastReceiver.
html
En el próximo capítulo. . .
En el próximo capítulo veremos algunas de las posibilidades para guardar
y recuperar datos en Android.
Acceso a datos
640 KB deberían ser sucientes para
todo el mundo
Erróneamente atribuída a Bill Gates
persistentes.
El primer modo de almacenar datos que vamos a ver puede que, incluso,
ni siquiera deba ser categorizado como tal, dado que es el propio sistema
quien hace la mayor parte del trabajo.
109
110 Capítulo 6. Acceso a datos
@Override
public void onSaveInstanceState(Bundle outInstance) {
} // onSaveInstanceState
return numPulsados;
} // getEtiquetaBoton
// Incrementamos el contador...
++_numVeces;
} // botonPulsado
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pulsame);
if (savedInstanceState != null) {
_numVeces = savedInstanceState.getInt(
STATE_NUM_VECES);
} // onCreate
Haz una copia del proyecto con el juego Adivina mi número. Llámalo
AdivinaMiNumeroEstable.
Para conseguir todo esto, vamos a seguir algunos atajos. Vamos a guar-
dar:
Dene tres constantes estáticas de cadena con los nombres que utili-
zarás en el bundle de mantenimiento de estado para los tres datos que
vamos a guardar:
if (savedInstanceState == null) {
// Estamos iniciándonos desde cero.
// Comenzamos una partida.
reiniciaPartida();
}
else {
// Tenemos que reconstruirnos desde el bundle.
_numElegido = savedInstanceState.getInt(
STATE_NUM_ELEGIDO);
_numIntentos = savedInstanceState.getInt(
STATE_NUM_INTENTOS);
if (_numElegido == -1)
// La partida está terminada.
partidaAcabada();
else {
// La partida está a mitad. Ponemos el texto de la
// etiqueta superior.
TextView tv =
tv = (TextView)findViewById(R.id.etiquetaSuperior);
tv.setText(
savedInstanceState.getString(STATE_MENSAJE));
// Actualizamos la etiqueta del número de intentos
actualizarIntentos();
}
}
SharedPreferences preferencias;
Una vez conseguido el objeto, obtener los atributos que contiene es similar
a como lo haríamos con un bundle :
SharedPreferences.Editor editor;
editor = preferencias.edit();
editor.putInt(<atributo>, <valor>);
...
editor.commit();
Para esta práctica vamos a hacer una actividad nueva que simulará un
cuadro de diálogo de selección de opciones. Mostrará varios botones de radio,
y el usuario deberá elegir una de las opciones que ofrecen. Tendrá además
dos botones, uno de aceptar y otro de cancelar. Si se pulsa el de aceptar,
se guardará la selección, de modo que la próxima vez que se lance la activi-
dad aparecerá seleccionada. Si se pulsa cancelar, se cerrará la actividad sin
guardar nada:
preferencias = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor;
editor = preferencias.edit();
RadioGroup rg;
rg = (RadioGroup) findViewById(R.id.preferenciasMelodia);
editor.putInt(PREFERENCIA_MELODIA,
rg.getCheckedRadioButtonId());
editor.commit();
finish();
} // onAceptar
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences preferencias;
preferencias = getPreferences(Context.MODE_PRIVATE);
int id = preferencias.getInt(PREFERENCIA_MELODIA,
R.id.silencio);
RadioButton rb;
rb = (RadioButton) findViewById(id);
rb.setChecked(true);
} // onCreate
1
Esta prueba no podrás hacerla si has ejecutado la aplicación en un dispositivo físico,
dado que el shell no tiene priviliegios de administrador.
Haz un cat del chero para ver la estructura del XML. Comproba-
rás que dentro está la entrada para nuestro atributo. El valor es el
identicador del botón de radio.
Si utilizas ls -l, podrás ver los permisos del chero. Comprueba que
el usuario y el grupo tienen acceso de lectura y de escritura. Ambos
son el usuario particular que Android ha asociado a nuestra aplicación
(app_xx).
MODE_PRIVATE
Vuelve al código java y sustituye las dos apariciones de
por MODE_WORLD_READABLE. Vuelve a ejecutar el programa, modica la
selección y vuelve a mirar los permisos del chero. Verás que el resto
del mundo tiene ahora acceso de lectura.
InputStream is = getResources().openRawResource(R.raw.<id>);
(como BufferReader o Scanner) para que nos facilite la lectura, al igual que
haríamos con una aplicación en Java tradicional.
ScrollView como
Congura el layout de la actividad para que tenga un
raíz con un LinearLayout dentro, que, a su vez, posea un RadioGroup
vacío y el botón de aceptar.
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RadioGroup
android:id="@+id/rgOpciones"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
</RadioGroup>
<Button
android:id="@+id/bAceptar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@android:string/ok"
android:onClick="onAceptar"/>
</LinearLayout>
</ScrollView>
Abre el chero recién creado, y mete varias líneas de texto con lo que
serían las melodías disponibles.
Silencio
Andrómeda
Basic Bell
Cassiopeia
Chime
A cricket chirps
Crossing walk
Cuisine
Down hill
Emotive sensation
Eridani
Faint
Happy synth
Illuminator
Lira
RadioGroup rg;
rg = (RadioGroup) findViewById(R.id.rgOpciones);
InputStream is;
is = getResources().openRawResource(R.raw.melodias);
Scanner sc = new Scanner(is);
while (sc.hasNextLine()) {
String melodia = sc.nextLine();
RadioButton rb;
rb = new RadioButton(this);
rb.setText(melodia);
rg.addView(rb);
}
((RadioButton)rg.getChildAt(0)).setChecked(true);
} // onCreate
Ten en cuenta que esta aplicación es sólo un ejemplo para probar la carga
desde cheros de recursos crudos. La utilidad que pretende tener (hacer las
veces de preferencias del teléfono para elegir una melodía) requeriría todavía
bastante más trabajo de programación.
que nos devuelve una instancia de una clase tradicional de Java que
representa un stream de un chero. Como antes, podremos utilizar clases
auxiliares para recubrirlo y que nos facilite la escritura.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/padding_medium" >
<TextView
android:id="@+id/tvNombre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignBaseline="@+id/etNombre"
android:padding="@dimen/padding_medium"
android:text="@string/introduceNombre"
tools:context=".LibroVisitas" />
<EditText
android:id="@+id/etNombre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/tvNombre"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:inputType="text"/>
<TextView
android:id="@+id/tvVisitantes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/etNombre"
android:layout_margin="@dimen/padding_medium"
android:text="@string/visitantesAnteriores"/>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/tvVisitantes">
<TextView
android:id="@+id/etVisitantes"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:inputType="textMultiLine"
android:gravity="top"/>
</ScrollView>
</RelativeLayout>
EditText et;
et = (EditText) findViewById(R.id.etNombre);
et.setOnKeyListener(
new android.view.View.OnKeyListener() {
public boolean onKey(View v,
int keyCode,
android.view.KeyEvent event) {
// Han pulsado una tecla...
if ((event.getAction() ==
android.view.KeyEvent.ACTION_DOWN) &&
(keyCode ==
android.view.KeyEvent.KEYCODE_ENTER)) {
// Ha sido "intro"
onNuevoNombre();
return true;
}
return false;
}
});
EditText et;
et = (EditText) findViewById(R.id.etNombre);
String nuevoNombre;
nuevoNombre = et.getText().toString();
if (nuevoNombre.trim().equals("")) {
muestraMensaje(R.string.errorNombre);
return;
}
try {
FileOutputStream fos;
fos = openFileOutput(NOMBRE_FICHERO,
Context.MODE_PRIVATE |
Context.MODE_APPEND);
java.io.OutputStreamWriter out;
out = new OutputStreamWriter(fos);
out.write(nuevoNombre + "\n");
out.close();
muestraMensaje(R.string.bienvenido);
actualizarVisitantes();
}
catch (Exception e) {
muestraMensaje(R.string.errorRegistro);
}
InputMethodManager imm;
imm = (InputMethodManager)getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(et.getWindowToken(), 0);
et.requestFocus();
et.setText("");
} // onNuevoNombre
try {
String visitante;
FileInputStream fis;
fis = openFileInput(NOMBRE_FICHERO);
Scanner scanner = new Scanner(fis);
if (scanner.hasNextLine()) {
visitante = scanner.nextLine();
strBuf.append(visitante);
}
while (scanner.hasNextLine()) {
visitante = scanner.nextLine();
strBuf.append("\n" + visitante);
} // while
scanner.close();
tv.setText(strBuf.toString());
} // try
catch (Exception e) {
// Algo fue mal :-(
muestraMensaje(R.string.errorVisitantes);
} // try-catch
} // actualizarVisitantes
Toast t;
String mensaje;
mensaje = getResources().getString(id);
t = Toast.makeText(this, mensaje, Toast.LENGTH_LONG);
t.show();
} // muestraMensaje
$ ./avd shell
Notas bibliográcas
A continuación se proporcionan algunas direcciones web con información
sobre cada uno de los temas tratados en este capítulo:
http://developer.android.com/training/basics/activity-lifecycle/
recreating.html
Entrada/salida en Java
http://docs.oracle.com/javase/tutorial/essential/io/
Descripción general sobre recursos (entre ellos los de raw/)
http://developer.android.com/guide/topics/resources/providing-resources.
html
Descripción general de las posibilidades de almacenamiento en An-
droid.
http://developer.android.com/guide/topics/data/data-storage.html
No hemos visto cómo acceder a cheros arbitrarios. Si tienes curiosidad,
puedes comenzar por la clase Environment.
http://developer.android.com/reference/android/os/Environment.html
En el próximo capítulo. . .
En el próximo capítulo veremos el acceso a algunos de los sensores y
hardware especíco habituales en los dispositivos móviles.
ejemplo de grácos.
Vibrator vibrator;
vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
129
130 Capítulo 7. Sensores y otro hardware
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vibracion);
} // onCreate
1
Prueba la aplicación . Fallará.
<uses-permission android:name="android.permission.VIBRATE"/>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/padding_medium">
<TextView
1
Como podrás suponer, el emulador de AVD no emula la vibración, por lo que tendrás
que usar un dispositivo físico.
android:id="@+id/tvCancion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/padding_medium"
android:layout_alignBaseline="@+id/bReproducir"
android:layout_alignParentLeft="true"
android:text="@string/queCancionEs"/>
<Button
android:id="@+id/bReproducir"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/tvCancion"
android:layout_alignParentRight="true"
android:padding="@dimen/padding_medium"
android:text="@string/reproducir"
android:onClick="onReproducir"
tools:context=".Vibracion" />
<ScrollView
android:id="@+id/scrollView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/bReproducir"
android:layout_above="@+id/bAceptar">
<RadioGroup
android:id="@+id/rgOpciones"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="@dimen/padding_medium">
<RadioButton
android:id="@+id/ParaElisa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ParaElisa"
android:checked="true"/>
<RadioButton
android:id="@+id/champions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/champions"/>
<RadioButton
android:id="@+id/cumple"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cumple"/>
<RadioButton
android:id="@+id/wakaWaka"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/wakaWaka"/>
<RadioButton
android:id="@+id/yellowSubmarine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/yellowSubmarine"/>
</RadioGroup>
</ScrollView>
<Button
android:id="@+id/bAceptar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:onClick="onAceptar"
android:text="@android:string/ok"/>
</RelativeLayout>
_vibrator.cancel();
_vibrator.vibrate(patron, -1);
} // onReproducir
super.onPause();
_vibrator.cancel();
} // onPause
_vibrator.cancel();
RadioGroup rg;
rg = (RadioGroup) findViewById(R.id.rgOpciones);
int idRadioButton;
idRadioButton = rg.getCheckedRadioButtonId();
int idString;
if (idRadioButton == R.id.cumple)
idString = R.string.acertaste;
else
idString = R.string.fallaste;
} // onAceptar
Los dispositivos móviles van equipados con una creciente cantidad de sen-
sores que les permiten captar información del entorno a través de diferentes
magnitudes físicas.
SensorManager sm;
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sensores);
SensorManager sm;
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
java.util.List<Sensor> listaSensores;
listaSensores = sm.getSensorList(Sensor.TYPE_ALL);
TextView tv;
tv = (TextView) findViewById(R.id.sensores);
tv.setText(sb.toString());
} // onCreate
m
Acelerómetro, gravedad y aceleración lineal, medidos en
s2
Detección de proximidad
Android quién nos informe sobre los cambios en sus valores. Por tanto, nece-
sitamos registrarnos como oyentes del sensor en el gestor de sensores. Para
eso, utilizaremos el método:
donde:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/tvX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_above="@+id/tvY"
android:padding="@dimen/padding_medium"
android:text="@string/x" />
<TextView
android:id="@+id/tvValorX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/tvX"
android:layout_alignBaseline="@+id/tvX"
android:padding="@dimen/padding_medium" />
<TextView
android:id="@+id/tvY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="@dimen/padding_medium"
android:text="@string/y" />
<TextView
android:id="@+id/tvValorY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/tvY"
android:layout_alignBaseline="@+id/tvY"
android:padding="@dimen/padding_medium" />
<TextView
android:id="@+id/tvZ"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/tvY"
android:padding="@dimen/padding_medium"
android:text="@string/z" />
<TextView
android:id="@+id/tvValorZ"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/tvZ"
android:layout_alignBaseline="@+id/tvZ"
android:padding="@dimen/padding_medium"/>
<TextView
android:id="@+id/tvPrecision"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@id/tvZ"
android:layout_alignParentBottom="true"
android:padding="@dimen/padding_medium"
android:text="@string/precision" />
<TextView
android:id="@+id/tvValorPrecision"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/tvPrecision"
android:layout_alignBaseline="@+id/tvPrecision"
android:padding="@dimen/padding_medium"/>
</RelativeLayout>
TextView _etX;
TextView _etY;
TextView _etZ;
TextView _etPrecision;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_acelerometro);
Vamos a ver un ejemplo muy simple que demuestra esta última posibili-
dad, para crear una actividad en la que aparecen barras de colores alternos.
super(context);
}
} // class DibujosView
Paint _brocha1;
Paint _brocha2;
Ejecuta el programa.
_brochas[i].setColor(0xFF000000 +
(r << 16) + (g << 8) + b);
_brochas[i].setStyle(Paint.Style.FILL);
r += 20;
g -= 20;
b += 20;
} // for
} // Constructor
} // class DibujosView
if (event.getAction() == MotionEvent.ACTION_UP)
_numDedos = 0;
else {
_numDedos = event.getPointerCount();
if (_numDedos > 10)
_numDedos = 10;
for (int i = 0; i < _numDedos; ++i) {
_dedos[i].x = event.getX(i);
_dedos[i].y = event.getY(i);
}
}
invalidate();
return true;
}
} // onDraw
Notas bibliográcas
A continuación se proporcionan algunas direcciones web con información
sobre cada uno de los temas tratados en este capítulo:
http://developer.android.com/reference/android/os/Vibrator.html
http://developer.android.com/reference/android/hardware/SensorManager.
html
http://developer.android.com/reference/android/hardware/SensorEvent.
html
http://developer.android.com/guide/topics/graphics/2d-graphics.html
El próximo capítulo. . .
... ½½te toca escribirlo a ti!! Ha llegado la hora de que experimentes con
todas las posibilidades que proporciona Android. Se quedan en el tintero
muchas características interesantes. Conamos que el curso te haya servido
como impulso para encontrarlas :-)