Programació multimèdia

Gairebé qualsevol aplicació actual per a dispositius mòbils que vulgui ser atractiva per als usuaris, aprofitar el maquinari i donar la màxima informació amb una pantalla reduïda, ha d’incorporar informació en un o més sentits per a l’usuari: imatges, vídeo, so, etc. Els dispositius mòbils també porten un conjunt de sensors (d’orientació, de posició geogràfica, etc.) que faciliten molt la interacció dels usuaris i les aplicacions.

Per construir aplicacions mòbils valuoses cal saber emprar tots aquests elements, i a Android no és gaire complicat fer-ho.

Visualització d'imatges

Existeixen diferents tècniques disponibles per visualitzar imatges a les aplicacions Android. Començarem amb la tècnica més senzilla, per visualitzar una imatge fixa, i a continuació veurem com canviar la imatge dinàmicament, és a dir, com a resposta a una acció de l’usuari. Finalment, anirem més enllà i visualitzarem tot un conjunt d’imatges fent servir una galeria d’imatges, que ens permetrà navegar entre totes les imatges d’una col·lecció.

Imatges estàtiques

Per tal de fer servir imatges a les vostres aplicacions, cal que primer les afegiu com a recursos de l’aplicació. Però abans és convenient preparar-les per tal que després no us donin cap mena de problemes. Concretament, el que cal fer és el següent:

  • Es recomana fer servir imatges en format PNG.
  • El nom dels fitxers d’imatge només pot contenir lletres minúscules i números, sense espais, signes, lletres majúscules ni amb accent, ce trencada o d’altres caràcters no anglesos.
  • Quan feu servir moltes imatges a una aplicació cal fixar-se bé en quina és la seva resolució (punts d’ample i d’alt). En general, és convenient que totes les imatges tinguin una mida semblant.

El format PNG

El format Portable Network Graphics (PNG) és un format d’imatge lliure (sense patents) i sense pèrdua de qualitat que, a més, permet transparència. Per aquestes raons és el format d’imatge recomanat per a les aplicacions Android, enfront d’altres formats populars com ara el JPEG.

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Imatges estàtiques” de la secció “Annexos”.

Un cop preparada la imatge cal incorporar-la a la vostra aplicació. En primer lloc heu de crear un nou projecte Android (podeu anomenar-lo multimedia1 i deixar la resta per defecte). Tot seguit, canvieu la vista del projecte d’Android a Project i desplegueu la carpeta del vostre projecte. Accediu tot seguit a la subcarpeta /app/src/main/res (res ve de resources, ‘recursos’, és a dir, les dades i fitxers addicionals com ara imatges o sons d’una aplicació) i, dins seu, a la subcarpeta drawable-hdpi (vegeu la figura).

Figura Subcarpeta drawable-hdpi a la vista project de la nostra aplicació

Quan hàgiu trobat aquesta subcarpeta, heu d’arrossegar-hi la imatge que voleu visualitzar per tal que passi a formar part de l’aplicació. Us apareixerà un diàleg per modificar el nom de la imatge i la seva ruta; cal que accepteu.

Resolució d'imatges

Com podeu observar, hi ha diverses subcarpetes el nom de les quals comença amb drawable ('dibuixable’). Per què? Una aplicació Android es pot executar en molts models diferents de dispositius, amb pantalles de mides i resolucions molt diferents. Quan creem les imatges per a les nostres aplicacions és recomanable crear-les per a resolucions diferents: molt altes, altes, mitjanes i baixes, per a dispositius amb aquestes resolucions. D’aquesta manera es garanteix que la visualització serà òptima en tots els casos. Precisament això representen les subcarpetes: imatges de baixa resolució (ldpi, low dots per inch, els punts per polsada de la imatge), mitjana (mdpi, medium dots per inch), alta (hdpi, high dots per inch), extra alta (xhdpi, extra high dots per inch) i extra extra alta (xxhdpi, extra extra high dots per inch). De moment només farem servir l’alta resolució, per no allargar els exemples.

D’aquesta manera, la imatge ja està incorporada a la vostra aplicació, però això no implica que sigui visible. La forma més senzilla de visualitzar una imatge en una aplicació Android és mitjançant el control ImageView, que es troba a la secció widgets de la paleta de l’editor del layout (obriu la carpeta de l’aplicació i aneu a /res/layout/activity_main.xml). A la figura es pot veure aquest control.

Figura Localització del control ImageView a l’editor de layout

Afegiu l'ImageView a la vostra aplicació a la cantonada superior esquerra, l'ImageView no ocuparà tota la pantalla, per tal que ho faci haurem de fer clic als botons set layout_width to match_parent i set layout_height to match_parent o canvieu a l’XML les propietats android:layout_width i android:layout_height a match_parent. Fent doble-clic sobre l'ImageView apareixerà un diàleg amb la propietat src, seleccioneu la imatge que heu copiat abans des del directory Drawable de la pestanya Project.

Un cop fet això ja podeu executar el programa i gaudir de la vostra primera imatge, com es mostra a la figura.

Figura Aplicació que visualitza una imatge

Descripció textual de les imatges

Si feu clic a la imatge o us posicioneu a ImageView de l’XML, veureu que s’hi mostra un avís (la icona d’una bombeta groga). Si feu clic a l’avís, veureu que aquest ens diu que falta la propietat contentDescription a la imatge. Aquesta propietat és una descripció textual de les imatges, necessària, per exemple, per a persones amb discapacitats visuals que fan servir assistents de veu.

Per acabar d’ajustar l’aplicació, cal que afegiu una cadena explicant el que hi ha a la imatge (al fitxer strings.xml), amb el nom descripcio_imatge, per exemple, i que torneu al main.xml i afegiu la línia del contentDescription (la darrera línia del següent codi) de manera que la part de l’ImageView quedi de la següent manera:

  1. <ImageView
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:id="@+id/imageView"
  5. android:layout_alignParentTop="true"
  6. android:layout_alignParentLeft="true"
  7. android:layout_alignParentStart="true"
  8. android:src="@drawable/ou"
  9. android:contentDescription="@string/descripcio_imatge"/>

Selecció dinàmica d'imatges

Acabeu de veure com mostrar una imatge seleccionant el fitxer d’imatge amb l’editor de layouts d’Android. Aquesta és la manera més senzilla i directa de mostrar una imatge a l’usuari. Però com es pot canviar aquesta imatge dinàmicament, és a dir, en resposta a una acció de l’usuari?

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Selecció dinàmica d’imatges” de la secció “Annexos”.

En aquest exemple modificareu l’aplicació anterior per tal que la imatge visualitzada canviï quan l’usuari la toqui. Per tal que la nova aplicació tingui un nom diferent, aneu al fitxer strings.xml i canvieu la cadena app_name.

A continuació afegiu una nova imatge a la carpeta /res/drawable-hdpi. De moment, si executeu l’aplicació només veureu la imatge inicial. Com podeu fer que el programa reaccioni i mostri una altra imatge quan la toqueu?

Obriu el codi del programa (fitxer MainActivity.java) per tal d’afegir el codi de resposta al clic a la imatge. En primer lloc, afegiu la modificació tal com segueix a la declaració de la classe per tal que pugui detectar clics:

  1. public class MainActivity extends ActionBarActivity implements View.OnClickListener {

Heu d’afegir el següent a les importacions que fa Java per tal que pugui treballar amb OnClickListener i poder accedir a l'ImageView:

  1. import android.view.View;
  2. import android.widget.ImageView;

El primer que heu de fer és associar el clic damunt la imatge amb una resposta per part de l’aplicació afegint el següent codi al mètode onCreate():

  1. ImageView visorImatge =(ImageView) findViewById(R.id.imageView);
  2. visorImatge.setOnClickListener(this);

A continuació cal crear el mètode de resposta al clic, que el que farà és accedir a l’ImageView i canviar el recurs al qual està associat:

  1. @Override
  2. public void onClick(View v) {
  3. // Canviem la imatge assignant-li l'altre recurs
  4. ImageView visorImatge = (ImageView) findViewById(R.id.imageView);
  5. visorImatge.setImageResource(R.drawable.pollastre);
  6. }

Com podeu veure, per cada recurs que afegim al projecte es crea un identificador (en realitat, un enter) que ens permet identificar els recursos i treballar-hi fàcilment. En aquest cas és R.drawable.pollet.

Amb les cadenes de text i els controls del layout passa el mateix. Podeu mirar (però no canviar) els fitxers R.java que es troben a la carpeta /app/build/source/r/, accessible des de la vista project de l’explorador del projecte.

Si proveu d’executar el programa, comprovareu com al començament es veu la imatge inicial, però quan la toqueu canvia per la segona imatge, com es veu a la figura.

Figura Aplicació amb la imatge canviada

Animacions

Un aspecte fonamental per donar una aparença més dinàmica a les aplicacions amb fort contingut multimèdia són les animacions, que pel que fa al desenvolupament per a Android podem classificar en dos tipus: animacions per interpolació (tween) i animacions per fotogrames (frames). Veureu exemples de tots dos tipus.

Per cert, quan hagueu experimentat amb els dos tipus d’animació veureu que és possible combinar-los per crear efectes encara més especials.

Animacions per interpolació

En primer lloc definirem què vol dir això d’interpolació, per tal d’entendre com funcionen aquests tipus d’animacions.

De manera intuïtiva, podem definir interpolació com l’acció de trobar els valors que té una funció entre dos punts coneguts. Nosaltres fem interpolacions quan, per exemple, unim dos punts amb una línia, o tres punts amb una corba.

Tween en anglès vol dir el mateix que between, és a dir, entre (entre dos punts, per exemple). S’anomenen animacions tween perquè troben el que s’ha de fer entre dos punts determinats pel programador.

Per crear una animació per interpolació només cal que definiu l’estat inicial d’una imatge (de fet, es pot aplicar a qualsevol altra vista, com ara un text) i l’estat final desitjat, i quant ha de trigar la imatge en passar de l’estat inicial al final, i l’interpolador ja fa la resta. Els paràmetres que podeu canviar de la nostra imatge són l’escala, la posició i la rotació. És a dir, podem fer que la imatge vagi canviat de mida, que es vagi movent o que vagi girant. O que es produeixi més d’un efecte alhora, com veureu de seguida.

També es pot canviar la transparència de la imatge, cosa que permet fer que les imatges apareguin o desapareguin de forma gradual.

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Animacions per interpolació” de la secció “Annexos”.

Per tal de poder experimentar amb les animacions, cal crear un nou projecte Android (l’anomenarem Multimedia3), afegint-li una imatge i un ImageView que ens la mostri. Us recomanem que feu servir una imatge en format PNG amb un motiu clar (una pilota, un cotxe, un animal…) i amb el fons transparent, de manera que no es vegi més que el motiu principal. En general serà més fàcil si es tracta d’un dibuix que no pas d’una foto. Centreu la imatge a la part superior del layout, com es veu a la figura.

Figura Aplicació d’animacions amb la imatge inicial

Per tal d’apreciar millor les animacions, fareu que s’iniciin quan fem clic a la imatge. Cal afegir un mètode com el següent a la classe MainActivity, que omplireu més endavant amb les instruccions escaients:

  1. public void onClick(View v){
  2.  
  3. }

I a continuació, heu de cercar la propietat de la imatge onClick, i escriure-hi onClick per tal que en fer clic a la imatge es cridi el mètode que acabem d’escriure. Això genera un atribut a la definició de l’element al layout.xml que fa que quan l’usuari cliqui l’ImageView es cridi el mètode onClick():

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  3. android:layout_height="match_parent" tools:context=".MainActivity">
  4.  
  5. <ImageView
  6. android:id="@+id/imageView1"
  7. android:layout_width="match_parent"
  8. android:layout_height="116dp"
  9. android:onClick="onClick"
  10. android:src="@drawable/basketball"/>
  11. </RelativeLayout>

Farem quatre animacions diferents:

  • Fer botar la pilota.
  • Màgia: una pilota que apareix.
  • Fer rodar la pilota.
  • Fer venir de lluny la pilota.

Fer botar la pilota

La primera animació que fareu consisteix a fer botar la pilota. Per afegir una animació a la vostra aplicació, en primer lloc cal crear una nova carpeta anomenada anim/ dins de la carpeta res/ (pitjant el botó dret sobre res/ i New/Android resouce directory i escollint de tipus anim). A continuació cal afegir un nou fitxer XML que contindrà les especificacions de l’animació. Pitgeu el botó dret sobre la carpeta res/ i aneu a New/New resource file. Anomeneu-lo, per exemple, botar.xml. El tipus de recurs ha de ser Animation, i el Root Element pot ser set, com es pot veure a la figura. De seguida veurem què volen dir aquestes opcions, de moment és suficient saber que són els diferents tipus d’animacions disponibles.

Recordeu: els noms de tots els fitxers (imatges, XML, etc.) que afegiu als vostres projectes han de ser en minúscules i sense espais ni signes (només el guió baix).

Figura Afegint un fitxer d’animació

El contingut del fitxer botar.xml ha de ser el següent:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <set xmlns:android="http://schemas.android.com/apk/res/android">
  3. <translate
  4. android:interpolator="@android:anim/accelerate_interpolator"
  5. android:repeatCount="infinite"
  6. android:repeatMode="reverse"
  7. android:fromYDelta="0%p"
  8. android:toYDelta="80%p"
  9. android:duration="1000"/>
  10. </set>

Veiem ràpidament què volen dir aquestes línies:

  • set permet definir una animació composta de més d’una transformació. En aquest cas no s’aplica, però d’aquesta manera es pot ampliar fàcilment més endavant.
  • xmlns:android defineix l’espai de noms android per tal que les línies següents puguin fer-lo servir com a prefix per a la resta d’opcions.
  • translate indica que comença una transformació de posició, volem moure la imatge.
  • interpolator és la funció que fareu servir per calcular els valors intermedis. Si és linear vol dir que els valors canvien a ritme constant, i si és accelerate vol dir que els valors canvien cada cop més ràpid. En aquest cas és el que ens interessa, perquè la pilota caigui cada cop més ràpid.
  • repeatCount: defineix quantes vegades s’ha de repetir l’efecte. En aquest cas, volem que es repeteixi per sempre.
  • repeatMode té dos possibles valors: restart faria que en caure la pilota aquesta tornés directament al començament, mentre que reverse faria que la pilota pugués fer el recorregut invers al que ha fet per caure.
  • fromYDelta: estableix en quin punt (de la y, és a dir, a la coordenada vertical) comença l’animació. Es pot posar un valor absolut en píxels (“140”) , un valor relatiu a la vista (“40%”) o al seu parent, en el layout de l’aplicació, indicant un valor tipus “50%p”.
  • toYDelta: estableix en quin punt de la coordenada vertical acaba l’animació. El format és el mateix que per fromYDelta.
  • duration: estableix la durada de l’animació en mil·lisegons.

Per a les animacions de tipus translate també hi ha les opcions fromXDelta i toXDelta, equivalents als de la Y que acabem de veure.

Per tal que l’animació es visualitzi, cal carregar-la, associar-la a la imatge i posar-la en marxa. Aneu al mètode onClick() que heu escrit abans i afegiu el següent:

  1. ImageView imatge = (ImageView)findViewById(R.id.imageView1);
  2. Animation animacioPilota = AnimationUtils.loadAnimation(this, R.anim.botar);
  3. imatge.startAnimation(animacioPilota);

Fixeu-vos que s’ha creat un identificador per a l’animació amb el nom del fitxer on l’heu definida.

Màgia: una pilota que apareix

Sovint fareu servir animacions per fer aparèixer imatges i d’altres elements de forma gradual, per tal de donar un aspecte més interessant a la vostra aplicació. Fer-ho és molt senzill, només cal que creeu un altre fitxer anomenat res/anim/apareixer.xml i que li doneu el següent contingut:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <set xmlns:android="http://schemas.android.com/apk/res/android">
  3. <alpha
  4. android:fromAlpha="0"
  5. android:toAlpha="1"
  6. android:duration="2000"
  7. />
  8. </set>

L’estructura és molt semblant a la del fitxer d’animació anterior, però en aquest cas ens trobem alguns elements nous:

  • alpha es refereix al grau de transparència d’una imatge, o sigui que les animacions d’aquest tipus el que faran és canviar la transparència amb el temps.
  • fromAlpha és el valor inicial de transparència; 0 vol dir transparent del tot, és a dir, invisible.
  • toAlpha és el valor final de transparència; 1 vol dir opac del tot.

Una aplicació Android pot tenir més d’un fitxer d’animacions i seleccionar qualsevol d’ells.

Per tal que la vostra aplicació faci servir aquesta nova animació, cal editar el mètode onClick() i fer que l’ordre loadAnimation carregui R.anim.apareixer, que és la nova animació. Proveu d’executar l’aplicació per notar l’efecte. Com veieu, en aquest cas l’animació no es repeteix si no torneu a clicar a la imatge.

Fer rodar la pilota

Si el que voleu veure és la pilota rodant d’una banda a una altra, cal que afegiu un nou fitxer d’animació anomenat rodar.xml amb el següent contingut:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <set xmlns:android="http://schemas.android.com/apk/res/android">
  3. <rotate
  4. android:interpolator="@android:anim/linear_interpolator"
  5. android:repeatCount="infinite"
  6. android:repeatMode="reverse"
  7. android:fromDegrees="0"
  8. android:toDegrees="360"
  9. android:pivotX="50%"
  10. android:pivotY="50%"
  11. android:duration="4000"/>
  12.  
  13. <translate
  14. android:interpolator="@android:anim/linear_interpolator"
  15. android:repeatCount="infinite"
  16. android:repeatMode="reverse"
  17. android:fromXDelta="-50%p"
  18. android:toXDelta="50%p"
  19. android:duration="4000"/>
  20. </set>

La novetat, en aquest cas, és que hi ha dues transformacions: la pilota gira (rotate) però també es desplaça. Per això cal escriure dues entrades. A la secció rotate hi ha algunes opcions noves:

L’ordre en el qual s’escriuen les diferents transformacions en un fitxer d’animacions és fonamental, perquè és l’ordre en el qual s’apliquen. Proveu a canviar d’ordre el rotate i el translate i comprovareu la diferència.

  • fromDegrees: en quin angle (mesurat en graus) comença la rotació.
  • toDegrees: en quin angle acaba la rotació. En aquest exemple acaba als 360° per tal que faci la volta completa. Si voleu que doni més voltes podeu posar un valor més gran que 360, per exemple 720 perquè doni dues voltes cada cop.
  • pivotX i pivotY: indiquen el punt de la imatge respecte al qual aquesta girarà: no és el mateix girar respecte del centre, com en aquest cas, que girar respecte d’un costat.

Per cert, recordeu d’editar l’onClick() perquè carregui aquesta animació i no cap altra.

Fer venir de lluny la pilota

Finalment, creareu una animació que mostri la imatge venint de lluny i alhora girant. Aquesta animació quedaria molt maca amb la imatge d’una portada de diari, com a les pel·lícules antigues on s’acostumava a fer aquest efecte quan sortia alguna notícia als diaris.

Afegiu un altre fitxer d’animació que podeu anomenar venir.xml i escriviu el següent codi:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <set xmlns:android="http://schemas.android.com/apk/res/android">
  3. <rotate
  4. android:interpolator="@android:anim/linear_interpolator"
  5. android:duration="500"
  6. android:repeatCount="8"
  7. android:pivotX="40%"
  8. android:pivotY="40%"
  9. android:fromDegrees="0"
  10. android:toDegrees="360"/>
  11.  
  12. <scale
  13. android:interpolator="@android:anim/linear_interpolator"
  14. android:repeatCount="0"
  15. android:duration="4000"
  16. android:pivotX="50%"
  17. android:pivotY="50%"
  18. android:fromXScale="0"
  19. android:toXScale="1"
  20. android:fromYScale="0"
  21. android:toYScale="1"/>
  22. </set>

Ara, al rotate hem canviat el punt de pivot per tal que la imatge giri una mica més i no només doni voltes sobre ella mateixa.

Reproducció d'animacions

Des de la versió d’Android Honeycomb (3.x) es pot afegir una opció a l’etiqueta set per tal de reproduir les operacions d’animació una darrera de l’altra i no totes alhora: <set android:ordering = “sequentially”>.

Pel que fa a la transformació d’escala (scale), els seus paràmetres són força evidents després d’haver treballat amb les altres transformacions. Bàsicament cal indicar l’escala inicial i final en X i Y.

Podem associar una mateixa animació a diversos visors alhora, per tal de donar un estil uniforme a la nostra aplicació.

Animacions per a fotogrames

Sovint, les transformacions que ens proporciona Android no ens serveixen per aconseguir l’animació que necessitem a la nostra aplicació i és necessari generar-les amb programes específics d’animació. En aquest punt tenim diferents possibilitats, com ara generar un vídeo o també, si l’animació no és massa llarga, podem generar un conjunt d’imatges amb els diferents passos de l’animació, de manera que passant ràpidament les imatges l’usuari tingui la sensació de presenciar moviment.

Fotogrames a alta velocitat

Cadascuna de les imatges de la seqüència rep el nom de fotograma, i al cap i a la fi el cine, la televisió i els videojocs, el que fan és mostrar fotogrames a alta velocitat (com a mínim, 25 per segon) per tal de donar-nos la impressió que estem veient moviment continu.

Comenceu un nou projecte Android (en l’exemple l’anomenarem Multimedia4). Trobeu un conjunt d’imatges que pugueu fer servir com a animació, com per exemple el que es mostra a la figura.

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Animacions per fotogrames” de la secció “Annexos”.

Figura Conjunt d’imatges per crear una animació per fotogrames

Amb vuit o deu imatges és suficient per apreciar l’efecte, no cal que feu servir moltes més imatges.

Tot i que l’efecte és més evident si les imatges formen part d’una seqüència, també podeu fer servir imatges independents.

En primer lloc, assegureu-vos que els fitxers d’imatge tenen un nom adient per afegir-los als projectes: noms en minúscula, sense espais i sense signes que no siguin guions baixos. A continuació, arrossegueu les imatges a la carpeta res/drawable. Tot seguit, afegiu-hi un nou fitxer XML de tipus (Resource Type) drawable i escriviu a Root Element: animation-list, que és una llista d’animació. Tal com mostra la figura, doneu-li un nom vàlid al fitxer i continueu.

Figura Creació d’una llista d’animació

A continuació, editeu el fitxer XML per tal que quedi de la següent manera (en comptes de “frameXX”, haureu d’escriure el nom de les imatges que heu afegit prèviament, sense l’extensió):

  1. <?xml version="1.0"encoding="utf-8"?>
  2. <animation-list xmlns:android="http://schemas.android.com/apk/res/android">
  3. <item android:drawable="@drawable/frame00" android:duration="200"/>
  4. <item android:drawable="@drawable/frame01" android:duration="200"/>
  5. <item android:drawable="@drawable/frame02" android:duration="200"/>
  6. <item android:drawable="@drawable/frame03" android:duration="200"/>
  7. <item android:drawable="@drawable/frame04" android:duration="200"/>
  8. <item android:drawable="@drawable/frame05" android:duration="200"/>
  9. <item android:drawable="@drawable/frame06" android:duration="200"/>
  10. <item android:drawable="@drawable/frame07" android:duration="200"/>
  11. </animation-list>

Amb aquest fitxer es defineix quina és la seqüència d’imatges que formaran l’animació i quina és la durada de cada fotograma, en mil·lisegons.

Tot seguit cal crear un ImageView al layout i, a la propietat src, associar-li l’animació que tot just heu creat (en aquest exemple s’anomena brickfilm), com es mostra a la figura.

Figura Seleccionar l’animació per a fotogrames

Si proveu d’executar l’aplicació només veureu el primer fotograma; cal dir-li a l’animació que s’executi. Per fer-ho, aneu al MainActivity.java i afegiu les següents instruccions al final del mètode onCreate():

  1. ImageView imatge = (ImageView) findViewById(R.id.imageView);
  2. AnimationDrawable animacio = (AnimationDrawable) imatge.getDrawable();
  3. animacio.start();

Comproveu que s’han afegit els imports i executeu l’aplicació. Bàsicament el que heu fet amb aquestes instruccions és accedir a la imatge, obtenir el que està dibuixant amb getDrawable() (en aquest cas, l’animació, perquè així li ho heu indicat), i dir-li a aquesta animació que comenci a executar-se. A la figura podeu veure una captura de l’aplicació en funcionament.

Figura Visualització de l’animació per a fotogrames

Com veieu, l’animació es repeteix de manera indefinida. Hi ha l’opció de dir-li que s’aturi en acabar, si voleu provar-la aneu al fitxer que defineix l’animació (brickfilm.xml en aquest exemple) i afegiu la següent opció a l’etiqueta animation-list per tal que quedi:

  1. <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">

Les animacions es poden aplicar a botons i d’altres controls. Això s’acostuma a fer servir a jocs i aplicacions semblants, per donar-los un aspecte més lúdic i dinàmic.

Les animacions per fotogrames es poden aplicar tant a la view que estem fent servir com al seu fons (background). Penseu que, com que les imatges en format PNG poden tenir transparència, seria possible tenir una imatge de fons i una animació al damunt, o fins i tot una animació a sobre de l’altra. Això sí, cal ser una mica artista perquè estèticament això quedi bé!

So i música

Tot seguit veureu com afegir arxius de so o música a les vostres aplicacions i com reproduir-los de manera més o menys sofisticada. També es poden reproduir els fitxers d’àudio que hi ha emmagatzemats al dispositiu, o fins i tot accedir a àudio online.

Els formats d’àudio suportats per Android són, principalment: mp3, ogg, flac i wav.

Reproducció bàsica

Si només voleu que la vostra aplicació reprodueixi una pista d’àudio quan s’engega, els passos a seguir són senzills.

En primer lloc s’ha d’afegir un arxiu d’àudio amb un format compatible amb Android i un nom de fitxer compatible amb Java. Cal crear una nova carpeta a res/ que es digui raw/ i arrossegar el fitxer d’àudio a aquesta carpeta.

Per escoltar àudio no cal afegir cap control al layout, creareu dinàmicament un MediaPlayer que és l’objecte encarregat de reproduir àudio i vídeo. Per això només cal afegir les següents línies al final de l’onCreate() de l’activity principal (nompista és el nom del fitxer d’àudio que hem afegit, sense l’extensió):

  1. MediaPlayer so = MediaPlayer.create(this, R.raw.nompista);
  2. so.start();

Podeu comprovar que en obrir l’aplicació es reprodueix l’arxiu de so. Això sí, sense opció a aturar-lo ni res de semblant. Hi ha molt a millorar!

Controls de reproducció

Tot i que és possible controlar el MediaPlayer amb botons i d’altres elements estàndard, hi ha un widget específic per a aquesta tasca que anomenat MediaController. Aquest tipus de control es pot afegir des de l’editor del layout, però és més senzill afegir-lo des del codi directament.

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Controls de reproducció de so” de la secció “Annexos”.

Primerament, afegiu a l’activitat el següent:

  1. public class MainActivity extends ActionBarActivity implements MediaController.MediaPlayerControl {

Amb aquest codi indiqueu que la vostra activitat durà a terme les funcions de control de so que ofereix MediaController. Cal afegir un import amb android.widget.MediaController. Aleshores, l’Android Studio indicarà un error a l’activitat, dient que ha de definir els mètodes abstractes de MediaPlayerControl. La manera més senzilla de resoldre-ho és situar-se sobre l’error i clicar a l’opció Implement methods de la bombeta d’error (o prémer Alt + Enter); aleshores afegirà automàticament deu mètodes (canPause fins start), que anireu fent servir per afegir funcionalitat a la vostra aplicació.

També cal anar al fitxer main.xml i assignar-li al RelativeLayout un ID, per exemple vista_controls_so, com podeu veure a continuació:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  3. android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
  4. android:paddingRight="@dimen/activity_horizontal_margin"
  5. android:paddingTop="@dimen/activity_vertical_margin"
  6. android:paddingBottom="@dimen/activity_vertical_margin" android:id="@+id/vista_controls_so" tools:context=".MainActivity">

Tot seguit, afegiu els següents atributs a la classe de l’activitat:

  1. MediaPlayer so;
  2. MediaController controls;

Tot seguit, aneu a l’onCreate(), elimineu les línies que vau afegir per a la primera versió del programa de reproducció de so i afegiu-hi les següents línies:

  1. so= MediaPlayer.create(this, R.raw.bluestutorial);
  2. controls = new MediaController(this);
  3. controls.setMediaPlayer(this);
  4. controls.setAnchorView(findViewById(R.id.vista_controls_so));

Amb aquestes instruccions, el que esteu fent és crear el MediaPlayer per reproduir la pista d’àudio, i després crear el MediaController per disposar d’uns controls per a l’àudio. A continuació associeu el codi del MediaController a l’activitat actual, i finalment definiu a quina vista de l’aplicació s’ha de dibuixar el MediaController.

Que encara no s’hi veu res? Això és perquè, per defecte, el MediaController s’amaga automàticament. Cal que l’obligueu a mostrar-se quan toqueu la pantalla, afegint el següent mètode a la vostra activitat:

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event) {
  3. controls.show();
  4. return false;
  5. }

Si al show() li passeu un nombre enter n, els controls d’àudio romandran visibles durant n segons; si li passeu un zero, els controls no s’amagaran. Ara sí, si executeu l’aplicació ja s’haurien de veure els controls d’àudio com es mostren a la figura.

Figura Controls d’àudio del Mediaplayer

De moment, aquests controls no fan gaire cosa perquè heu d’omplir els seus mètodes amb codi que faci el que vosaltres voleu. Comenceu pel més bàsic: fer que el botó de reproduir (play) engegui la música. Per això cal anar al mètode start (creat automàticament per implementar MediaController.MediaPlayerControl) i afegir:

  1. so.start();

Amb la qual cosa li dieu al reproductor d’àudio que comenci a reproduir la pista d’àudio. També s’ha d’anar al mètode canPause() i fer que torni true per tal que el botó de reproduir sigui actiu. Amb això ja s’hauria d’escoltar quan toqueu el play. Comproveu-ho.

Per tal que la interacció entre els controls i el reproductor d’àudio sigui més completa, cal anar omplint els mètodes que s’han generat per tal que quedin com es veu a continuació:

  1. @Override
  2. public int getCurrentPosition() {
  3. // Torna la posició actual a la pista d'àudio
  4. return so.getCurrentPosition();
  5. }
  6.  
  7. @Override
  8. public int getDuration() {
  9. // Torna la durada de la pista d'àudio
  10. return so.getDuration();
  11. }
  12.  
  13. @Override
  14. public boolean isPlaying() {
  15. // Torna cert quan s'està reproduint àudio
  16. return so.isPlaying();
  17. }
  18.  
  19. @Override
  20. public void pause() {
  21. // Quan l'usuari toca el botó de pausa
  22. so.pause();
  23. }
  24.  
  25. @Override
  26. public void seekTo(int pos) {
  27. // Per anar a una posició de la pista
  28. so.seekTo(pos);
  29. }

La durada de les pistes multimèdia i les posicions es mesuren en mil·lisegons.

La resta de mètodes es poden deixar tal com estan ara mateix. Amb això ja tindreu un reproductor d’àudio amb funcions de reproducció, pausa i cerca d’una posició.

Vídeo

Reproduir vídeos als dispositius Android és molt senzill, com veureu en aquest apartat. Per començar, afegireu els vídeos com a recursos a la vostra aplicació d’exemple. També es poden reproduir vídeos d’Internet o emmagatzemats al dispositiu.

Els formats de vídeo més àmpliament suportats pels dispositius Android són l’MPEG-4 (.mp4) i el 3GPP (.3gp).

Per tal de visualitzar un vídeo a les vostres aplicacions, cal que afegiu el següent element a la nostra aplicació: dins de la pestanya, a l’editor gràfic de layouts Images & Media, afegiu un VideoView, que serà la pantalla amb la qual es veurà el vídeo.

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Reproducció de vídeo” de la secció “Annexos”.

Per afegir un vídeo com a recurs, heu de crear una carpeta anomenada raw/ dins de la carpeta res/ i arrossegar-hi un vídeo amb el format adequat i amb un nom compatible amb Java, com és habitual. Al vostre exemple, aquest vídeo s’anomenarà castell.3gp.

A continuació cal anar al codi de l’activitat i afegir-hi el següent:

  1. VideoView visor = (VideoView)findViewById(R.id.videoView1);
  2. Uri video = Uri.parse("android.resource://" +
  3. getPackageName() + "/"
  4. + R.raw.castell);
  5. visor.setVideoURI(video);
  6. visor.setMediaController(new MediaController(this));
  7. visor.start();

Fixeu-vos que el VideoView no accepta directament un identificador de recurs com a vídeo, per aquesta raó cal accedir-hi amb l’expressió que forma una adreça URI (Universal Resource Identifier, identificador universal de recursos) accedint als recursos empaquetats a l’aplicació que esteu construint.

A continuació s’associa aquest recurs al VideoView, es crea un MediaController per tal de tenir controls de reproducció i, finalment, es fa que el vídeo comenci. És clar que per a què tot això funcioni cal afegir els imports adients.

Malauradament, la reproducció de vídeo no sempre funciona a l’emulador d’Android, raó per la qual és possible que només escolteu el so del vídeo. En aquest cas us caldrà passar l’aplicació a un dispositiu real per poder visualitzar-lo.

Geolocalització

GPS significa Global Positioning System, és a dir, sistema de posicionament global, i és un sistema que permet conèixer la situació pròpia (longitud i latitud) mitjançant senyals enviats per satèl·lits.

Una de les característiques més interessants dels dispositius mòbils és la seva capacitat de detectar la seva posició geogràfica per tal de donar a l’usuari un seguit d’informació, com ara mostrar la seva situació en un mapa, llistar els recursos més propers (restaurants, benzineres…), registrar el seu recorregut mentre marxa per la muntanya, etc. El mètode més conegut de geolocalització, és a dir, d’obtenció de la posició geogràfica, és el sistema GPS.

Hi ha altres mètodes de geolocalització, com ara detectar les xarxes Wi-Fi presents i deduir a partir de les xarxes identificades quina és la posició geogràfica. Aquest mètode no és tan precís com el GPS, però d’altra banda pot funcionar sota terra o dins d’edificis, on el senyal GPS generalment no arriba.

En aquest apartat veureu com obtenir les coordenades GPS i després les fareu servir per visualitzar la vostra posició en un mapa.

Coordenades GPS

Primerament cal donar permís a la vostra aplicació per accedir al sensor GPS. Per fer-ho, després de crear una nova aplicació Android, editeu el fitxer AndroidManifest.xml i afegiu la següent línia just abans de l’etiqueta <application>:

La posició de l’usuari del dispositiu és un aspecte més de la seva privacitat, per això cal que l’aplicació demani permís per accedir al GPS.

  1. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Coordenades GPS” de la secció “Annexos”.

Per tal que la vostra activitat pugui rebre les actualitzacions de la posició del dispositiu, cal que implementi LocationListener, aleshores editeu la capçalera de l’activitat perquè quedi de la següent manera:

  1. public class MainActivity extends ActionBarActivity implements LocationListener {

Afegiu l’import android.location.LocationListener si no ho ha fet ja l’Android Studio. Veureu que ens marca com a error el nom de la classe perquè no implementa els mètodes del LocationListener; al missatge que apareix, feu clic a “Implement methods” per tal que els hi afegeixi. Concretament, els mètodes que hem d’implementar són:

  • onLocationChanged: és cridat quan la localització GPS canvia, típicament perquè el dispositiu es desplaça a una altra posició. Serveix per actualitzar la posició a l’aplicació.
  • onProviderDisabled: es fa la crida quan l’usuari ha deshabilitat el servei GPS.
  • onProviderEnabled: al contrari que l’anterior, és cridat quan l’usuari ha habilitat el servei.
  • onStatusChanged: és cridat quan el servei canvia d’estat, d’activat a desactivat o viceversa. Ens serveix per saber quan el provider és incapaç d’obtenir la informació de posició o si la recupera després d’un temps d’inactivitat. El paràmetre status ens informa d’aquest fet, pren tres valors: OUT_OF_SERVICE, TEMPORARILY_UNAVAILABLE i AVAILABLE.

Abans de completar els mètodes anteriors cal activar el sistema de localització afegint el següent codi a l’onCreate() de la vostra activitat:

  1. LocationManager gestorLoc = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
  2. gestorLoc.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);

Com sempre, caldrà afegir els imports que us digui l’Android Studio. Amb aquestes instruccions s’accedeix al servei GPS i es demana que les actualitzacions de posició s’enviïn a aquesta activitat. Els arguments numèrics són, respectivament, el temps aproximat (en mil·lisegons) entre actualitzacions i la distància mínima (en metres) que ha de canviar la posició per rebre una actualització. A una aplicació real caldrà posar-hi valors adients.

El més important és el mètode onLocationChanged(), que és cridat cada cop que la posició del dispositiu canvia. Editeu-lo per tal que quedi com segueix:

  1. @Override
  2. public void onLocationChanged(Location location) {
  3.  
  4. String text = "Posició actual:\n" +
  5. "Latitud = " + location.getLatitude() + "\n" +
  6. "Longitud = " + location.getLongitude();
  7.  
  8. Toast.makeText(getApplicationContext(), text,
  9. Toast.LENGTH_LONG).show();
  10.  
  11. }

El mètode rep la posició actual i el que fa és formar un text amb la latitud i longitud, que són els paràmetres que defineixen la posició geogràfica.

Les toast (torrades) són petits missatges temporals que podem fer servir per mostrar informació a l’usuari.

També omplireu els mètodes següents per tal que l’aplicació notifiqui a l’usuari quan la cobertura GPS es perd o es recupera:

  1. @Override
  2. public void onProviderDisabled(String provider) {
  3. Toast.makeText(getApplicationContext(),
  4. "GPS desactivat per l'usuari",
  5. Toast.LENGTH_LONG ).show();
  6. }
  7.  
  8. @Override
  9. public void onProviderEnabled(String provider) {
  10. Toast.makeText(getApplicationContext(),
  11. "GPS habilitat per l'usuari",
  12. Toast.LENGTH_LONG).show();
  13. }
  14.  
  15. @Override
  16. public void onStatusChanged(String provider, int status, Bundle extras) {
  17.  
  18. String missatge = "";
  19. switch (status) {
  20. case LocationProvider.OUT_OF_SERVICE:
  21. missatge = "GPS status: Out of service";
  22. break;
  23. case LocationProvider.TEMPORARILY_UNAVAILABLE:
  24. missatge = "GPS status: Temporarily unavailable";
  25. break;
  26. case LocationProvider.AVAILABLE:
  27. missatge = "GPS status: Available";
  28. break;
  29. }
  30.  
  31. Toast.makeText(getApplicationContext(),
  32. missatge,
  33. Toast.LENGTH_LONG).show();
  34. }
  35.  
  36. }

Ara bé, com podeu provar aquesta aplicació? La primera opció i la més realista és executar-la en un dispositiu real, però si això no és possible, teniu dues opcions que no són massa realistes però que us permetran de provar-la.

En primer lloc, executeu l’aplicació a l’emulador. Amb l’aplicació en marxa, obriu una finestra del terminal i escriviu el següent:

  1. telnet localhost 5554
  2. geo fix 30.1 28.4

Amb això, el que feu és connectar-vos a l’emulador i enviar-li la longitud i la latitud. El número 5554 és el port per defecte pel qual us podeu connectar a l’emulador. El port específic que fa servir el vostre emulador es mostra a la seva barra d’aplicació, mireu per exemple el 5554 a la figura. Si mireu la pantalla de l’emulador, veureu com apareix la informació que li heu enviat, semblant a la toast de la figura. Compte, que desapareix en uns segons!

La latitud és la distància (angular) d’un punt a l’equador, és a dir, a l’eix sud-nord. La longitud és la distància (angular) d’un punt al meridià de Greenwich, és a dir, a l’eix est-oest.

Figura Toast amb les coordenades GPS

L’altra opció, només disponible si feu servir l’entorn Android Studio, és anar a Tools/Android/Android Device Monitor, concretament a la pestanya Emulator Control, que conté diferents eines per controlar l’emulador: per simular una trucada telefònica, la recepció d’un SMS… Si continueu baixant dins d’aquesta finestra hi trobareu una eina anomenada Location Controls que permet enviar la posició a l’emulador (pestanya GPS), i que es mostra a la figura.

Figura Eina per enviar coordenades GPS

Si introduïu una coordenada i premeu el botó Send l’enviareu a l’emulador. Però aquesta eina us ofereix una manera molt millor d’introduir coordenades GPS: fer servir fitxers GPX.

Els fitxers GPX (GPS eXchange format) són fitxers XML que contenen recorreguts expressats com a seqüències de coordenades GPS, amb un temps d’arribada associat a cada punt.

Si obriu la pestanya GPX podreu carregar un fitxer en aquest format i enviar-lo a l’emulador amb el botó Play. També hi ha un control anomenat Speed que us permet enviar les coordenades més ràpidament si voleu, sobretot perquè sovint aquests fitxers s’han enregistrat caminant o amb bicicleta i, per tant, es mouen lentament pel mapa. A la figura en podeu veure un exemple.

Figura Càrrega d’una ruta GPX

D’aquesta manera podeu veure com es van actualitzant les coordenades a l’aplicació, fent el recorregut del mapa.

L’altra opció, KML (Keyhole Markup Language), és equivalent a GPX amb la diferència que és el format que fa servir Google Earth.

Podeu descarregar gratis fitxers GPX o KML al web http://es.wikiloc.com.

Visualització de mapes

Tot just heu vist com llegir la posició geogràfica del dispositiu (i de l’usuari, segurament!) mitjançant el servei GPS, però només l’heu pogut fer servir per mostrar les coordenades per pantalla amb una simple toast. La vostra aplicació seria més atractiva si en comptes d’escriure uns números per pantalla dibuixés el mapa amb la situació actual i l’anés actualitzant a mesura que us moveu. Això és el que fareu en aquest apartat.

Com probablement ja sabeu, la font més important de mapes i imatges per satèl·lit és Google Maps/Earth, i atès que Google ha estat la creadora d’Android ho ha posat força fàcil perquè feu servir els seus mapes a les aplicacions Android.

Per poder-ho fer, cal configurar el vostre projecte per tal que faci servir les Google API i els Google Play services, és a dir, el conjunt de biblioteques i serveis per accedir a les aplicacions i recursos de Google.

En primer lloc configurarem un emulador per treballar amb les google APIs. Haurem d’anar a l’Android SDK Manager i descarregar la darrera versió de:

  • Google APIs Intel x86 Atom System Image de la darrera versió de l’SDK.
  • Google APIs de la darrera versió de l’SDK.
  • Google Play services, que trobareu a la secció extras.

Figura Android SDK Manager amb Google API instal·lat

Per tal d’executar un projecte que faci servir les Google APIs necessitem disposar d’un AVD amb les llibreries i aplicacions necessàries. Així, comproveu que el target del vostre emulador sigui “Google APIs”, com podeu observar a la figura.

Figura Emulador amb Google APIs

Ara que ja ho tenim tot llest per poder crear el projecte, introduïu la següent configuració:

  • Application name: MultimediaMaps
  • Company domain: cat.xtec.ioc
  • Minium SDK: API 10: Android 2.3.3 (Gingerbread)

A la selecció d’activitats escolliu Google Maps Activity (figura) i deixeu els paràmetres per defecte.

Figura Selecció de l’activitat

Al directori /res/values trobarem un fitxer anomenat google_maps_api.xml amb un contingut similar al següent:

  1. <resources>
  2. <!--
  3. TODO: Before you run your application, you need a Google Maps API key.
  4.  
  5. To get one, follow this link, follow the directions and press "Create" at the end:
  6.  
  7. https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=FF:47:8C:B5:B3:8A:XX:13:79:XX:E5:DB:4F:XX:8F:6A:A5:BD:FF:F5%3Bioc.xtec.cat.multimediamaps
  8.  
  9. You can also add your credentials to an existing key, using this line:
  10. FF:47:8C:B5:B3:8A:XX:13:79:XX:E5:DB:4F:XX:8F:6A:A5:BD:FF:F5;ioc.xtec.cat.multimediamaps
  11.  
  12. Once you have your key (it starts with "AIza"), replace the "google_maps_key"
  13. string in this file.
  14. -->
  15. <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">
  16. YOUR_KEY_HERE
  17. </string>
  18. </resources>

Figura Creació de la clau

Accediu a la URL que se us proposa des d’un navegador web i, després de crear un nou projecte, se us permetrà crear una clau per poder fer servir l’API de Google Maps (com podeu veure a la figura). Per afegir més aplicacions lligades a una mateixa clau hem d’afegir línies amb l’SHA1, un ”;” i el package name de l’aplicació. Una vegada generada, copieu el valor del camp CLAVE DE LA API i enganxeu-lo a l’XML com a contingut de l’etiqueta string (substituirem el text YOUR_KEY_HERE).

Amb aquests passos ja podrem executar l’emulador per comprovar el funcionament del mapa, però abans observem quines són les modificacions que ha fet l’Android Studio per tal que tot funcioni. Si obriu el fitxer AndroidManifest.xml podreu observar el següent contingut:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="ioc.xtec.cat.multimediamaps" >
  4.  
  5. <uses-permission android:name="android.permission.INTERNET" />
  6. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  7. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  8. <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
  9. <!--
  10. The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
  11. Google Maps Android API v2, but are recommended.
  12. -->
  13. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  14. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  15.  
  16. <application
  17. android:allowBackup="true"
  18. android:icon="@mipmap/ic_launcher"
  19. android:label="@string/app_name"
  20. android:theme="@style/AppTheme" >
  21. <meta-data
  22. android:name="com.google.android.gms.version"
  23. android:value="@integer/google_play_services_version" />
  24. <meta-data
  25. android:name="com.google.android.maps.v2.API_KEY"
  26. android:value="@string/google_maps_key" />
  27.  
  28. <activity
  29. android:name=".MapsActivity"
  30. android:label="@string/title_activity_maps" >
  31. <intent-filter>
  32. <action android:name="android.intent.action.MAIN" />
  33.  
  34. <category android:name="android.intent.category.LAUNCHER" />
  35. </intent-filter>
  36. </activity>
  37. </application>
  38.  
  39. </manifest>

Podem observar que afegeix els següents permisos:

  • android.permission.INTERNET: per accedir a Internet, necessari per descarregar els mapes.
  • android.permission.ACCESS_NETWORK_STATE: per detectar canvis en la xarxa.
  • android.permission.WRITE_EXTERNAL_STORAGE: permet escriure a l’emmagatzemament del telèfon, en aquest cas és necessari per descarregar fitxers per generar la memòria cau dels mapes.
  • com.google.android.providers.gsf.permission.READ_GSERVICES: permet a l’API accedir als serveis de Google.
  • android.permission.ACCESS_COARSE_LOCATION: accés a la posició aproximada mitjançant la xarxa: wifi i torres de telefonia.
  • android.permission.ACCESS_FINE_LOCATION: accés a la posició més acurada, fent servir a més el GPS per obtenir-la.

I a més, afegeix dues etiquetes meta-data amb la informació sobre la versió dels Google Play Services i la clau que hem afegit al fitxer google_maps_api.xml.

El layout de l’aplicació constarà d’un fragment que serà manipulat des del fitxer MapsActivity.java. Si ens fixem amb el codi de l’activitat, podem veure com acaba fent una crida a setUpMap() que afegeix un marcador al mapa en la coordenada (0,0) amb el títol de ”Marker”.

Executeu el projecte i visualitzareu un mapa del món amb un marcador en la coordenada geogràfica (0,0), és a dir, a la latitud i longitud 0. Si feu clic sobre la marca us sortirà el text ”Marker” tal com podeu veure a la figura.

Figura Google Maps a l’emulador

Per tal d’afegir controls de zoom a la vostra aplicació de manera que pugueu apropar-vos des de l’emulador d’una manera còmoda cal que modifiqueu el mètode setUpMapIfNeeded() per tal que quedi de la següent manera:

  1. private void setUpMapIfNeeded() {
  2. // Do a null check to confirm that we have not already instantiated the map.
  3. if (mMap == null) {
  4. // Try to obtain the map from the SupportMapFragment.
  5. mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
  6. .getMap();
  7. // Check if we were successful in obtaining the map.
  8. if (mMap != null) {
  9. setUpMap();
  10. UiSettings settings = mMap.getUiSettings();
  11. settings.setZoomControlsEnabled(true);
  12. }
  13. }
  14. }

És a dir, afegim les línies UiSettings settings = mMap.getUiSettings(); i settings.setZoomControlsEnabled(true); per obtenir la configuració del mapa i modificar el valor de ZoomControlsEnabled per posar-lo a true.

Podreu trobar més mètodes per configurar el vostre mapa mitjançant la variable mMap; per exemple, si voleu canviar el tipus de mapa podeu accedir al mètode mMap.setMapType(int type), que rep com a argument el tipus de mapa: GoogleMap.MAP_TYPE_NORMAL, GoogleMap.MAP_TYPE_HYBRID, GoogleMap.MAP_TYPE_SATELLITE, GoogleMap.MAP_TYPE_TERRAIN i GoogleMap.MAP_TYPE_NONE. Proveu els diferents valors i observeu els canvis que es produeixen en la representació.

Seguiment de la vostra posició

Si volem que el mapa s’actualitzi amb la nostra posició haurem d’implementar LocationListener i, cada cop que es produeixi un canvi d’aquests, actualitzar el mapa.

Així, feu que la classe MapsActivity implementi LocationListener i recordeu afegir els mètodes que Android Studio ens exigeix.

  1. public class MapsActivity extends FragmentActivity implements LocationListener {
  2. ...
  3. @Override
  4. public void onLocationChanged(Location location) {
  5.  
  6. }
  7.  
  8. @Override
  9. public void onStatusChanged(String provider, int status, Bundle extras) {
  10.  
  11. }
  12.  
  13. @Override
  14. public void onProviderEnabled(String provider) {
  15.  
  16. }
  17.  
  18. @Override
  19. public void onProviderDisabled(String provider) {
  20.  
  21. }
  22. ...
  23. }

Des dels mètodes onStatusChanged, onProviderEnabled i onProviderDisabled podríem informar sobre els canvis que es produeixin al servei GPS; ara, però, ens centrarem en el mètode onLocationChanged, on haurem de:

  1. Obtenir la posició a partir del paràmetre location.
  2. Crear la càmera (àrea del mapa que es visualitza en un moment determinat) amb la posició i un nivell de zoom.
  3. Desplaçar la càmera al punt proposat.

LatLng emmagatzema un parell de coordenades, la latitud i la longitud.

Així, afegirem el següent codi a onLocationChanged:

  1. @Override
  2. public void onLocationChanged(Location location) {
  3. // Creem un LatLng a partir de la posició
  4. LatLng posicio = new LatLng(location.getLatitude(),location.getLongitude());
  5. // Afegim la càmera amb el punt generat abans i un nivell de zoom
  6. CameraUpdate camera = CameraUpdateFactory.newLatLngZoom(posicio, 19);
  7. // Desplacem la càmera al nou punt
  8. mMap.moveCamera(camera);
  9. }

Una vegada hem obtingut la posició, fem la crida al mètode newLatLngZomm(LatLng latLng, float zoom) de CameraUpdateFactory i que rep dos paràmetres: el primer determinarà el punt on es situarà la càmera i el segon especificarà el nivell de zoom, que haurà de prendre una valor entre 2.0f i 21.0f. Finalment, actualitzem la càmera del mapa.

Per tal que el listener comenci a controlar els canvis de posició, recordeu afegir a l'onCreate el següent codi:

  1. LocationManager gestorLoc = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
  2. gestorLoc.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);

Si proveu d’enviar unes coordenades GPS a l’emulador, comprovareu com canvia la vista. Lògicament, com més zoom hi apliquem, més evident serà el moviment. Per veure l’efecte és recomanable que carregueu un fitxer GPX amb el DDMS (Tools/Android/Android Device Monitor), i que gaudiu de la passejada!

Afegint marques al mapa

Sovint les aplicacions que mostren mapes afegeixen marques per assenyalar a l’usuari els punts que li siguin interessants, com ara els restaurants més propers, les parades de metro o les ciutats que ha visitat. En aquest apartat veureu com afegir aquests senyals (icones) al vostre mapa, i com situar-los on vosaltres voleu. Per fer-ho continuareu amb l’aplicació anterior.

Per anar afegint les marques a mesura que va canviant la posició haurem de modificar el mètode onLocationChanged. El procediment serà el següent:

  1. Creem un element del tipus MarkerOptions.
  2. Afegim les propietats desitjades: Títol (title), posició (position), descripció (snippet) i icona (icon).
  3. Afegim el marcador al mapa.
  4. Si hem de manipular el marcador posteriorment, ens podem guardar l’objecte Marker que retorna la funció addMarker; una possible utilitat de guardar-lo és fer la crida a showInfoWindow() per fer que es mostri la informació del marcador sense haver de fer clic sobre ell.

El codi del mètode onLocationChanged quedarà de la següent manera:

  1. @Override
  2. public void onLocationChanged(Location location) {
  3.  
  4. // Creem un LatLng a partir de la posició
  5. LatLng posicio = new LatLng(location.getLatitude(), location.getLongitude());
  6. // Afegim la càmera amb el punt generat abans i un nivell de zoom
  7. CameraUpdate camera = CameraUpdateFactory.newLatLngZoom(posicio, 19);
  8. // Desplacem la càmera al nou punt
  9. mMap.moveCamera(camera);
  10.  
  11. // Creem i afegim el marcador
  12. MarkerOptions opcions = new MarkerOptions();
  13. opcions.title("Títol");
  14. opcions.position(posicio);
  15. opcions.snippet("Descripció del marcador");
  16. opcions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE));
  17. // Aquí l'afegim al mapa i ens el guardem a una variable (ja es veu la icona al mapa però cal fer-hi clic per veure la informació)
  18. Marker marca = mMap.addMarker(opcions);
  19. // Fem que es mostri la informació sense haver-hi de fer clic
  20. marca.showInfoWindow();
  21. }

Si executem l’aplicació i anem enviant diferents posicions fent servir l’Android Device Manager veurem un resultat similar al que es mostra en la figura.

Podeu descarregar el codi corresponent a aquesta activitat a l’annex anomenat “Google Maps” de la secció “Annexos”.

Figura Google Maps amb marcadors

Dibuixar

Sovint es necessita que les aplicacions puguin dibuixar qualsevol element a qualsevol posició de la pantalla. L’exemple més clar serien les aplicacions de dibuix, però n’hi ha moltes altres que també ho fan servir: per mostrar gràfiques i esquemes, per a jocs, per mostrar marques especials…

A més, moltes vegades no podeu fer servir imatges predefinides perquè necessiteu canviar-ne la mida, la forma o el color.

Tot seguit veureu com es poden dibuixar formes geomètriques tot controlant els seus paràmetres (mida, posició, color…) segons les indicacions de l’usuari. Concretament, quan l’usuari toqui en una posició de la pantalla, s’hi dibuixarà una figura. Per donar-li més varietat, l’aplicació anirà dibuixant formes diferents cada cop que toqueu la pantalla.

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Dibuixar” de la secció “Annexos”.

Comenceu per crear un nou projecte Android, anomenat per exemple Dibuixar, que tingui el layout per defecte. En aquest exemple no fareu servir aquest layout, sinó que el generareu i treballareu amb ell des del codi Java. Per això ara cal que obriu l’activitat principal i escriviu el següent codi, eliminant les instruccions que calgui:

  1. package ioc.xtec.cat.dibuixar;
  2.  
  3. import android.graphics.Color;
  4. import android.os.Bundle;
  5. import android.support.v7.app.ActionBarActivity;
  6.  
  7.  
  8. public class MainActivity extends ActionBarActivity {
  9.  
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState) {
  12. super.onCreate(savedInstanceState);
  13.  
  14. Llenc llenc = new Llenc(this);
  15. llenc.setBackgroundColor(Color.BLACK);
  16. setContentView(llenc);
  17. }
  18. }

De moment hi ha dues idees clau: estem creant un objecte de classe Llenc (pels llenços de pintar) i fem servir aquest objecte com a vista de la nostra aplicació en comptes del layout per defecte.

En informàtica s’anomena llenç (canvas en anglès) a qualsevol superfície en la qual es dibuixa.

Què és aquesta classe Llenc? Doncs l’heu de definir vosaltres. Per fer-ho, creeu una nova classe fent clic al botó dret sobre l’espai de noms i seleccionant /New/Java Class i farem que aquesta nova classe derivi de la classe View afegint public class Llenc extends View {; concretament, escolliu View (android.view). Amb això l’Android Studio us indica un error, que heu de resoldre posant-vos a sobre de la línia i triant l’opció “Create constructor matching super” i seguidament “View(context:Context)”. Un cop fet això, us hauria de quedar un codi com el següent:

  1. package ioc.xtec.cat.dibuixar;
  2.  
  3. import android.content.Context;
  4. import android.view.View;
  5.  
  6. public class Llenc extends View {
  7. public Llenc(Context context) {
  8. super(context);
  9. }
  10. }

Què vol dir que la classe Llenc derivi de View? Doncs que es tracta d’una vista igual que les que feu servir a les vostres aplicacions (com ara els TextViews, els botons, els ImageView, els MapView, o qualsevol altre). Això implica que vosaltres teniu el dret de dibuixar el que vulgueu en aquest element. I com que hem dit que els continguts de la nostra aplicació es mostren com un objecte de classe Llenc, doncs ja teniu la manera de dibuixar el que vulgueu.

Per començar, cal crear alguns atributs de la vostra classe i indicar que els objectes Llenc es poden tocar (amb setFocusable()):

  1. public class Llenc extends View {
  2.  
  3. Paint pintar = new Paint();
  4.  
  5. int x = 100;
  6. int y = 100;
  7. int cont = 0;
  8.  
  9. public Llenc(Context context) {
  10. super(context);
  11. setFocusable(true);
  12. }
  13. }

L’objecte de classe Paint és una mena de pinzell amb el qual controlareu el color i altres paràmetres del que dibuixareu; amb la x i la y controlareu les coordenades on ha tocat l’usuari, i finalment el comptador el fareu servir per alternar entre diferents motius de dibuix.

Tot seguit, volem detectar quan l’usuari toca l’aplicació, la qual cosa es fa fàcilment amb el mètode onTouchEvent() que es mostra a continuació. El que fa és mirar si es tracta d’un event del tipus tocar la pantalla (ACTION_DOWN) i aleshores prendre les coordenades on s’ha tocat:

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event) {
  3. int eventaction = event.getAction();
  4.  
  5. if (eventaction == MotionEvent.ACTION_DOWN) {
  6. x = (int) event.getX();
  7. y = (int) event.getY();
  8. invalidate();
  9. }
  10. return true;
  11. }

L’ordre invalidate() és molt important, perquè li diu al llenç que els seus continguts s’han de tornar a dibuixar; això provoca que es cridi el mètode onDraw(), que és on fareu els dibuixos.

El Canvas funciona com una mena de full de paper en blanc al qual s’hi poden afegir diferents tipus de figures; per exemple, amb drawRect() podeu dibuixar rectangles, amb drawCircle() cercles, etc. En general, es poden dibuixar formes senzilles, com ara òvals, punts, línies, o també text. Com és habitual, les coordenades del llenç comencen al cantó superior esquerre.

En aquest cas alternareu entre diferents motius senzills, dibuixats a les coordenades x i y en les que hagi tocat l’usuari, i amb l’atribut cont controlareu quin motiu toca dibuixar cada cop. Recordeu que el pinzell pintar és el que us permet triar el color. La resta d’operacions de dibuix queden prou clares amb el seu nom:

  1. @Override
  2. public void onDraw(Canvas canvas) {
  3. switch (cont) {
  4. case 0:
  5. pintar.setColor(Color.CYAN);
  6. canvas.drawRect(x - 20, y - 20, x + 20, y + 10, pintar);
  7. pintar.setColor(Color.YELLOW);
  8. canvas.drawRect(x - 20, y, x + 20, y + 20, pintar);
  9. break;
  10.  
  11. case 1:
  12. pintar.setColor(Color.RED);
  13. canvas.drawCircle(x, y, 40, pintar);
  14. break;
  15.  
  16. case 2:
  17. pintar.setColor(Color.GREEN);
  18. pintar.setTextSize(40);
  19. canvas.drawText("HOLA", x, y, pintar);
  20. break;
  21.  
  22. case 3:
  23. pintar.setColor(Color.MAGENTA);
  24. canvas.drawOval(new RectF(x - 40, y - 20, x + 40, y + 20), pintar);
  25. break;
  26. }
  27. cont = (cont + 1) % 4;
  28. }

A la figura podeu veure l’aplicació en funcionament:

Figura Aplicació que dibuixa amb un llenç

Amb aquestes tècniques podeu enriquir la vostra aplicació afegint botons per triar les eines de dibuix, les mides i els colors, i fent que s’emmagatzemin els elements dibuixats perquè es visualitzin tots a la pantalla, no només l’últim on hem tocat.

Anar a la pàgina anterior:
Referències
Anar a la pàgina següent:
Activitats