Upload
luis-burgos-salazar
View
146
Download
0
Embed Size (px)
Citation preview
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-1
App Android:Desarrollo de aplicaciones en Android.
Tutorial: Lector RSS de CAMONMaterial complementario (para hacer
“copy/paste” del código) proporcionado a los asistentes de la charla-taller impartida en
CAMON Alicante el 11-12 de enero de 2011.
Boyán Bonev, Pablo Suau, Miguel A. Lozano y el Dep. CCIA de la Universidad de Alicante
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-2
Puntos a tratar
• Creación del proyecto con Eclipse• Manifest, actividad principal y recursos• Vista de tabla, layout para las filas, adaptador• Descarga y parsing de XML en segundo plano• Descarga de imágenes en segundo plano• Diálogo, menú, cambios de orientación• Intents para síntesis del habla y navegador• Generar paquete y firmarlo
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-3
• Instalación: • Descargar y descomprimir Android SDK• Instalación del plug-in para Eclipse:
• Help > Install new software > Available software > Add: https://dl-ssl.google.com/android/eclipse/
• Ok, seleccionar el software, Next, Finish.
• Reiniciar Eclipse.
• Configuración de Eclipse:• Windows > Preferences > Android > SDK Location:
– Indicamos la ruta donde hemos descomprimido el Android SDK
Creación del proyecto con Eclipse
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-4
AVD Manager
• Crear nuevo dispositivo virtual (AVD)
• Seleccionar la versión de Android
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-5
Emulador
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-6
Crear el proyecto
• Asistente de Eclipse• Genera la estructura
básica del proyecto• AndroidManifest.xml• Actividad principal• Layout• Recursos
• Conviene seleccionar la mínima versión posible
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-7
Añadir recursos
• En values/strings.xml las cadenas de texto que vamos a mostrar en la interfaz
• En drawable, el icono de CAMON a alta, media y baja resolución
• En layout/main.xml la disposición de los componentes de la interfaz gráfica
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-8
strings.xml
• Introducimos (en la pestaña XML):<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">CAMON</string><string name="url_base">http://www.tucamon.es</string><string name="url_rss">http://www.tucamon.es/welcome/rss?format=rss</string>
<string name="noitems">Lista vacía</string><string name="errordered">¡Error en la descarga!</string>
<string name="calla">¡Calla!</string><string name="recargar">Recargar</string><string name="acerca_de">Acerca de...</string><string name="acerca_de_camon">Acerca de CAMON</string><string name="licencia">Esta aplicación descarga las noticias RSS de www.tucamon.es y las muestra. Fue desarrollada en el taller de Android llevado a cabo en el aula de CAMON de Alicante en enero de 2011, por los asistentes y por Boyán Bonev (Universidad de Alicante) Esta aplicación no es \"oficial\" de CAMON, no se puede vender y su finalidad es didáctica. La Universidad de Alicante y CAMON no se responsabilizan de su uso y modificaciones hechas por terceros.</string>
<string name="aceptar">Aceptar</string><string name="ok">OK</string><string name="leemelo">Léemelo</string><string name="web">Web</string><string name="atras">Atrás</string>
<string name="espere">Espere...</string><string name="descargandonoticias">Descargando noticias</string><string name="descargandonoticia">Descargando noticia</string><string name="imagenes_descargadas">Imágenes descargadas</string>
</resources>
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-9
Drawable
• En las carpetas drawable introducimos el icono de la CAMON, redimensionado a tres resoluciones diferentes:• drawable-hdpi/icon.png de 72x72 píxeles • drawable-mdpi/icon.png de 48x48 píxeles• drawable-ldpi/icon.png de 36x36 píxeles
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-10
layout/main.xml
• Introducimos un ListView que será la tabla donde iremos colocando las noticias de la web de CAMON. De momento no producirá ningún resultado visible.
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" >
<ListView android:id="@+id/ListView01" android:layout_width="fill_parent" android:layout_height="fill_parent"android:scrollbars="vertical" />
</LinearLayout>
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-11
AndroidManifest.xml
• Especificaremos que la aplicación necesita permisos para acceder a Internet
• El XML quedaría así:<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="es.tucamon" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Camon" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
</application> <uses-sdk android:minSdkVersion="4" /> <uses-permission android:name="android.permission.INTERNET"/></manifest>
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-12
Actividad principal
• es.tucamon.Camon.java• Añadimos dos cadenas que nos harán falta,
recogiéndolas de los recursos string.• Añadimos una variable Context porque
accederemos a ella varias veces.public class Camon extends Activity {
private static String URL_BASE;private static String URL_RSS;
Context context; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Context = getApplicationContext();
URL_BASE = getString(R.string.url_base); URL_RSS = getString(R.string.url_rss);
}}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-13
Acceso a componentes del layout
• Obtenemos una referencia utilizando el identificador del elemento.
• Asignamos un Listener para clicks sobre los items de la tablapublic class Camon extends Activity {
private static String URL_BASE;private static String URL_RSS;
Context context;
ListView listView;
/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Context = getApplicationContext();
URL_BASE = getString(R.string.url_base); URL_RSS = getString(R.string.url_rss);
listView = (ListView)findViewById(R.id.ListView01); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) { // en "id" tenemos el número de fila seleccionada
} }); }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-14
Clase noticia
• Creamos una nueva clase Noticia.java en el mismo paquetepublic class Noticia { private String titulo; private Spanned descripcion; private String link; private String fecha; private String linkImagen; private Drawable imagen;
public Noticia(){ titulo = ""; descripcion = new SpannedString(""); link = ""; fecha = ""; linkImagen = ""; } public Noticia(String titulo, String fecha, Spanned descripcion, String link,String linkImagen){ this.titulo=titulo; this.fecha=fecha; this.descripcion=descripcion; this.link=link; this.linkImagen=linkImagen; }
public String getTitulo() { return titulo; } public void setTitulo(String titulo) { this.titulo = titulo; } public Spanned getDescripcion() { return descripcion; } public void setDescripcion(Spanned descripcion) { this.descripcion = descripcion; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getFecha() { return fecha; } public void setFecha(String fecha) { this.fecha = fecha; } public String getLinkImagen() { return linkImagen; } public void setLinkImagen(String linkImagen) { this.linkImagen = linkImagen; } public void setImagen(Drawable imagen) { this.imagen = imagen; } public Drawable getImagen() { return imagen; }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-15
Layout para las filas
• En la carpeta res/layout creamos un nuevo xml llamado fila.xml
• Le añadimos una imagen con identificador @+id/FilaImagen y al lado de ella un Layout que disponga los componentes verticalmente, con dos campos de texto: @+id/FilaTexto1 y @+id/FilaTexto2
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal">
<ImageView android:id="@+id/FilaImagen" android:layout_width="100px" android:layout_height="70px" />
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical">
<TextView android:id="@+id/FilaTexto1" android:textSize="12sp" android:textColor="#E1DEFF" android:textStyle="bold" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
<TextView android:id="@+id/FilaTexto2" android:textSize="10sp" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
</LinearLayout></LinearLayout>
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-16
Adaptador para la tabla
• NoticiasAdapter colocará la información de los objetos Noticia en el layout fila.xml, para cada fila (para cada noticia)
public class NoticiasAdapter extends ArrayAdapter<Noticia> { ArrayList<Noticia> noticias; Context context;
public NoticiasAdapter(Context context, int textViewResourceId, ArrayList<Noticia> noticias) { super(context, textViewResourceId, noticias); this.noticias = noticias; this.context = context; }
@Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.fila, null); } Noticia noticia = noticias.get(position); if (noticia != null) { TextView tv1 = (TextView) convertView.findViewById(R.id.FilaTexto1); TextView tv2 = (TextView) convertView.findViewById(R.id.FilaTexto2); ImageView iv = (ImageView) convertView.findViewById(R.id.FilaImagen); if (tv1 != null) { tv1.setText(noticia.getTitulo()); } if (tv2 != null) { tv2.setText(noticia.getFecha()); } if (iv != null) { iv.setImageDrawable(noticia.getImagen()); } } return convertView; }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-17
Adaptador para la table
• Declaramos una lista de noticias y el adaptador
• Asignamos el adaptador a la tabla (listView) dentro del método onCreate
public class Camon extends Activity {
private static String URL_BASE;private static String URL_RSS;
Context context;ListView listView;ArrayList<Noticia> noticias;NoticiasAdapter noticiasAdapter;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Context = getApplicationContext();
URL_BASE = getString(R.string.url_base); URL_RSS = getString(R.string.url_rss);
noticias = new ArrayList<Noticia>(); noticiasAdapter = new NoticiasAdapter(context, R.layout.fila, noticias);
listView = (ListView)findViewById(R.id.ListView01); listView.setAdapter(noticiasAdapter); listView.setOnItemClickListener(new OnItemClickListener() {
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-18
Descarga y parsing de XML en segundo plano• Las operaciones lentas deben realizarse en
segundo plano, de lo contrario la aplicación dejará de responder y Android nos ofrecerá matarla.
• Cualquier operación de red se puede considerar una operación lenta.
• Para crear procesos en segundo plano podemos utilizar Threads. • El problema: no podemos acceder a los componentes
gráficos desde otro hilo que no sea el principal.
• La solución: Android nos ofrece las AsyncTask que facilitan el acceso a los componentes gráficos
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-19
Declaración de AsyncTask
• La declaramos dentro de la clase Camon para poder acceder a sus campos (sus variables)
public class Camon extends Activity {
private static String URL_BASE; private static String URL_RSS;
Context context; ListView listView; ArrayList<Noticia> noticias; NoticiasAdapter noticiasAdapter; TareaDescarga tarea;
@Override public void onCreate(Bundle savedInstanceState) { // ... lanzaDescargaDeNoticias(); }
void lanzaDescargaDeNoticias(){ try { tarea = new TareaDescarga(); tarea.execute(new URL(URL_RSS)); } catch (MalformedURLException e) { e.printStackTrace(); } } private class TareaDescarga extends AsyncTask<URL, String, List<Noticia>>{ // ... }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-20
Métodos de AsyncTaskprivate class TareaDescarga extends AsyncTask<URL, String, List<Noticia>>{ ArrayList<Noticia> noticiasDescargadas; ProgressDialog progressDialog; boolean error=false;
@Override protected List<Noticia> doInBackground(URL... params) {
return noticiasDescargadas; }
@Override protected void onPreExecute() {
}
@Override protected void onCancelled() {
}
@Override protected void onProgressUpdate(String... values) {
}
@Override protected void onPostExecute(List<Noticia> result) {
}}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-21
Métodos de AsyncTask
• El método doInBackground( ) realizará la descarga pero no podrá acceder a la interfaz; lo hará solicitando la ejecución de onProgressUpdate( ) y onPostExecute( )
• También declaramos un ProgressDialog que debemos ir actualizando durante el progreso de la descarga (y parsing de XML).
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-22
AsyncTask – acceso a interfaz@Overrideprotected void onPreExecute() { super.onPreExecute(); noticiasAdapter.clear(); progressDialog = ProgressDialog.show(Camon.this, getString(R.string.espere), getString(R.string.descargandonoticias),true,true); progressDialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { tarea.cancel(true); } });}
@Overrideprotected void onCancelled() { super.onCancelled(); noticiasAdapter.clear(); noticiasDescargadas = new ArrayList<Noticia>();}
@Overrideprotected void onProgressUpdate(String... progreso) { super.onProgressUpdate(progreso); progressDialog.setMessage(progreso[0]); noticiasAdapter.notifyDataSetChanged();}
@Overrideprotected void onPostExecute(List<Noticia> result) {super.onPostExecute(result); for(Noticia n:noticiasDescargadas){ noticiasAdapter.add(n); } noticiasAdapter.notifyDataSetChanged(); progressDialog.dismiss(); if(error){ Toast.makeText(context, R.string.errordered, Toast.LENGTH_LONG).show(); }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-23
AsyncTask – doInBackground( )
@Overrideprotected List<Noticia> doInBackground(URL... params) { try { URL url = params[0]; noticiasDescargadas = new ArrayList<Noticia>();
XmlPullParserFactory parserCreator = XmlPullParserFactory.newInstance(); XmlPullParser parser = parserCreator.newPullParser(); parser.setInput(url.openStream(), null); int parserEvent = parser.getEventType(); int nItems = 0;
while (parserEvent != XmlPullParser.END_DOCUMENT) { switch (parserEvent) { // Examinar la información parseada // ... } parserEvent = parser.next();
} } catch (Exception e) { Log.e("Net", "Error in network call", e); error = true; } return noticiasDescargadas;}
• Utilizaremos XmlPullParser para trocear el documento XML.
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-24
Información a parsear
• Código fuente descargado de http://www.tucamon.es/welcome/rss?format=rss
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"> <channel> <atom:link type="application/rss+xml" rel="self" href="http://www.tucamon.es/contenido/rss"/> <title>Feed de artículos</title> <link>http://www.tucamon.es/</link> <description>Feed de artículos</description> <language>es-ES</language> <item> <title>Presentación de Los zapatos de tacón rojos</title> <description><p>El <strong> ... <img alt="Zapatos" src="/photo_posts/0000/8463/zapatos.jpg?1293014230" /></description> <author>(Chivone)</author> <pubDate>Wed, 22 Dec 2010 11:46:57 +0100</pubDate> <link>/contenido/presentacion-de-los-zapatos-de-tacon-rojos</link> <guid>/contenido/presentacion-de-los-zapatos-de-tacon-rojos</guid> </item> <item> <title>D-FORMA inaugura su exposición en CAMON Madrid</title> <description><p>El colectivo D-Forma present&oacute; en CAMON& ... </description> <author>(Silvia Muñoz García)</author> <pubDate>Tue, 21 Dec 2010 19:56:47 +0100</pubDate> <link>/contenido/d-forma-emulo-exposicion-paisaje-terro</link> <guid>/contenido/d-forma-emulo-exposicion-paisaje-terro</guid> </item> ...
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-25
El bucle del XmlPullParser while (parserEvent != XmlPullParser.END_DOCUMENT) { switch (parserEvent) { case XmlPullParser.START_TAG: String tag = parser.getName(); if (tag.equalsIgnoreCase("item")) { publishProgress(getString(R.string.descargandonoticia)+" "+(++nItems)); Noticia noticia = new Noticia(); parserEvent = parser.next(); boolean itemClosed = false; while (parserEvent != XmlPullParser.END_DOCUMENT && !itemClosed) { switch (parserEvent) { case XmlPullParser.START_TAG: tag = parser.getName(); if (tag.equalsIgnoreCase("title")) { noticia.setTitulo(parser.nextText()); } if (tag.toLowerCase().contains("pubdate")) { noticia.setFecha(parser.nextText()); } if (tag.equalsIgnoreCase("link")) { noticia.setLink(URL_BASE+parser.nextText()); } if (tag.equalsIgnoreCase("description")) { String textoHtml = parser.nextText(); Spanned texto = Html.fromHtml(textoHtml, new ImageGetter(), null);
noticia.setDescripcion(texto);
String linkImagen = URL_BASE+imageSource; noticia.setLinkImagen(linkImagen); } break; case XmlPullParser.END_TAG: tag = parser.getName(); if(tag.equalsIgnoreCase("item")){ itemClosed = true; noticiasDescargadas.add(noticia); } break; } parserEvent = parser.next(); } } break; } parserEvent = parser.next();}
String imageSource=null; class ImageGetter implements Html.ImageGetter { public Drawable getDrawable(String source) { imageSource = source; return new BitmapDrawable(); } };
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-26
Primer prototipo
• Tras descargar las noticias debe mostrarlas en la tabla
• El formato es el indicado por fila.xml y el adaptador que hemos implementado.
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-27
Descarga de imágenes
• Añadimos a Noticia un método que descarga la imagen de la noticia, dada una URL
public class Noticia { private String titulo; private Spanned descripcion; private String link; private String fecha; private String linkImagen; private Drawable imagen; //...
public void loadImagen(String url) throws MalformedURLException, IOException{ InputStream is = (InputStream) new URL(url).getContent(); imagen = Drawable.createFromStream(is, "src"); }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-28
Descarga de imágenes en segundo plano
private class TareaDescargaImagen extends AsyncTask<List<Noticia>, String, Drawable>{ @Override protected Drawable doInBackground(List<Noticia>... arg0) { List<Noticia> noticias = arg0[0]; int imagenesSinCargar = noticias.size(); while(imagenesSinCargar > 0){ imagenesSinCargar = noticias.size(); for(Noticia n:noticias){ if(n.getImagen()==null || n.getImagen().getIntrinsicHeight() <= 0){ // Reintento necesario? try{ n.loadImagen(n.getLinkImagen()); publishProgress(""); }catch(Exception e){ n.setImagen(getResources().getDrawable(R.drawable.icon)); } }else{ imagenesSinCargar --; } } try { Thread.sleep(1000); } catch (InterruptedException e) { } } return null; } @Override protected void onPostExecute(Drawable result) { super.onPostExecute(result); Toast.makeText(context, getString(R.string.imagenes_descargadas), Toast.LENGTH_SHORT).show(); noticiasAdapter.notifyDataSetChanged(); }
@Override protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); noticiasAdapter.notifyDataSetChanged(); }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-29
Iniciar la descarga
• Podemos iniciar la descarga de imágenes una vez que las noticias se hayan cargado.
private class TareaDescarga extends AsyncTask<URL, String, List<Noticia>>{
// ...
@Override protected void onPostExecute(List<Noticia> result) { super.onPostExecute(result); for(Noticia n:noticiasDescargadas){ noticiasAdapter.add(n); } noticiasAdapter.notifyDataSetChanged(); progressDialog.dismiss(); if(error){ Toast.makeText(context, R.string.errordered, Toast.LENGTH_LONG).show(); } lanzaDescargaDeImagenes(); }}
void lanzaDescargaDeImagenes(){ TareaDescargaImagen tdi = new TareaDescargaImagen(); tdi.execute(noticias);}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-30
Menú – el recurso XML
• Creamos un New Android XML File, de tipo menú y lo llamamos menu.xml. El asistente lo coloca en res/menu/menu.xml
• Lo editamos e introducimos tres items:
<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android"><item android:id="@+id/item01" android:title="@string/calla"></item><item android:id="@+id/item02" android:title="@string/recargar"></item><item android:id="@+id/item03" android:title="@string/acerca_de"></item></menu>
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-31
Menú – creación en la Activity principal
@Overridepublic boolean onCreateOptionsMenu(Menu m) { getMenuInflater().inflate(R.menu.menu, m); return true;} @Overridepublic boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()){ case R.id.item01:
break; case R.id.item02: lanzaDescargaDeNoticias(); break; case R.id.item03:
break; } return true;}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-32
Diálogo “Acerca de”case R.id.item03:AlertDialog.Builder ab=new AlertDialog.Builder(Camon.this);ab.setTitle(R.string.acerca_de_camon);ab.setIcon(getResources().getDrawable(R.drawable.icon));ab.setMessage(R.string.licencia);ab.setPositiveButton(R.string.aceptar,null);ab.show();break;
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-33
Diálogo con la noticia
• Creamos una clase en un fichero NoticiaAlertDialog.java, en el mismo paquete.public class NoticiaAlertDialog extends AlertDialog.Builder { private Noticia n; private Context c;
/** * After creating the NoticiaAlertDialog, don't forget to call .show(); * @param context The main Activity context. * @param noticia Noticia object with information on Noticia */ protected NoticiaAlertDialog(Context context, Noticia noticia) { super(context); n = noticia; c = context; this.setTitle(n.getTitulo()); this.setIcon(context.getResources().getDrawable(R.drawable.icon)); this.setMessage(n.getDescripcion()); this.setNegativeButton(context.getString(R.string.atras), null); this.setPositiveButton(context.getString(R.string.leemelo), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {
} }); this.setNeutralButton(context.getString(R.string.web), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {
} }); }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-34
Mostrar diálogo con la noticia
• Lo mostraremos al hacer click sobre un item de la lista. Introducimos el código en el listener que ya teníamos asignado a la lista:
listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) { Noticia n = noticias.get((int)id); NoticiaAlertDialog nad = new NoticiaAlertDialog(Camon.this, n); nad.show(); }});
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-35
Cambios de orientación
• Cuando cambia la orientación del móvil de vertical a horizontal, Android la reinicia para que todos los componentes se coloquen de nuevo en la nueva configuración de pantalla.
• En consecuencia las noticias se empiezan a cargar de nuevo.
• Hay 3 maneras de evitarlo:• Deshabilitarlo: <activity android:name="Camon"
android:configChanges="keyboardHidden|orientation">
• Ocuparnos de recolocar los componentes (complicado).• Guardarnos las noticias para no volver a bajarlas.
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-36
Cambios de orientación
public void onCreate(Bundle savedInstanceState) { // ... final ArrayList<Noticia> data = (ArrayList<Noticia>) getLastNonConfigurationInstance(); if (data == null) { noticias = new ArrayList<Noticia>(); noticiasAdapter = new NoticiasAdapter(context, R.layout.fila, noticias); lanzaDescargaDeNoticias(); }else{ noticias = data; noticiasAdapter = new NoticiasAdapter(context, R.layout.fila, noticias); } // ... } @Override public Object onRetainNonConfigurationInstance() { final ArrayList<Noticia> data = noticias; return data; }
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-37
Abrir el navegador web con un intent
• Hay actividades que nos proporciona el sistema operativo, y que pueden ser ejecutadas por medio de un Intent. Abrimos el navegador web al pulsar el botón correspondiente:public class NoticiaAlertDialog extends AlertDialog.Builder {
// ...
protected NoticiaAlertDialog(Context context, Noticia noticia) {
// ...
this.setNeutralButton(context.getString(R.string.web), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(n.getLink())); c.startActivity( intent ); } }); }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-38
Síntesis del habla
• Se utiliza la clase TextToSpeech
• Sólo se puede utilizar su método .speak( ) una vez que ha terminado su inicialización (implementar método onInit).
• Además es necesario tener instalado el TextToSpeech en el sistema operativo. Se puede hacer una comprobación y si no está instalado, lanzar la actividad que ofrece su instalación.
• Una vez terminado, hay que llamar al método .shutdown( ) del objeto de clase TextToSpeech, para que se liberen los recursos nativos involucrados.
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-39
Speech• Creamos, en un fichero aparte, una clase que
encapsule toda la lógica necesaria para utilizar la síntesis del habla: Speech.java
package es.tucamon;import java.util.Locale;import android.app.Activity;import android.content.Intent;import android.speech.tts.TextToSpeech;import android.speech.tts.TextToSpeech.Engine;import android.speech.tts.TextToSpeech.OnInitListener;/** * Encapsulates some methods for using TextToSpeech from an Activity. * @author Boyan Bonev, Universidad de Alicante * @date December 20th, 2010 */public class Speech { private Activity activity; private TextToSpeech tts; private static final int TTS_DATA_CHECK = 1; private String text; private boolean isTTSinstalled = false; private boolean isTTSinitialized = false;
/** * Gets the reference to the Activity for being able to call Intents. * @param activity */ public Speech(Activity activity){ this.activity = activity; }
// ...}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-40
Métodos de Speech/** * Does not assume that TextToSpeech is installed but starts an intent * for checking whether it is or not. The Activity which calls this * function should implement: * protected void onActivityResult(int requestCode, int resultCode, Intent data) { * speech.installOrSpeak(requestCode,resultCode); * } * @param txt Text to speak */public void speakAfterCheckingForTTS(String txt){ this.text = txt; if(isTTSinitialized){ tts.speak(txt, TextToSpeech.QUEUE_ADD, null); }else{ Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); activity.startActivityForResult(intent, TTS_DATA_CHECK); }}
/** * Assumes that TextToSpeech is installed * @param txt Text to speak */public void speakWithoutCheckingForTTS(String txt){ tts = new TextToSpeech(activity, new OnInitListener() { public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { Locale loc = new Locale("es","",""); if (tts.isLanguageAvailable(loc) >= TextToSpeech.LANG_AVAILABLE) tts.setLanguage(loc); tts.setPitch(0.8f); tts.setSpeechRate(1.1f); isTTSinitialized = true; //Speak this: tts.speak(text, TextToSpeech.QUEUE_ADD, null); } } });}
/** * Called after checking for the installation of TTS * @param requestCode * @param resultCode */public void installOrSpeak(int requestCode, int resultCode){ if(requestCode == TTS_DATA_CHECK){ if (resultCode == Engine.CHECK_VOICE_DATA_PASS) { isTTSinstalled = true; speakWithoutCheckingForTTS(text); } else { Intent installVoice = new Intent(Engine.ACTION_INSTALL_TTS_DATA); activity.startActivity(installVoice); } } }
/** * Should be called in the onPause() or onStop() method of the Activity. */public void stop(){ if (tts != null) { tts.stop(); tts.shutdown(); isTTSinitialized = false; }}
public boolean isTTSinstalled() { return isTTSinstalled;}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-41
Uso de la clase Speech
• La declaramos en nuestra actividad Camon y la inicializamos en onCreate( ).
• Añadimos el método onActivityResult( ) e invocamos Speech.stop( ) cuando Camon termine su ejecución, en onStop( ), así como cuando se pulse el botón “Calla” del menú.
• Ya tenemos cuándo “callar”, ahora falta decidir cuándo hablar.
public class Camon extends Activity { Speech speech;
public void onCreate(Bundle savedInstanceState) {
// ... speech = new Speech(this);
// ... } protected void onActivityResult(int requestCode, int resultCode, Intent data) {
speech.installOrSpeak(requestCode,resultCode); } @Override public void onStop() { speech.stop(); super.onStop(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()){ case R.id.item01: speech.stop(); break; case R.id.item02: lanzaDescargaDeNoticias(); break; case R.id.item03: // ... break; } return true; }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-42
Speech en NoticiaAlertDialog
• Para pronunciar la descripción de la noticia al pulsar “leer” del NoticiaAlertDialog, le pasaremos por el constructor el objeto speech ya inicializado.
public class NoticiaAlertDialog extends AlertDialog.Builder { private Noticia n; private Context c; private Speech s;
protected NoticiaAlertDialog(Context context, Speech speech, Noticia noticia) { super(context); n = noticia; c = context; s = speech; this.setTitle(n.getTitulo()); this.setIcon(context.getResources().getDrawable(R.drawable.icon)); this.setMessage(n.getDescripcion()); this.setNegativeButton(context.getString(R.string.atras), null); this.setPositiveButton(context.getString(R.string.leemelo), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { s.speakAfterCheckingForTTS(n.getTitulo()+". "+n.getDescripcion()); } }); this.setNeutralButton(context.getString(R.string.web), new DialogInterface.OnClickListener() { // ... }); }}
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-43
Pasar Speech por el constructor
• No olvidemos pasar el objeto speech como parámetro del constructor de NoticiaAlertDialog
• La aplicación ya permite escuchar una descripción mientras seguimos examinando la lista.
listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id){ Noticia n = noticias.get((int)id); NoticiaAlertDialog nad = new NoticiaAlertDialog(Camon.this, speech, n); nad.show(); }});
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-44
Paquete
• Podemos generar un paquete instalable de prueba, firmándolo con un certificado “self-signed” (no firmado por ninguna autoridad certificadora).
• keytool -genkey -v -keystore mialmacen.keystore -alias aliasname -keyalg RSA -validity 10000• Introducimos una contraseña para el almacén y otra
para el certificado (puede ser la misma).
• Eclipse: sobre el proyecto, Android tools > Export signed package.• Nos pedirá abrir el almacén, y su contraseña.
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-45
Probar el paquete
• Debemos guardarlo en el dispositivo móvil, copiándolo por cable o enviándolo por e-mail.• Si al enviar el .apk a gmail, y al intentar abrirlo da un
error, se puede probar renombrándolo a .zip; entonces el cliente de gmail nos permite guardarlo; le cambiamos el nombre a .apk (con un gestor de archivos) y lo ejecutamos.
• Al ejecutarlo se inicia el instalador que nos advierte que la aplicación necesita acceso a Internet.
Formación en Tecnologías Java
CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-46
¿Preguntas...?