Professional Documents
Culture Documents
david@edu.xunta.es
Android / DAM2
IES Chan do Monte
Este obra est bajo una licencia de Creative Commons Reconocimiento-CompartirIgual 4.0 Internacional.
Portions of this document are reproduced from work created and shared by the Android Open Source Project
and used according to terms described in the Creative Commons 2.5 Attribution License.
1 de 75
david@edu.xunta.es
Contenidos
Versiones de la plataforma y nmero de API.......................................................................................4
Entorno de trabajo y utilidades............................................................................................................5
Entornos de programacin..............................................................................................................5
AVD Manager...................................................................................................................................5
ADB..................................................................................................................................................6
Acceso desde lnea de comandos sqlite3.....................................................................................8
Manifiesto...........................................................................................................................................10
Proceso de construccin de una aplicacin.......................................................................................12
Programando para distintas pantallas................................................................................................14
Conceptos......................................................................................................................................14
Unidades........................................................................................................................................15
Consejos.........................................................................................................................................16
Recursos..............................................................................................................................................17
Acceso............................................................................................................................................17
Tipos de recursos y calificadores de configuracin.......................................................................17
Actividades.........................................................................................................................................19
Ciclo de vida...................................................................................................................................19
Interfaces............................................................................................................................................20
View y ViewGroup..........................................................................................................................20
Layouts...........................................................................................................................................20
Cdigo de manejo del interfaz.......................................................................................................20
Implementacin del clic de un botn.......................................................................................21
Notificaciones.....................................................................................................................................23
Toast...............................................................................................................................................23
Snackbar.........................................................................................................................................23
Intents.................................................................................................................................................24
Almacenamiento................................................................................................................................26
Preferencias compartidas (Shared Preferences)............................................................................26
Pantalla de preferencias............................................................................................................27
Almacenamiento interno...............................................................................................................29
Almacenamiento externo..............................................................................................................29
Almacenamiento a travs de la red...............................................................................................30
Base de datos.................................................................................................................................30
Gestor de base de datos SQLite..........................................................................................................31
Caractersticas bsicas...................................................................................................................31
Creacin y acceso a la base de datos.............................................................................................32
Operaciones con la base de datos.................................................................................................33
Recuperacin de datos a travs de cursores.................................................................................34
Listas...................................................................................................................................................35
Poblar la lista desde el cdigo........................................................................................................35
2 de 75
david@edu.xunta.es
3 de 75
david@edu.xunta.es
Versin de la plataforma
N de API Nombre
...
Android 6.0
23
MARSHMALLOW
Android 5.1
22
LOLLIPOP_MR1
Android 5.0
21
LOLLIPOP
Android 4.4W
20
Android 4.4
19
KITKAT
Android 4.3
18
JELLY_BEAN_MR2
17
JELLY_BEAN_MR1
16
JELLY_BEAN
15
ICE_CREAM_SANDWICH_MR1
14
ICE_CREAM_SANDWICH
Android 3.2
13
HONEYCOMB_MR2
Android 3.1.x
12
HONEYCOMB_MR1
Android 3.0.x
11
HONEYCOMB
10
GINGERBREAD_MR1
GINGERBREAD
Android 2.2.x
FROYO
Android 2.1.x
ECLAIR_MR1
Android 2.0.1
ECLAIR_0_1
Android 2.0
ECLAIR
Android 1.6
DONUT
Android 1.5
CUPCAKE
Android 1.1
BASE_1_1
Android 1.0
BASE
4 de 75
david@edu.xunta.es
AVD Manager
El gestor AVD (Android Virtual Devices) nos permite de una manera grfica crear y gestionar
distintos dispositivos android necesarios para probar testear nuestras aplicaciones.
Podremos emular multitud de caractersticas en distintos dispositivos de una manera
sencilla. Entre las caractersticas que podemos configurar se encuentran:
5 de 75
david@edu.xunta.es
Aunque los AVD's creados funcionan perfectamente, la realidad es que la emulacin resulta
especialmente lenta. Existen otras alternativas ms rpidas como puede ser el emulador
Genymotion, proporcionado como una mquina virtual de Virtual Box.
ADB
ADB (Android Debug Bridge) es una utilidad de lnea de comandos que nos permite
comunicarnos con un dispositivo Android que tengamos conectado al equipo, o bien con un
emulador. Su ubicacin es en la carpeta platform-tools en la carpeta del SDK de Android. Si se va a
trabajar mucho con dicha utilidad, no es mala idea aadirla a la variable de entorno PATH.
Aunque lo habitual es conectar el dispositivo fsico va cable USB, es posible acceder a
travs de Wi-Fi.
C:\>adb
Android Debug Bridge version 1.0.29
-d
-e
-s <serial number>
-p <product name or path>
devices
connect <host>[:<port>]
disconnect [<host>[:<port>]]
device commands:
adb push <local> <remote>
adb pull <remote> [<local>]
adb sync [ <directory> ]
adb
adb
adb
adb
adb
adb
6 de 75
david@edu.xunta.es
adb install [-l] [-r] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>
- push this package file to the device and install it
('-l' means forward-lock the app)
('-r' means reinstall the app, keeping its data)
('-s' means install on SD card instead of internal storage)
('--algo', '--key', and '--iv' mean the file is encrypted already)
adb uninstall [-k] <package> - remove this app package from the device
('-k' means keep the data and cache directories)
adb bugreport
adb help
adb version
scripting:
adb wait-for-device
- block until device is online
adb start-server
- ensure that there is a server running
adb kill-server
- kill the server if it is running
adb get-state
- prints: offline | bootloader | device
adb get-serialno
- prints: <serial-number>
adb status-window
- continuously print device status for a specified device
adb remount
- remounts the /system partition on the device read-write
adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program
adb reboot-bootloader
- reboots the device into the bootloader
adb root
- restarts the adbd daemon with root permissions
adb usb
- restarts the adbd daemon listening on USB
adb tcpip <port>
- restarts the adbd daemon listening on TCP on the specified port
networking:
adb ppp <tty> [parameters]
- Run PPP over USB.
Note: you should not automatically start a PPP connection.
<tty> refers to the tty for PPP stream. Eg. dev:/dev/omap_csmi_tty1
[parameters] - Eg. defaultroute debug dump local notty usepeerdns
adb sync notes: adb sync [ <directory> ]
<localdir> can be interpreted in several ways:
- If <directory> is not specified, both /system and /data partitions will be updated.
- If it is "system" or "data", only the corresponding partition is updated.
environmental variables:
ADB_TRACE
ANDROID_SERIAL
ANDROID_LOG_TAGS
Especialmente interesante es el shell que nos proporciona el adb, donde podemos ejecutar
ciertos comandos de un shell UNIX as como utilidades como sqlite3 para acceder a bases de datos
SQLite, o screenrecord (API >=19 ~ v.4.4), para grabar en video la actividad de la pantalla.
En el caso de disponer de varios dispositivos conectados simultneamente (virtuales o no),
una llamada directa a adb shell nos informa de que hay varios dispositivos conectados. En ese caso
debemos proceder de la siguiente manera:
7 de 75
david@edu.xunta.es
C:\...\>set ANDROID_SERIAL=192.168.56.101:5555
C:\...\>adb shell
shell@android:/ #
Para trabajar directamente con el dispositivo fsico hay que recordar habilitar el Modo
depuracin USB. Si con adb devices en vez de aparecernos el dispositivo como device aparece
unauthorized, hay que aceptar en el dispositivo la huella digital (clave RSA) para autorizar a ese
ordenador a acceder al dispositivo
Una vez tenemos acceso al emulador nos moveremos hasta la localizacin de la base de
datos y la abriremos con la utilidad sqlite3, pasndole su nombre como parmetro.
C:\>adb shell
shell@android:/ $ su
su
Test prop
no androVM.su.bypass prop -> su access rights managed by the SuperUser app
shell@android:/ # cd data/data/com.empresa.app /databases
cd data/data/com.empresa.app /databases
8 de 75
david@edu.xunta.es
Tenga especial cuidado al escribir el nombre de la base de datos pues es case sensitive, y de
no coincidir con la existente, se creara una vaca.
Podemos escribir comandos precedidos por un punto (.comando) o SQL terminado en ;
sqlite> .help
.help
.backup ?DB? FILE
.bail ON|OFF
.databases
.dump ?TABLE? ...
.echo ON|OFF
.exit
.explain ?ON|OFF?
.header(s) ON|OFF
.help
.import FILE TABLE
.indices ?TABLE?
.log FILE|off
.mode MODE ?TABLE?
.nullvalue STRING
.output FILENAME
.output stdout
.prompt MAIN CONTINUE
.quit
.read FILENAME
.restore ?DB? FILE
.schema ?TABLE?
.separator STRING
.show
.stats ON|OFF
.tables ?TABLE?
.timeout MS
.vfsname ?AUX?
.width NUM1 NUM2 ...
.timer ON|OFF
sqlite>
9 de 75
david@edu.xunta.es
Manifiesto
Toda aplicacin Android necesita un archivo denominado AndroidManifest.xml en su
carpeta raz. La estructura general y los elementos (sin atributos ni contenido) se pueden observar
a continuacin:
<manifest>
<uses-permission />
<permission />
<permission-tree />
<permission-group />
<instrumentation />
<uses-sdk />
<uses-configuration />
<uses-feature />
<supports-screens />
<compatible-screens />
<supports-gl-texture />
<application>
<activity>
<intent-filter>
<action />
<category />
<data />
</intent-filter>
<meta-data />
</activity>
<activity-alias>
<intent-filter>
...
</intent-filter>
<meta-data />
</activity-alias>
<service>
<intent-filter>
...
</intent-filter>
<meta-data/>
</service>
10 de 75
david@edu.xunta.es
Declara un BroadCastReceiver, el cual permite a la aplicacin recibir
intents que son enviados en modo difusin (broadcast)
<provider>
<grant-uri-permission />
<meta-data />
<path-permission />
</provider>
<uses-library />
</application>
</manifest>
android.hardware.audio.low_latency
Bluetooth
android.hardware.bluetooth
Camera
android.hardware.camera
android.hardware.camera.autofocus
android.hardware.camera.flash
android.hardware.camera.front
android.hardware.camera.any
Location
android.hardware.location
android.hardware.location.network
android.hardware.location.gps
Microphone
android.hardware.microphone
NFC
android.hardware.nfc
Sensors
android.hardware.sensor.accelerometer
android.hardware.sensor.barometer
android.hardware.sensor.compass
android.hardware.sensor.gyroscope
android.hardware.sensor.light
android.hardware.sensor.proximity
Screen
android.hardware.screen.landscape
android.hardware.screen.portrait
Telephony
android.hardware.telephony
android.hardware.telephony.cdma
android.hardware.telephony.gsm
Television
android.hardware.type.television
Touchscreen
android.hardware.faketouch
android.hardware.faketouch.multitouch.distinct
android.hardware.faketouch.multitouch.jazzhand
android.hardware.touchscreen
android.hardware.touchscreen.multitouch
android.hardware.touchscreen.multitouch.distinct
android.hardware.touchscreen.multitouch.jazzhand
USB
android.hardware.usb.host
android.hardware.usb.accessory
Wifi
android.hardware.wifi
Ver http://developer.android.com/guide/topics/manifest/manifest-intro.html
11 de 75
david@edu.xunta.es
david@edu.xunta.es
Ver http://developer.android.com/tools/building
13 de 75
david@edu.xunta.es
Conceptos
Tamao de pantalla
Tamao fsico, medido como la diagonal de la pantalla.
Para simplificar, Android agrupa todas las posibles pantallas existentes en cuatro grupos:
small (pequea), normal (normal), large (grande) y xlarge (extra-grande)
Densidad de pantalla
La cantidad de pixels en una cierta rea fsica de la pantalla. Suele medirse en puntos por
pulgada (dpi dots per inch).
Para simplificar, Android agrupa todas las posible densidades existentes en cuatro grupos:
ldpi (baja), mdpi (media), hdpi (alta) y xhdpi (extra-alta)
Orientacin
La orientacin de la pantalla desde el punto de vista del usuario. Puede ser vertical o modo
retrato (portrait) y horizontal o modo paisaje (landscape), y por lo general hay que tener en cuenta
que es posible que pueda cambiar en tiempo de ejecucin.
Resolucin
El nmero total de pixels fsicos de la pantalla. Las aplicaciones no deben trabajar
directamente con este dato sino con tamaos de pantalla y densidades.
14 de 75
david@edu.xunta.es
Unidades
Pixels (px)
Un punto fsico de la pantalla. Es dependiente de la densidad pues su tamao vara con
esta. No se debera utilizar.
Pulgadas (in) Milimetros (mm) Puntos (pt = 1/72in)
Medidas independientes de la densidad, pues siempre ocupan lo mismo. No se deberan
utilizar salvo que necesitemos tamaos exactos en la pantalla.
Density-independent pixel (dp)
Un pixel virtual utilizado para definir dimensiones y posiciones en el layout de una manera
independiente de la densidad del dispositivo.
Un dp es equivalente a un pixel fsico en una pantalla 160dpi, la cual est considerada como
la pantalla de una densidad media. El sistema gestiona automticamente en tiempo de ejecucin
la conversin a pixels basndose en la densidad de la pantalla del dispositivo, con la siguiente
frmula: px = dp * (dpi / 160).
Por ejemplo, en una pantalla de 240 dpi, 1 dp son 1.5 pixels.
Siempre se debera usar dp's como unidades cuando se define el interfaz de la aplicacin
para asegurar una correcta visin en diferentes densidades.
Scale-independent pixel (sp)
Es similar al dp pero escalado por el tamao de texto preferido por el usuario. Se debe usar
al definir tamaos de texto pero nunca para tamaos de layouts.
15 de 75
david@edu.xunta.es
Consejos
Declarar los tamaos de pantalla que permite nuestra aplicacin
Se puede hacer incluyendo en el manifiesto el elemento <supports-screens>
<supports-screens android:resizeable=["true"| "false"]
android:smallScreens=["true" | "false"]
android:normalScreens=["true" | "false"]
android:largeScreens=["true" | "false"]
android:xlargeScreens=["true" | "false"]
android:anyDensity=["true" | "false"]
android:requiresSmallestWidthDp="integer"
android:compatibleWidthLimitDp="integer"
android:largestWidthLimitDp="integer"/>
16 de 75
david@edu.xunta.es
Recursos
Los recursos se deben almacenar siempre de manera independiente del cdigo en
subcarpetas de la carpeta res/ de la carpeta principal del proyecto.
Acceso
El acceso a dichos recursos puede hacerse desde el cdigo fuente y desde archivos XML.
Desde el cdigo se hace llamando a los mtodos adecuados y utilizando los IDs numricos
automticamente generados en la clase R. Esta clase no se debe tocar nunca manualmente pues la
herramienta aapt la regenera automticamente con cada compilacin del cdigo.
Ver http://developer.android.com/guide/topics/resources/accessing-resources.html
17 de 75
david@edu.xunta.es
Nombres de las
carpetas de recursos
Tipo de recurso
drawable
Archivos de imagen (.png .jpg .gif) o archivos XML de bitmaps, figuras, etc.
layout
menu
raw
values
animator, anim, color, xml, ... Archivos XML que almacenan otros tipos de informacin
Calificadores
w720dp, w1024dp
h720dp, h1024dp
Tamao
long, notlong
Orientacin
land, port
Interfaz
Modo nocturno
night, notnight
Densidad (dpi)
Interfaz tctil
notouch, finger
Tipo de teclado
v3, v4, v7
Ver http://developer.android.com/guide/topics/resources/providing-resources.html
18 de 75
david@edu.xunta.es
Actividades
java.lang.Object
android.content.Context
android.content.ContextWrapper
android.view.ContextThemeWrapper
android.app.Activity
Ciclo de vida
david@edu.xunta.es
Interfaces
java.lang.Object
android.view.View
android.widget.ViewGroup
android.widget.RelativeLayout
android.widget.LinearLayout
android.widget.AbsoluteLayout
android.widget.FrameLayout
android.widget.GridLayout
android.widget.ImageView
android.widget.ImageButton
android.widget.ProgressBar
android.widget.TextView
android.widget.EditText
android.widget.Button
android.widget.CompoundButton
android.widget.CheckBox
android.widget.ToggleButton
View y ViewGroup
La clase View representa el bloque bsico de construccin para los componentes del
interfaz. Es la clase base para todo tipo de widgets usados para crear interfaces interactivos con el
usuario.
La clase ViewGroup es (hereda) un tipo especial de View que puede contener otros Views
(o otros ViewGroup's, que son View's...). Es la clase base de todo tipo de Layouts y contenedores
de Views.
Todo view debe tener un id, por si accedemos desde cdigo o para referenciarlo
desde el propio XML (RelativeLayout)
Layouts
Creacin por cdigo y por archivo XML
Tipos: Linearlayout, RelativeLayout, etc.
El RelativeLayout es el mejor view group para disear un interfaz debido a que elimina la
necesidad de tener varios view group's anidados, manteniendo una jerarqua plana que mejora
mucho el rendimiento. Siempre ser mucho mejor utilizar un RelativeLayout que varios
LinearLayout anidados.
david@edu.xunta.es
interface android.view.View.OnClickListener {
public abstract void onClick (View v);
}
XML
.java
<Button
android:onClick="unMtodo"
/>
Es la manera ms sencilla, aunque hay que tocar en dos archivos y la asociacin entre el listener y
el mtodo podra dar problemas en algn caso si hay fragments en el cdigo. Se recomienda la
siguiente opcin, implementada totalmente en el cdigo.
La segunda opcin es crear un objeto que implemente dicho interfaz y pasrselo como parmetro
al mtodo public void setOnClickListener (View.OnClickListener l) , que se encarga de registrar el
callback que ser invocado cuando en este view se haga clic.
}
};
botn.setOnClickListener(ocl);
21 de 75
david@edu.xunta.es
botn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
unMtodo(v);
}
});
22 de 75
david@edu.xunta.es
Notificaciones
Toast
Toast es una clase que nos permite mostrar mensajes en pantalla un determinado tiempo y
luego desaparecen. La manera ms rpida de crearlos es utilizar el mtodo esttico makeText, al
que se le pasa el contexto, el mensaje y la duracin. El objeto Toast devuelto puede utilizarse
directamente para llamar al mtodo show() para que se muestre en pantalla.
Toast.makeText(this,Mensaje, Toast.LENGTH_SHORT).show() ;
Snackbar
Snackbar es una clase proporcionada con Material Design, y que nos permite ms
posibilidades que la anterior:
- Puede contener un botn de accin (deshacer, por ejemplo)
- Puede aparecer y desaperece automticamente (~ Toast) o quedar indefinidamente
- Se puede descartar con un desplazamiento lateral por parte del usuario (utilizando un
CoordinatorLayout)
- Se integra mejor en el interfaz, apareciendo en la parte inferior e integrandose en el UI
Para que aparezca en pantalla llamaremos al mtodo esttico make que necesita tres
parmetros:
- view: referencia a una vista que permita encontrar un contenedor para alojar nuestro layout
- text/resId: texto o un entero con el identificador del recurso de cadena para mostrar en pantalla
- duration: Snackbar.LENGTH_INDEFINITE, LENGTH_LONG o LENGTH_SHORT
Utilizando encadenamiento de mtodos, antes de llamar al mtodo show(), podemos
llamar a otros mtodos estticos como setActionTextoColor y .setAction para establecer una accin
asociada.
23 de 75
david@edu.xunta.es
Intents
Un intent es bsicamente una descripcin abstracta de alguna operacin que queremos
realizar. Son mensajes que nos permiten invocar componentes o actividades (internos a nuestra
aplicacin, o externos). Se podra considerar como el puente de unin, en tiempo de ejecucin,
entre distintas actividades.
Un intent necesita como mnimo dos parmetros:
ACTION_MAIN
ACTION_VIEW
ACTION_ATTACH_DATA
ACTION_EDIT
ACTION_PICK
ACTION_CHOOSER
ACTION_GET_CONTENT
ACTION_DIAL
ACTION_CALL
ACTION_SEND
ACTION_SENDTO
ACTION_ANSWER
ACTION_INSERT
ACTION_DELETE
ACTION_RUN
ACTION_SYNC
ACTION_PICK_ACTIVITY
ACTION_SEARCH
ACTION_WEB_SEARCH
ACTION_FACTORY_TEST
data: Los datos sobre los que queremos operar, expresados como un Uri
aunque posteriormente se le puede aadir informacin como:
Los intents pueden ser implcitos, donde simplemente decimos la operacin que queremos
realizar. El sistema buscar los componentes registrados para dicha accin (a travs de sus intentfilter) y, de haber ms de uno, nos dejar elegirlo.
A continuacin mostramos ejemplos para que nos permita ver una pgina web en el
navegador, enviar un correo desde el cliente de correo que elijamos, etc.
24 de 75
david@edu.xunta.es
Cuando queremos obtener un resultado de una actividad que hemos iniciado, deberemos
llamar al mtodo startActivityForResult y sobreescribir onActivityResult
donde requestCode es un entero que nos ser devuelto en el mtodo onActivityResult para
distinguir qu actividad es la que ha finalizado, y options es un Bundle con opciones para indicar
cmo se debe iniciar la actividad.
protected void onActivityResult (int requestCode, int resultCode, Intent data)
es el mtodo que deber deber invocar en algn momento la actividad antes de cerrarse. Su valor
se recupera en el mtod onActivityResult. A menudo se utilizan las constantes RESULT_OK o
RESULT_CANCEL.
25 de 75
david@edu.xunta.es
Almacenamiento
Dependiendo de las necesidades especficas de cada aplicacin, podremos elegir distintas
opciones de almacenamiento.
Es un mtodo de Activity, y no le pasamos el nombre del archivo que tiene que leer porque, al ser
nico, utiliza el nombre de la actividad.
El atributo mode nos permite establecer el acceso:
MODE_PRIVATE
MODE_WORLD_READABLE
MODE_WORLD_WRITEABLE
Si, en cambio, utilizamos varios archivos de preferencias, usaremos otro mtodo donde
adems indicaremos el nombre que identifica al archivo que nos interesa.
public abstract SharedPreferences getSharedPreferences (String name, int mode) // mt. Context
26 de 75
david@edu.xunta.es
Pantalla de preferencias
Si necesitamos una actividad para editar preferencias, ver la clase PreferenceActivity. Esta
clase, obsoleta a partir de la versin 3.0, (ver PreferenceFragment) nos permite crear de manera
automtica una pantalla (tpica de Ajustes...) a partir de un archivo XML donde definimos su
estructura.
27 de 75
david@edu.xunta.es
Y el archivo que almacena las preferencias es el archivo por defecto de la actividad, al que
podemos acceder con el mtodo getPreferences() y gestionar igual que cualquier otro archivo de
preferencias. Su posible contenido se puede visualizar a continuacin:
shell@android:/data/data/com.empresa.app/shared_prefs # cat com.empresa.app_preferences.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="orderByClientes">NumPedidos</string>
<string name="correo">correoImpresora@empresa.com</string>
<boolean name="pedidosEncadenados" value="true" />
<boolean name="mostrarDescatalogados" value="false" />
</map>
28 de 75
david@edu.xunta.es
Almacenamiento interno
Permite almacenar archivos en la memoria interna del dispositivo. Por defecto, estos
archivos son privados para las aplicaciones que los crean, y no estn accesibles para el resto de
aplicaciones. Este tipo de archivos se eliminan cuando se desinstala la aplicacin.
Ver ms aqu
Almacenamiento externo
Todos los dispositivos Android disponen una memoria externa compartida. Esta puede ser
extrable (tarjeta SD, p.e.) o estar interna sin poder extraerse. Los archivos que se almacenen en
dicha memoria son accesibles en lectura y escritura.
Para tener acceso a dicha memoria, nuestra aplicacin deber obtener permisos de lectura
y/o de escritura en el manifiesto.
<manifest>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
29 de 75
david@edu.xunta.es
A partir de la versin 4.4, estos permisos no son necesarios si dichos archivos son privados
a nuestra aplicacin. De esta manera se podra utilizar el atributo maxSdkVersion para que el
permiso se solicitara slo cuando sea necesario.
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
</manifest>
Ver ms aqu
Base de datos
Android proporciona soporte completo a las bases de datos SQLite,un gestor de bases de
datos extremadamente ligero. En el siguiente epgrafe se puede ver en detalle.
30 de 75
david@edu.xunta.es
Cdigo libre.
Escrito en ANSI-C, se puede obtener su cdigo en un nico archivo para unirlo a nuestro
proyecto. La versin completa ocupa menos de 500K, y mucho menos excluyendo partes
opcionales.
Soportado en todas las plataformas existentes: Windows, Unix (Linux, Mac OS-X, iOS,
Android), etc.
Soporta SQL, pero no el estndar SQL-92 completo. Por ejemplo, no soporta RIGHT OUTER
JOIN ni FULL OUTER JOIN, etc. La integridad referencial (FOREIGN KEY) la soporta a partir de
la versin 3.6.19, aunque viene desactivada por defecto. S soporta TRIGGERS, por lo que
en versiones anteriores se puede implementar.
SQLite utiliza asignacin de tipos dinmica, es decir, en todo momento se asignan los tipos
en funcin de los valores utilizados. De hecho, aunque se establezca en el CREATE TABLE,
los tipos de datos no se establecen para cada columna de la tabla, sino que para cada valor
se le asignar un tipo TEXT, INTEGER, REAL, BLOB o tambin pueden ser NULL.
Desde cdigo, el esquema de la base de datos se puede consultar en una tabla especial
denominada sqlite_master. Con un simple SELECT se puede acceder a los objetos
existentes: tablas, ndices, etc. El campo type nos informa del tipo de objeto y, obviamente,
se trata de una tabla de slo lectura mantenida por el sistema.
Existen multitud de aplicaciones con un entorno visual para manejar las bases de datos
SQLite (p.e. SQLite Administrator), e incluso extensiones para los navegadores ms
utilizados (p.e. SQLiteManager Firefox add-on).
31 de 75
david@edu.xunta.es
El mtodo recomendado para crear una nueva base de datos es creando una subclase de
SQLiteOpenHelper, cuyo constructor necesita cuatro parmetros:
- Context context: El contexto de la aplicacin, para crear y usar la base de datos
- String name: El nombre de la base de datos, o null para una base de datos en memoria
- SQLiteDatabase.CursorFactory factory: usado para crear cursores null los crea por defecto
- int version: Comenzando en 1, es la versin de la base de datos con la que debera trabajar el
cdigo actual . Si la base de datos encontrada es menor, se llamar a onUpgrade. Si es mayor, se
llamar a onDowngrade.
En nuestra nueva clase habr que sobreescribir el mtodo onCreate(SQLiteDatabase db),
donde se ejecutar el SQL para crear las tablas necesarias. Este mtodo slo es llamado cuando la
base de datos no existe.
Es importante recordar las limitaciones existentes en los tipos de datos en SQLite Version 3.
En concreto, tiene un tipado dinmico muy flexible (lo cual no tiene porque ser algo bueno...), es
decir, cualquier columna puede almacenar cualquier tipo de valor (excepto una columna INTEGER
PRIMARY KEY). De hecho, se puede probar como ejercicio la invencin de un nuevo tipo de datos
para una columna y veremos que todo funciona bien e incluso el comando .schema del shell nos
informar adcuadamente de nuestra nueva invencin.
Otro mtodo que debemos sobreescribir es onUpgrade (SQLiteDatabase db, int oldVersion,
int newVersion), que nos informa de la versin de la base de datos encontrada para que la
actualicemos a la versin que necesita la aplicacin. Igualmente existe un mtodo onDowngrade
(SQLiteDatabase db, int oldVersion, int newVersion), aunque su ejecucin se realizar en
situaciones muy especiales.
32 de 75
david@edu.xunta.es
public static SQLiteDatabase openDatabase (String path, SQLiteDatabase.CursorFactory factory, int flags)
public static SQLiteDatabase openOrCreateDatabase (String path, SQLiteDatabase.CursorFactory factory)
33 de 75
david@edu.xunta.es
De navegacin:
public abstract boolean moveToFirst|Last|Previous|Next ()
public abstract boolean moveToPosition (int position)
public abstract boolean move (int offset)
De extraccin:
public abstract String|float|int|byte[]|etc getString|Float|Int|Blob|etc (int columnIndex)
De esquema:
public
public
public
public
public
abstract
abstract
abstract
abstract
abstract
// o filas.getLong(0);
// o filas.getString(1);
}
while(filas.moveToNext())
}
else {
// NO HAY FILAS
}
// o filas.getLong(0);
// o filas.getString(1);
34 de 75
david@edu.xunta.es
Listas
});
@Override
public void onNothingSelected(AdapterView<?> parent)
{
// el adaptador no tiene datos...
}
35 de 75
david@edu.xunta.es
@Override
public void onNothingSelected(AdapterView<?> arg0) {
// adaptador sin datos
});
}
}
36 de 75
david@edu.xunta.es
37 de 75
david@edu.xunta.es
Fijarse que estamos utilizando la llamada a super.getView(...) por lo que este mtodo ya
nos devuelve el layout que debemos utilizar. Solamente debemos 'personalizarlo' en funcin de los
datos correspondientes a la posicin indicada. En este caso, la asignacin de las propiedades debe
realizarse a todos los View's puesto que la llamada al getView del padre est optimizada y
posiblemente reutilice layouts que ya no son necesarios (convertView), pero cuyas propiedades
han sido modificadas anteriormente y ahora deben ser reasignadas en su totalidad.
38 de 75
david@edu.xunta.es
<TextView
android:id="@+id/nombre"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_toEndOf="@id/foto"
android:layout_toStartOf="@+id/mute"
android:gravity="left|center_vertical"
android:padding="5dp" />
<ImageView
android:id="@+id/mute"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:padding="10dp" />
</RelativeLayout>
39 de 75
david@edu.xunta.es
null);
tvNombre.setText(contacto.toString());
// Asignamos la foto al ImageView (ver ms adelante)
if (contacto.isMute())
mute.setImageResource(R.drawable.mute);
else
mute.setImageDrawable(null);
Reutilizar layouts creados anteriormente y que hay que destruir porque al desplazar la lista
pierden visibilidad. Estos layouts pueden reutilizarse cambiando el contenido de sus View's,
evitando as operaciones muy costosas como crear los objetos en memoria representrados
en sus respectivos archivos XML (inflado).
Utilizar el 'patrn' ViewHolder, almacenando una estructura de fcil acceso y extensible en
su elemento Tag almacenando informacin como las referencias a los Views, evitando
llamadas a mtodos muy costosos como findViewById.
40 de 75
david@edu.xunta.es
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
Contacto contacto = (Contacto)datos.get(position);
View layoutFila = convertView;
if(convertView==null) {
layoutFila = LayoutInflater.from(contexto).inflate(R.layout.filacontacto,
viewHolder = new ViewHolder();
viewHolder.foto
= (ImageView) layoutFila.findViewById(R.id.foto);
viewHolder.tvNombre
= (TextView) layoutFila.findViewById(R.id.nombre);
viewHolder.mute
= (ImageView) layoutFila.findViewById(R.id.mute);
layoutFila.setTag(viewHolder);
}
else
viewHolder = (ViewHolder) convertView.getTag();
String strNombre = contacto.getId() + ".jpg";
// Creamos una carpeta fotos en la carpeta /data/data/paquete/files/
String strRuta = contexto.getFilesDir().getAbsolutePath() + "/fotos/" +
File ruta = new File(strRuta);
if (ruta.exists()) {
Bitmap bitmap = BitmapFactory.decodeFile(strRuta);
viewHolder.foto.setImageBitmap(bitmap);
}
else
viewHolder.foto.setImageResource(R.drawable.contacto);
null);
strNombre;
viewHolder.tvNombre.setText(contacto.toString());
if (contacto.isMute())
viewHolder.mute.setImageResource(R.drawable.mute);
else
viewHolder.mute.setImageDrawable(null);
}
return layoutFila;
41 de 75
david@edu.xunta.es
AsistenteBD.java
import
import
import
import
android.content.ContentValues;
android.content.Context;
android.database.sqlite.SQLiteDatabase;
android.database.sqlite.SQLiteOpenHelper;
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
ListaClientesActivity.java
import
import
import
import
import
import
import
import
import
import
import
import
import
android.content.Intent;
android.database.Cursor;
android.database.sqlite.SQLiteDatabase;
android.support.v7.app.AppCompatActivity;
android.os.Bundle;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.widget.AdapterView;
android.widget.ArrayAdapter;
android.widget.ListView;
android.widget.Toast;
java.util.ArrayList;
42 de 75
david@edu.xunta.es
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_lista_clientes, menu); return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch(item.getItemId()) {
case R.id.action_settings:
return true;
case R.id.action_nuevoCliente:
Intent i = new Intent(this,ClienteActivity.class);
startActivityForResult(i, REQUEST_CODE_NUEVO_CLIENTE);
return true;
}
return super.onOptionsItemSelected(item);
}
43 de 75
david@edu.xunta.es
miniCliente.java
public class miniCliente {
int codCliente;
String nombre, apellidos;
boolean VIP;
public miniCliente(int codCliente,String nombre,String apellidos, boolean VIP) {
this.codCliente=codCliente;
this.nombre=nombre;
this.apellidos=apellidos;
this.VIP=VIP;
}
activity_cliente.xml
<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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="gal.amenedo.mantenimientodeclientes.ClienteActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/etNombre"
android:hint="@string/nombre"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/etApellidos"
android:hint="@string/apellidos"
android:layout_below="@+id/etNombre"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/etNIF"
android:hint="@string/NIF"
android:layout_below="@+id/etApellidos"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/listaProvincias"
android:layout_below="@+id/etNIF"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/VIP"
android:id="@+id/chkVIP"
android:layout_below="@+id/listaProvincias"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/guardar"
android:id="@+id/bGuardar"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
44 de 75
david@edu.xunta.es
ClienteActivity.java
public class ClienteActivity extends AppCompatActivity implements View.OnClickListener {
boolean esNuevoCliente;
int codCliente;
SQLiteDatabase bd;
EditText etNombre, etApellidos, etNIF;
Spinner listaProvincias;
CheckBox chkVIP;
Button bGuardar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bd = new AsistenteBD(this).getReadableDatabase();
codCliente = getIntent().getIntExtra("codCliente", -1);
esNuevoCliente = (codCliente == -1);
setResult(RESULT_CANCELED);
//region View's
setContentView(R.layout.activity_cliente);
etNombre = (EditText) findViewById(R.id.etNombre);
etApellidos = (EditText) findViewById(R.id.etApellidos);
etNIF = (EditText) findViewById(R.id.etNIF);
listaProvincias=(Spinner)findViewById(R.id.listaProvincias);
chkVIP = (CheckBox) findViewById(R.id.chkVIP);
bGuardar = (Button) findViewById(R.id.bGuardar);
//endregion
bGuardar.setOnClickListener(this);
setTitle(getString(esNuevoCliente ? R.string.nuevoCliente : R.string.editarCliente));
bGuardar.setText(getString(esNuevoCliente ? R.string.insertar : R.string.guardar));
int codProvincia=-1; //codProvincia virtual correspondiente al 1 elemento vaco de la lista de provincias
if (!esNuevoCliente) {
Cursor filas = bd.rawQuery("SELECT * FROM Clientes WHERE codCliente=?", new String[]{codCliente + ""});
int colIndexNombre = filas.getColumnIndex("nombre");
int colIndexApellidos = filas.getColumnIndex("apellidos");
int colIndexNIF = filas.getColumnIndex("NIF");
int colIndexCodProvincia = filas.getColumnIndex("codProvincia");
int colIndexVIP = filas.getColumnIndex("VIP");
if (filas.moveToFirst()) {
String nombre = filas.getString(colIndexNombre);
String apellidos = filas.getString(colIndexApellidos);
String NIF = filas.getString(colIndexNIF);
codProvincia = filas.getInt(colIndexCodProvincia);
boolean VIP = filas.getInt(colIndexVIP) == 1;
etNombre.setText(nombre);
etApellidos.setText(apellidos);
etNIF.setText(NIF);
// la provincia seleccionada se establece despus de poblar la lista para aprovechar el bucle
chkVIP.setChecked(VIP);
}
filas.close();
45 de 75
david@edu.xunta.es
@Override
public void onClick(View v) { // Guardar
String nombre=etNombre.getText().toString().trim();
String apellidos=etApellidos.getText().toString().trim();
String NIF=etNIF.getText().toString().trim();
int codProvincia=((Provincia)listaProvincias.getSelectedItem()).getCodProvincia();
Object objCodProvincia=(codProvincia==-1)?null:codProvincia;
//TODO: if(hayErroresEnDatos) { Toast - focus - return }
if(esNuevoCliente) {
String sql = "INSERT INTO clientes (nombre,apellidos,NIF,codProvincia,VIP) VALUES (?,?,?,?,?)";
Object[] valores = {nombre,apellidos,NIF,objCodProvincia,chkVIP.isChecked()?1:0};
bd.execSQL(sql,valores);
}
else {
String sql = "UPDATE Clientes SET nombre=?,apellidos=?,NIF=?,codProvincia=?,VIP=? WHERE codCliente=?";
Object[] valores = {nombre,apellidos,NIF,objCodProvincia,chkVIP.isChecked()?1:0, codCliente};
bd.execSQL(sql,valores);
}
setResult(RESULT_OK);
finish();
}
46 de 75
david@edu.xunta.es
Fragmentos
Un fragmento es un tipo de componente que encapsula un cdigo y (casi siempre) su
interfaz de usuario, y que puede ser reutilizado por distintas actividades. De hecho, un fragmento
siempre se utilizar en el contexto de una actividad, no puede utilizarse de forma independiente.
Se puede considerar que es una seccin de una actividad con su propio ciclo de vida, que
recibe sus propios eventos y que se pueden reutilizar por distintas actividades, aadiendose o
eliminandose incluso de manera dinmica.
La interaccin con los fragmentos se realiza a travs de Activity.getFragmentManager() y
Fragment.getFragmentManager(). Hay que tener tambin en cuenta que los Fragmentos no
heredan de Context, por lo que usaremos el mtodo getActivity() para obtener la actividad que lo
contiene en la ejecucin actual.
Ciclo de vida
Los fragmentos tienen su propio ciclo de vida, aunque ste siempre depende de su
actividad asociada. Es decir, si una actividad est parada, ninguno de sus fragmentos pueden estar
activos, y si una actividad es destruida, tambin se destruirn sus fragmentos.
47 de 75
david@edu.xunta.es
Los mtodos ms importantes que son llamados al crear un fragmento son los siguientes:
onAttach(Activity) .- llamado cuando el fragmento se asocia con la actividad
onCreate(Bundle) .- llamado para realizar la creacin inicial del fragmento
View onCreateView(LayoutInflater, ViewGroup, Bundle) .- Se usa para definir su layout,
es decir, debe crear y devolver la vista jerrquica (el View) asociada al fragmento.
Normalmente se infla y se devuelve el layout deseado a travs del bombn pasado
como primer parmetro, y como segundo parmetro se pasa el ViewGroup para que sea
el padre del view generado.
onActivityCreated(Bundle) .- avisa al fragmento de que su actividad se ha creado (ha
finalizado su Activity.onCreate())
onViewStateRestored(Bundle) .- avisa al fragmento de que el estado de su vista ha sido
restaurado.
onStart() .- hace el fragmento visible al usuario (igual que su actividad)
onResume() .- el fragmento interacta con el usuario (igual que su actividad)
Gestin esttica
La creacin bsica de fragmentos se realiza creando su clase java (que herede de Fragment
o alguna de sus subclases), creando su layout (si dispone de l, habitualmente s) y finalmente
inflndolo en su evento onCreateView.
Para utilizarlo necesitamos enlazarlo en los layouts en los que queramos que aparezca a
travs del elemento <fragment>
<fragment android:name="paquete.nombre.clase.fragmento"
android:id="@+id/id_fragmento"
...
android:layout_width="0dp"
android:layout_height="match_parent"
/>
48 de 75
david@edu.xunta.es
Gestin dinmica
Cuando se aade un fragmento estticamente a una actividad, declarndolo con
<fragment> en su layout, este no se podr eliminar en tiempo de ejecucin. Si queremos
intercambiar fragmentos de una actividad dinmicamente tendremos que crear utilizar
FragmentTransaction, que proporciona el API para aadir, eliminar y reemplazar fragmentos.
Si queremos aadir un fragmento a una actividad, siempre es necesario tener un View
preparado para contener a dicho fragmento. Una buena idea es utilizar un FrameLayout, que es el
contenedor ms sencillo.
Usando una nica FragmentTransaction podemos realizar mltiples operaciones con
fragmentos, como por ejemplo las siguientes:
49 de 75
david@edu.xunta.es
frgPrecioCoste.setValue2(6f);
}
<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.etmoneda.MainActivity" >
<fragment
android:id="@+id/frgPrecioCoste"
android:name="com.example.etmoneda.FrgMoneda"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
/>
<fragment
android:id="@+id/frgPrecioVenta"
android:name="com.example.etmoneda.FrgMoneda"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/frgPrecioCoste"
/>
</RelativeLayout>
public class FrgMoneda extends Fragment {
static final float CAMBIO = 1.5f;
EditText et1,et2;
@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frg_moneda, container);
et1 = (EditText) view.findViewById(R.id.et1);
et2 = (EditText) view.findViewById(R.id.et2);
TextWatcher tw1 = new TextWatcher() {
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) { actualizar(et1,et2,CAMBIO); }
};
TextWatcher tw2 = new TextWatcher() {
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) { actualizar(et2, et1,1/CAMBIO); }
};
et1.addTextChangedListener(tw1); et1.setTag(tw1);
et2.addTextChangedListener(tw2); et2.setTag(tw2);
return view;
50 de 75
public
public
public
public
void setValue1(float
void setValue2(float
String getValue1() {
String getValue2() {
david@edu.xunta.es
f) { et1.setText(""+f); }
f) { et2.setText(""+f); }
return et1.getText().toString(); }
return et2.getText().toString(); }
float f;
try {
f = Float.parseFloat(str);
resultado = "" + f*cambio;
}
catch(NumberFormatException ex) {
resultado = "***";
}
TextWatcher tw = (TextWatcher) et2.getTag();
et2.removeTextChangedListener(tw);
// para que no salte su TextWatcher (==bucle infinito)
et2.setText(resultado);
et2.addTextChangedListener(tw);
// restauramos su TextWatcher
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.etmoneda.MainActivity" >
<EditText
android:id="@+id/et1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:ems="10" >
<requestFocus />
</EditText>
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
<EditText
android:id="@+id/et2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:ems="10" >
</EditText>
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$" />
</LinearLayout>
51 de 75
david@edu.xunta.es
Principal.java
import android.os.Bundle;
import android.app.Activity;
public class Principal extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
layout/activity_main.xml
<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".Principal" >
<fragment
android:id="@+id/frgLista"
android:name="com.example.fragmentesqueleto.FrgLista"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
52 de 75
david@edu.xunta.es
layout-land/activity_main.xml
<LinearLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="horizontal"
android:baselineAligned="false"
tools:context=".Principal" >
<fragment
android:id="@+id/frgLista"
android:name="com.example.fragmentesqueleto.FrgLista"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<fragment
android:id="@+id/frgDetalleLandscape"
android:name="com.example.fragmentesqueleto.FrgDetalle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
</LinearLayout>
FrgLista.java
import
import
import
import
import
import
android.app.ListFragment;
android.content.Intent;
android.os.Bundle;
android.view.View;
android.widget.ArrayAdapter;
android.widget.ListView;
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
FrgDetalle frgDetalle = (FrgDetalle)
getFragmentManager().findFragmentById(R.id.frgDetalleLandscape);
if(frgDetalle!=null && frgDetalle.isInLayout()) {
frgDetalle.setKey(position);
}
else {
Intent intent = new Intent(this.getActivity(), DetalleActivity.class);
intent.putExtra("posicion", position);
startActivity(intent);
}
}
}
53 de 75
david@edu.xunta.es
DetalleActivity.java
import android.os.Bundle;
import android.app.Activity;
public class DetalleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detalle);
Bundle extras = getIntent().getExtras();
//if(extras!=null) // debera serlo siempre ...
int posicion = extras.getInt("posicion");
FrgDetalle frgDetalle = (FrgDetalle)
getFragmentManager().findFragmentById(R.id.frgDetallePortrait);
frgDetalle.setKey(posicion);
}
layout/activity_detalle.xml
<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".DetalleActivity" >
<fragment
android:id="@+id/frgDetallePortrait"
android:name="com.example.fragmentesqueleto.FrgDetalle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
FrgDetalle.java
import
import
import
import
import
import
android.app.Fragment;
android.os.Bundle;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.TextView;
54 de 75
david@edu.xunta.es
layout/frg_detalle.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tvPosition"
android:layout_width="107dp"
android:layout_height="wrap_content"
/>
</LinearLayout>
android.app.Activity;
android.app.ListFragment;
android.os.Bundle;
android.view.View;
android.widget.ArrayAdapter;
android.widget.ListView;
david@edu.xunta.es
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if(avisarA != null)
avisarA.clicEnLista(position);
else
throw new IllegalStateException("No hay nadie esperando el clic en la lista");
Principal.java
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
public class Principal extends Activity implements FrgLista.IClicEnLista {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void clicEnLista(int posicion) {
FrgDetalle frgDetalle=(FrgDetalle) getFragmentManager().
findFragmentById(R.id.frgDetalleLandscape);
if(frgDetalle!=null && frgDetalle.isInLayout()) {
frgDetalle.setKey(posicion);
}
else {
Intent intent = new Intent(this, DetalleActivity.class);
intent.putExtra("posicion", posicion);
startActivity(intent);
}
}
}
56 de 75
david@edu.xunta.es
Para utilizarla debemos crear un objeto de una clase que la herede, y llamar al mtodo
execute, que tiene tres parmetros (tres tipos genricos):
}
new BajarArchivos().execute(url1, url2, url3);
Existen cuatro pasos bsicos en la ejecucin de la tarea, reflejados en cuatro mtodos que
suelen ser sobreescritos para configurarla adecuadamente. Todos estos mtodos tienen acceso al
UI excepto el segundo (doInBackground) que se ejecuta en un hilo aparte y adems es de obligada
sobreescritura (abstract).
void onPreExecute() .- se invoca antes de que se empiece a ejecutar la tarea. Aqu se suele
inicializar la tarea y se muestra una barra de progreso en el UI.
david@edu.xunta.es
aqu es donde incluiremos el cdigo que deseamos ejecutar en un hilo separado. A este
mtodo se le pasa un nmero variable de parmetros del tipo Param (el que se hay
indicado en su creacin). Al acabar el mtodo se devolver el resultado de la operacin (del
tipo Result tambin indicado en su creacin), dato al que se acceder en el mtodo
onPostExecute. En este mtodo se suele llamar al mtodo publishProgress(Progress...) para
publicar una o ms unidades de progreso, unidades que se pasan automticamente al UI
Thread a travs del mtodo onProgressUpdate(Progress...).
import
import
import
import
import
import
import
import
import
import
java.net.MalformedURLException;
java.net.URL;
android.os.AsyncTask;
android.os.Bundle;
android.app.Activity;
android.app.ProgressDialog;
android.content.DialogInterface;
android.content.DialogInterface.OnCancelListener;
android.view.View;
android.widget.Toast;
58 de 75
david@edu.xunta.es
new BajarPaginas().execute(urlElPais,urlElMundo,urlLaVoz,urlFaro,urlDiario);
}
private class BajarPaginas extends AsyncTask<URL, Integer, Integer> {
@Override
protected void onPreExecute() {
dialogo.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancel(true);
}
});
dialogo.setProgress(0);
dialogo.show();
}
@Override
protected Integer doInBackground(URL... params) {
int i=0;
for(; i<params.length; i++) {
publishProgress(i);
// simulamos bajar la pgina URL
try { Thread.sleep(1000); } catch (InterruptedException e) {}
if(isCancelled()) break;
}
return i;
}
@Override
protected void onProgressUpdate(Integer... values) {
dialogo.setProgress(values[0]);
}
@Override
protected void onPostExecute(Integer result) {
dialogo.dismiss();
Toast.makeText(MainActivity.this,"Bajados " + result + " archivos", Toast.LENGTH_LONG).show();
}
@Override
protected void onCancelled(Integer result) {
dialogo.dismiss();
Toast.makeText(MainActivity.this, "Tarea cancelada por el usuario.\n" +
"Bajados hasta el momento " + result + " archivos", Toast.LENGTH_LONG).show();
}
}
}
59 de 75
david@edu.xunta.es
mapas 3D
mapas basados en vectores para mayor velocidad y/o anchos de banda reducidos
transiciones animadas
etc.
60 de 75
david@edu.xunta.es
carpeta
<android-sdk>/extras/google-play-
Una vez instalado el SDK de los servicios de Google Play, y slo en el caso de que queramos
probar la aplicacin en un emulador, debemos:
Instalar desde el SDK-Manager (si no lo estn ya) las Google APIs en las versiones que
queramos de Android (4.22 API 17 o mayor).
Crear un AVD cuya plataforma destino sea dicha Google API, para probar ah la aplicacin.
david@edu.xunta.es
carpeta <android-sdk>/extras/google/google_play_services/libproject/google-play-services_lib/ al
workspace. Si estamos usando Eclipse podemos usar File > Import > Android > Existing Android
Code into Workspace (chequeando la casilla de copiar). En Propiedades > Android del proyecto
recien importado, si la casilla de verificacin IsLibrary no est seleccionada, hacerlo.
Finalmente, debemos referenciar esta librera desde nuestra aplicacin de mapas. Si
estamos usando Eclipse podemos ir a Properties del proyecto > Categora Android > y aadimos la
librera.
62 de 75
david@edu.xunta.es
23:31
1.267 debug.keystore
1 archivos
1.267 bytes
0 dirs 44.290.076.672 bytes libres
C:\Users\_\.android>keytool
La segunda opcin es, si utilizamos Eclipse, acceder a la opcin Windows > Preferences y
acceder a la categora Android > Build.
63 de 75
david@edu.xunta.es
64 de 75
david@edu.xunta.es
android:name="com.google.android.maps.v2.API_KEY"
android:value="laAPIKeyObtenida"/>
android:name="android.permission.INTERNET"/>
android:name="android.permission.ACCESS_NETWORK_STATE"/>
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
Y finalmente, debido a que los mapas se renderizan utilizando OpenGL ES v2 (OpenGL for
Embedded Systems: variante simplificada de OpenGL para dispositivos integrados), indicaremos,
dentro del elemento <manifest>:
<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>
65 de 75
david@edu.xunta.es
Mapas
Para probar la aplicacin podemos utilizar un MapView o aadir el siguiente MapFragment
a un layout:
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.MapFragment"/>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MapFragment frgMap = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
GoogleMap map = frgMap.getMap();
if (map != null) {
LatLng cdm = new LatLng(42.389741,-8.709544);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(cdm, 17));
}
}
66 de 75
david@edu.xunta.es
67 de 75
david@edu.xunta.es
Estado inicial
tipo de mapa
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"
map:cameraBearing="112.5"
map:cameraTargetLat="-33.796923"
map:cameraTargetLng="150.922433"
map:cameraTilt="30"
map:cameraZoom="13"
map:mapType="normal"
map:uiCompass="false"
map:uiRotateGestures="true"
map:uiScrollGestures="false"
map:uiTiltGestures="true"
map:uiZoomControls="false"
map:uiZoomGestures="true"/>
O tambin podremos establecer los valores iniciales del mapa en ejecucin, creando un
objeto GoogleMapOptions de la siguiente manera:
68 de 75
david@edu.xunta.es
Bocadillos en https://developers.google.com/maps/documentation/android/infowindows
Figuras en https://developers.google.com/maps/documentation/android/shapes
Marcadores en https://developers.google.com/maps/documentation/android/marker
Imgenes en https://developers.google.com/maps/documentation/android/groundoverlay
y en https://developers.google.com/maps/documentation/android/tileoverlay
Cambiando la vista
Ver en https://developers.google.com/maps/documentation/android/views
private Marker creaMarcador(GoogleMap mapa, LatLng pos, String titulo, String datos, boolean arrastrable) {
return mapa.addMarker(new MarkerOptions()
.position(pos)
.title(titulo)
.snippet(datos)
.draggable(arrastrable)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW)));
}
69 de 75
david@edu.xunta.es
y aadiendo este cdigo a continuacin del anterior, creamos marcadores con una pulsacin larga:
mapa.setOnMapLongClickListener(new OnMapLongClickListener() {
@Override public void onMapLongClick(LatLng posicion) {
creaMarcador(mapa, posicion, "Posicin", posicion.toString(), false).showInfoWindow();
});
70 de 75
david@edu.xunta.es
Localizacin
Para gestionar la localizacin tenemos dos posibilidades: utilizar el API proporcionado por
la librera android.location (clase LocationManager), o utilizar el proporcionado por Google
Location Services API, parte de Google Play Services (clase LocationClient). Esta ltima es una
librera de alto nivel, mucho ms potente que la primera, y que gestiona de manera automtica
localizaciones, movimientos, precisin, as como una gestin ptima de consumo para aprovechar
mejor la batera.
Por lo general obtener la localizacin de un dispositivo no es sencillo por diversos motivos:
la precisin que vara en cada seal, estar situado en interiores, el usuario se mueve, multitud de
fuentes distintas de localizacin (GPS Global Positioning System , antenas de telefona e incluso
redes Wi-Fi).
Paquete android.location
La clase LocationManager nos proporciona acceso a distintos proveedores de localizacin,
los cuales disponen de los listeners (LocationListener) adecuados para que nos avisen
periodicamente de las nuevas localizaciones detectadas.
La clase LocationProvider es la superclase de los distintos proveedores de localizacin
existentes:
- NETWORK_PROVIDER
- GPS_PROVIDER
71 de 75
david@edu.xunta.es
72 de 75
david@edu.xunta.es
Juntndolo todo
73 de 75
david@edu.xunta.es
listaLongitudes = savedInstanceState.getDoubleArray("longitudes");
for(int i=0; i<listaLatitudes.length; i++) {
LatLng punto = new LatLng(listaLatitudes[i],listaLongitudes[i]);
listaPosiciones.add(punto);
pl.add(punto);
}
mapa.addPolyline(pl);
}
@Override
protected void onResume() {
super.onResume();
long minTiempo = 500; // en milisegundos
float minDistancia = 1; // en metros
locationManager.requestLocationUpdates(provider, minTiempo, minDistancia, this);
}
@Override
protected void onPause() {
super.onPause();
locationManager.removeUpdates(this);
}
private Marker creaMarcador(GoogleMap mapa, LatLng posicion, String titulo, String datos, boolean arrastrable) {
return mapa.addMarker(new MarkerOptions()
.position(posicion)
.title(titulo)
.snippet(datos)
.draggable(arrastrable)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW)));
}
// *** Interfaz LocationListener ******************
@Override
public void onLocationChanged(Location location) {
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
listaPosiciones.add(latLng);
int numPosiciones = listaPosiciones.size();
tvInfo.setText("N de puntos: " + numPosiciones );
pl.add(latLng);
//if(chkVerMapa.isChecked())
mapa.addPolyline(pl);
if(numPosiciones==1)
mapa.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));
}
@Override
public void onProviderDisabled(String provider) {
Toast.makeText(this, "Proveedor desactivado: " + provider, Toast.LENGTH_SHORT).show();
}
@Override
public void onProviderEnabled(String provider) {
Toast.makeText(this, "Proveedor activado: " + provider, Toast.LENGTH_SHORT).show();
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
74 de 75
david@edu.xunta.es
Bibliografa
http://developer.android.com
http://www.vogella.com
http://www.sgoliver.net
75 de 75