Activitats

Enllaçant activitats mitjançant 'intents'

L’objectiu d’aquesta activitat és crear dues activitats i poder comunicar-les entre si mitjançant l’enviament d’intents.

Les aplicacions d’Android poden incloure una o més activitats. Quan una aplicació té més d’una activitat l’usuari ha de poder navegar d’una activitat a l’altra; aquesta navegació es realitza a través d’intents.

Podeu descarregar a continuació el codi corresponent a aquesta activitat:

03_intents1.zip ( 336.2 KB )

Creeu un projecte nou que tingui una única activitat i un únic layout.

El següent pas serà crear més activitats. Per fer-ho haureu de crear una nova classe que derivi d’ Activity i definir-la en el fitxer AndroidManifest.xml del projecte. Podeu fer aquesta tasca editant directament els fitxers de text necessaris o podeu fer servir l’entorn gràfic d’Android Studio per fer-ho.

Si fem clic amb el botó dret sobre app podem accedir al menú: New/Activity/Blank Activity. Creeu-ne una de nova amb el nom d’“Activitat2”.

Una vegada creada, si mirem el fitxer manifest.xml, veurem que s’ha afegit una nova secció <activity> de nom “Activitat2”.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="cat.xtec.ioc.intents" >
  4.  
  5. <application
  6. android:allowBackup="true"
  7. android:icon="@drawable/ic_launcher"
  8. android:label="@string/app_name"
  9. android:theme="@style/AppTheme" >
  10. <activity
  11. android:name=".MainActivity"
  12. android:label="@string/app_name" >
  13. <intent-filter>
  14. <action android:name="android.intent.action.MAIN" />
  15.  
  16. <category android:name="android.intent.category.LAUNCHER" />
  17. </intent-filter>
  18. </activity>
  19.  
  20. <activity
  21. android:name=".Activitat2"
  22. android:label="@string/title_activity_activitat2" >
  23. </activity>
  24.  
  25. </application>
  26. </manifest>

En la figura següent podeu veure ressaltat el bloc de codi s’ha afegit:

Figura

A continuació crearem un filtre dels intents que “escoltarà” l’activitat. Per fer-ho, editarem el fitxer manifest i afegirem a l’activitat corresponent l’intent-filter:

  1. <intent-filter>
  2. <action android:name="cat.xtec.ioc.intents.ACTIVITAT2"/>
  3. <category android:name="android.intent.category.DEFAULT"/>
  4. </intent-filter>

Aquest filtre consta de dos camps: action i category. Tots els intent-filter han de contenir un o més camps action, dels quals podem definir la categoria a través del camp category. En aquest exemple, l’acció ens diu que aquesta activitat s’iniciarà quan es llenci un intent cap a “cat.xtec.ioc.intents.ACTIVITAT2”. El fet d’afegir-lo a la categoria DEFAULT fa que pugui ser cridat amb startActivity() o startActivityForResult().

Cal que feu servir identificadors únics, per exemple identificadors de domini, per reduir les probabilitats que una altra activitat de l’aplicació d’un altre fabricant pugui tenir el mateix nom.

Com a resultat d’afegir l’intent-filter, el manifest quedarà de la següent manera:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="cat.xtec.ioc.intents" >
  4.  
  5. <application
  6. android:allowBackup="true"
  7. android:icon="@drawable/ic_launcher"
  8. android:label="@string/app_name"
  9. android:theme="@style/AppTheme" >
  10. <activity
  11. android:name=".MainActivity"
  12. android:label="@string/app_name" >
  13. <intent-filter>
  14. <action android:name="android.intent.action.MAIN" />
  15.  
  16. <category android:name="android.intent.category.LAUNCHER" />
  17. </intent-filter>
  18. </activity>
  19.  
  20. <activity
  21. android:name=".Activitat2"
  22. android:label="@string/title_activity_activitat2" >
  23. <intent-filter>
  24. <action android:name="cat.xtec.ioc.intents.ACTIVITAT2"/>
  25. <category android:name="android.intent.category.DEFAULT"/>
  26. </intent-filter>
  27. </activity>
  28.  
  29. </application>
  30.  
  31. </manifest>

Vegeu ressaltat en la figura següent la part del codi que hem afegit, en blau:

Figura

Finalment, al codi de la nostra activitat principal escriurem el codi necessari per llançar la segona activitat, en aquest cas en el moment que l’usuari premi la tecla d’augmentar el volum del seu telèfon (a l’emulador és la tecla ”+” del teclat numèric”). Quan l’usuari premi aquesta tecla (definida per la constant KeyEvent.KEYCODE_VOLUME_UP), feu servir el mètode startActivity() per mostrar l’Activitat2. A aquest mètode li passem una instància de la classe Intent amb el nom del filtre de l’Activitat2 (que havíem definit com a cat.xtec.ioc.intents.ACTIVITAT2). El codi de l’activitat principal (“MainActivity”) és el següent:

  1. package cat.xtec.ioc.intents;
  2.  
  3. import android.content.Intent;
  4. import android.support.v7.app.ActionBarActivity;
  5. import android.os.Bundle;
  6. import android.view.KeyEvent;
  7.  
  8. public class MainActivity extends ActionBarActivity {
  9.  
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState) {
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.activity_main);
  14. }
  15. public boolean onKeyDown(int keyCode, KeyEvent event) {
  16. if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
  17. startActivity(new Intent("cat.xtec.ioc.intents.ACTIVITAT2"));
  18. }
  19. return false;
  20. }
  21. }

L’string que es passa al constructor d‘Intent (cat.xtec.ioc.intents.ACTIVITAT2) és el nom de l’intent-filter que volem activar tal com està definit al fitxer AndroidManifest.xml.

Executeu el projecte i observeu com, quan es prem la tecla d’incrementar volum des de l’activitat principal, aquesta canvia per l’“Activitat2”, tal com es pot veure a la figura.

Figura

Les activitats a Android es poden invocar des de qualsevol aplicació del dispositiu. Per exemple, podeu crear un nou projecte des del qual mostrar l’activitat “Activitat2” fent servir el filtre d’intent cat.xtec.ioc.intents.ACTIVITAT2. Si l’activitat que voleu invocar està definida dins del mateix projecte, podeu invocar-la modificant la línia del codi que crida l’activitat:

  1. startActivity(new Intent(this, Activitat2.class));

Tingueu en compte, però, que això únicament es pot fer quan invoqueu una activitat des del mateix projecte.

Podeu fer la prova senzilla d’afegir més activitats al projecte i saltar d’unes activitats a altres amb els esdeveniments de teclat.

Filtres d’intent repetits

En cas que definiu, per error, més d’una activitat amb el mateix filtre d’intent (per exemple, si copieu el codi de l’activitat a l’AndroidManifest.xml), quan es llanci un intent el sistema no sabrà quina és l’activitat que ha de llançar i preguntarà mitjançant un diàleg com el que es veu a la següent figura amb quina activitat es vol completar l’acció. Això succeeix quan s’ha enviat un Intent i existeix més d’una activitat que el pot escoltar (l’intent ha passat la prova dels seus intent filters).

Per tal de comprovar-ho, podeu crear una nova activitat (Activitat3.java) i afegir al fitxer AndroidManifest.xml el mateix intent-filter que el de l’Activitat2.

Figura

Recordeu de disposar un filtre d’intent únic per cada activitat i així estalviar-vos aquesta situació.

Per exemple, si modifiqueu el codi de l’Activitat2 de tal manera que es pugui tornar a l’activitat principal prement el botó de baixar el volum, podríeu pensar en seguir el mateix procediment: crear un Intent amb el nom de l’activitat principal i que invoqui a startActivity().

Si copieu el següent codi a l’Activitat2:

  1. public boolean onKeyDown(int keyCode, KeyEvent event) {
  2. if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
  3. startActivity(new Intent("android.intent.action.MAIN"));
  4. }
  5. returnfalse;
  6. }

Obtindríeu quelcom com el que es veu a la figura.

Figura

Els filtres android.intent.action.MAIN i android.intent.category.LAUNCHER

El nom per defecte amb el qual es crea l’activitat a Android és android.intent.action.MAIN i aquest nom és present a totes les aplicacions. Els filtres android.intent.action.MAIN i android.intent.category.LAUNCHER són excepcions. Defineixen activitats que comencen noves tasques i que estan representades a la pantalla llançadora d’aplicacions (la pantalla on l’usuari pot escollir entre totes les aplicacions que vol llançar).

Si voleu poder tornar a l’activitat principal, podeu crear un intent filter nou amb el nom específic de l’activitat principal de la vostra aplicació (aquest nom serà únic per a tot el dispositiu). Afegiu el següent intent filter al fitxer de manifest dins de l’activitat principal:

  1. <action android:name="cat.xtec.ioc.intents.MAIN"/>
  2. <category android:name="android.intent.category.DEFAULT" />

I crideu l’activitat des de l’Activitat2 amb:

  1. startActivity(new Intent("cat.xtec.ioc.intents.MAIN"));

'Event listener': escoltar esdeveniments de 'widgets'

L’objectiu d’aquesta activitat és demostrar com definir un event listener i programar les accions que es realitzaran quan l’usuari premi un botó.

Volem crear una Activitat que tingui un layout amb quatre botons que responguin a la interacció de l’usuari, i per fer-ho haurem de definir els event listeners que ho faran possible (vegeu la figura). Volem que es mostri un missatge en la pantalla cada vegada que l’usuari premi un dels botons. Podem fer-ho de tres maneres diferents:

  • Definint un click listener per cada botó
  • Implementant la interfície OnClickListener
  • Fent servir a l’XML l’atribut onClick i implementant els mètodes
Figura

Vegem tot seguit aquests tres sistemes per definir els event listeners.

Sistema 1: definint un click listener per cada botó

En primer lloc, definirem el layout mitjançant el següent fitxer XML:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical" >
  6.  
  7. <Button
  8. android:id="@+id/boto1"
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:text="Botó 1" />
  12.  
  13. <Button
  14. android:id="@+id/boto2"
  15. android:layout_width="fill_parent"
  16. android:layout_height="wrap_content"
  17. android:text="Botó 2" />
  18.  
  19. <Button
  20. android:id="@+id/boto3"
  21. android:layout_width="fill_parent"
  22. android:layout_height="wrap_content"
  23. android:text="Botó 3" />
  24.  
  25. <Button
  26. android:id="@+id/boto4"
  27. android:layout_width="fill_parent"
  28. android:layout_height="wrap_content"
  29. android:text="Botó 4" />
  30.  
  31. </LinearLayout>

Cada botó té l’identificador boto1, boto2, etc… i els textos “Botó 1”, “Botó 2”, etc. Al codi de l’activitat, en primer lloc, us guardareu una referència al View sobre el qual voleu definir l’event listener. Per això podeu fer servir el mètode View findViewById(int id), al qual es passa l’identificador tal com està definit al fitxer AndroidManifest.xml. La variable R.id conté tots els identificadors de widgets que hem fet servir en la nostra activitat.

Una vegada tingueu la referència al botó, fareu una crida al mètode setOnClickListener (View.OnClickListener l). Aquest mètode registra el callback que serà invocat quan aquest View sigui clicat. Com a únic argument se li passa la funció de callback que s’executarà.

Heu d’afegir els imports:

  1. import android.view.View;
  2. import android.view.View.OnClickListener;

Aquest és el codi corresponent a un dels botons:

  1. //Listener del botó 1
  2. btn1 = (Button) findViewById(R.id.boto1);
  3.  
  4. btn1.setOnClickListener(
  5.  
  6. new OnClickListener() {
  7. @Override
  8. public void onClick(View v) {
  9. MostraMissatge("Botó 1");
  10. }
  11. }
  12. );

L’acció que s’executa quan es fa clic sobre el botó és la crida del mètode MostraMissatge(String s) que hem definit per mostrar un missatge per pantalla:

  1. public void MostraMissatge(String s) {
  2. Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
  3. }

Repetiu l’operació per a la resta de botons i executeu l’aplicació. El codi quedaria com es mostra en la figura següent:

Figura

El resultat el podeu veure a la figura.

Figura

Sistema 2: Implementant la interfície OnClickListener

En aquest cas, es tractaria d’implementar la interfície dins de la mateixa activitat (en l’arxiu MainActivity.java del codi font d’aquesta activitat hi podeu trobar els dos sistemes; al codi corresponent al primer sistema se li ha donat format de comentari). Per fer-ho, assegureu-vos que la vostra activitat implementa la interfície OnClickListener; això cal que ho definiu a la capçalera de la classe amb:

  1. public class MainActivity extends ActionBarActivity implements OnClickListener {

A continuació cal que definiu els listeners de cada botó, passant la pròpia activitat com a argument.

  1. //Definim els listeners
  2. btn1 = (Button)findViewById(R.id.boto1);
  3. btn1.setOnClickListener(this);
  4. btn2 = (Button)findViewById(R.id.boto2);
  5. btn2.setOnClickListener(this);
  6. btn3 = (Button)findViewById(R.id.boto3);
  7. btn3.setOnClickListener(this);
  8. btn4 = (Button)findViewById(R.id.boto4);
  9. btn4.setOnClickListener(this);

Com que la interfície queda definida per tots els botons amb la pròpia activitat, qualsevol botó que es premi farà que es cridi el mètode de callback onClick() de l’activitat. Per aquest motiu, el primer que fareu dins del callback serà mirar quin botó ha sigut el que n’ha originat la crida. Una vegada sabeu quin ha sigut, podeu executar el codi corresponent.

  1. public void onClick(View v)
  2. {
  3. // Fer alguna cosa quan s'ha polsat un View.
  4.  
  5. //Comprovem quin ha estat el View clicat i mostrem el missatge per pantalla
  6. if(v == btn1) MostraMissatge("Botó 1");
  7. else if(v == btn2) MostraMissatge("Botó 2");
  8. else if(v == btn3) MostraMissatge("Botó 3");
  9. else if(v == btn4) MostraMissatge("Botó 4");
  10. }

Vegeu en la figura següent com quedaria el codi:

Figura

Sistema 3: Fent servir a l’XML l’atribut onClick i implementant els mètodes

La darrera manera de realitzar l’activitat implica editar els continguts dels fitxers XML i java. Des de l’XML indicarem quin mètode s’ha d’executar quan fem clic a cada botó i aquest haurà d’existir al codi de l’activitat.

A la part de l’XML afegirem als botons l’atribut onClick, i per mostrar el procediment afegirem solament el codi referent al “botó 1”:

  1. <Button
  2. android:id="@+id/boto1"
  3. android:layout_width="fill_parent"
  4. android:layout_height="wrap_content"
  5. android:text="Botó 1"
  6. android:onClick="metodeBoto1"
  7. />

Amb la darrera línia estem indicant que quan es faci clic en aquest view, es llançarà el mètode metodeBoto1. Haurem de crear aquest mètode a l’activitat tenint en compte que ha de ser públic i ha de rebre com a únic paràmetre el view.

Així, el codi del nostre mètode serà el següent:

  1. public void metodeBoto1(View v) {
  2.  
  3. MostraMissatge("Botó 1");
  4.  
  5. }

Per obtenir la funcionalitat a tots els botons haurem de repetir el procediment per cadascun d’ells.

Obtenir resultats de les activitats

L’objectiu d’aquesta activitat és activar una activitat que ens retorni dades a l’activitat principal.

Podeu descarregar a continuació el codi corresponent a aquest programa:

El mètode startActivity(Intent) serveix per començar una nova activitat que es trobarà al capdamunt de la pila d’activitats (es mostrarà i tindrà el focus). Aquest pren un únic argument, un Intent, que descriu l’activitat que es vol executar.

Però hi haurà ocasions en les quals voldreu obtenir un resultat de tornada de l’activitat quan aquesta acabi. Per exemple, podeu llançar una activitat per demanar el nom d’usuari i contrasenya, i voleu aquests valors quan torneu a l’activitat original. Per retornar valors de la crida a l’activitat heu de fer servir la versió del mètode startActivityForResult(Intent intent, int requestCode); aquesta té un segon argument de tipus enter que serveix per identificar la crida a l’activitat, ja que podríeu cridar diferents activitats al mateix temps i algunes d’elles poden no retornar el resultat immediatament. Quan es retorna un valor, es requereix d’aquest valor enter per saber a quina activitat correspon el valor retornat.

Creeu un projecte que tingui dues activitats. Cal que definiu a l’activitat principal una constant numèrica que servirà per identificar l’activitat, així com la crida a l’activitat, al fer clic en un botó, tal com podeu veure en el següent codi:

  1. static final int ACTIVITAT_NOM = 0;
  2.  
  3. ...
  4.  
  5. //Demana un nom
  6. btn1 = (Button) findViewById(R.id.btn_act2);
  7.  
  8. btn1.setOnClickListener(new View.OnClickListener() {
  9. @Override
  10. public void onClick(View v) {
  11. startActivityForResult(new Intent(getApplicationContext(), Activitat2.class),ACTIVITAT_NOM);
  12. }
  13. });

La interfície de l’activitat principal podria ser:

  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" tools:context=".MainActivity">
  7.  
  8.  
  9. <Button
  10. android:layout_width="match_parent"
  11. android:layout_height="wrap_content"
  12. android:text="Activitat 2"
  13. android:id="@+id/btn_act2"
  14. android:layout_alignParentTop="true"
  15. android:layout_alignParentLeft="true"
  16. android:layout_alignParentStart="true" />
  17. </RelativeLayout>

L’activitat 2 conté una caixa de text i un botó per obtenir el nom de l’usuari. Aquest és el contingut del fitxer XML que defineix el layout de l’activitat 2.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical" >
  6.  
  7. <TextView
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:text="Introduïu el vostre nom"
  11. />
  12.  
  13. <EditText
  14. android:id="@+id/txt:username"
  15. android:layout_width="fill_parent"
  16. android:layout_height="wrap_content"
  17. />
  18.  
  19. <Button
  20. android:id="@+id/btn_OK"
  21. android:layout_width="fill_parent"
  22. android:layout_height="wrap_content"
  23. android:text="OK"
  24. />
  25.  
  26. </LinearLayout>

Podeu veure el resultat gràfic a la figura.

Figura

Volem que quan es premi el botó OK retorni el contingut de la caixa de text a l’activitat principal.

Afegim el següent codi a l’activitat 2:

  1. //obtenir botó OK
  2. Button btn = (Button) findViewById(R.id.btn_OK);
  3.  
  4. //handler d'esdeveniment pel botó OK
  5. btn.setOnClickListener(new View.OnClickListener() {
  6.  
  7. //@Override
  8. public void onClick(View v) {
  9. Intent data = new Intent();
  10.  
  11. //obtenir EditText
  12. EditText txt_username = (EditText) findViewById(R.id.txt_username);
  13.  
  14. //obtenir text del EditText
  15. String s = new String();
  16. s = txt_username.getText().toString();
  17.  
  18. //Preparar les dades en el Intent
  19. data.setData(Uri.parse(s));
  20.  
  21. //retornar dades
  22. setResult(RESULT_OK, data);
  23.  
  24. //tanca l'activitat
  25. finish();
  26. }
  27. });

Per retornar un valor a l’activitat que s’ha cridat heu de fer servir un objecte intent que enviarà les dades mitjançant el mètode setResult(int resultCode, Intent data).

En primer lloc, obtenim un objecte del tipus botó amb el mètode findViewById (int id), que ens retorna un View identificat per l’argument id del fitxer XML que s’ha processat a onCreate(). Tots aquests id pertanyen a la variable R.id.

Amb el mètode getText() de l’objecte EditText obtenim el text que ha introduït l’usuari a la caixa de text, i amb aquest text construïm les dades que retornarem com a resultat de l’activitat mitjançant el mètode setData (Uri data). A setData() se li passa com a argument un URI. El mètode Uri.parse(String) serveix per analitzar i codificar un string en forma d’URI.

Una vegada tenim el resultat dins d’un intent, el retornem amb setResult (int resultCode, Intent data). Aquest mètode retorna dos arguments a l’activitat:

  • El codi de resultat, que serà RESULT_OK, RESULT_CANCELLED o altres valors definits per l’usuari (aquests valors definits per l’usuari han de tenir un valor superior a la constant RESULT_FIRST_USER, que té un valor d’1).
  • Les dades que es passaran a l’activitat pare que invoca aquesta activitat (un objecte intent).

Si l’execució de l’activitat falla per qualsevol motiu (per exemple, perquè es penja), l’activitat pare rebrà el codi de resultat RESULT_CANCELED.

En l’activitat pare heu d’implementar el mètode onActivityResult(int requestCode, int resultCode, Intent data). Es tracta d’una funció de callback que es crida cada vegada que una activitat retorna un valor. Té tres arguments:

  • int requestCode: el codi que es va fer servir originalment per a la crida a startActivityForResult() i que permet identificar a quina crida a activitats pertany aquest resultat.
  • int resultCode: el codi que retorna el mètode setResult().
  • Intent data: un objecte Intent, especificat pel mètode setResult(). Retorna les dades de l’activitat que acaba de finalitzar.
  1. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  2. //Comprovem a quina activitat correspon
  3. if(requestCode == ACTIVITAT_NOM) {
  4. //Si l'activitat ha acabat correctament
  5. if(resultCode == RESULT_OK) {
  6. //Mostrem el text retornat per l'activitat per pantalla
  7. Toast.makeText(this, data.getData().toString(), Toast.LENGTH_LONG).show();
  8. }
  9. }
  10. }

Dins d‘onActivityResult mirem a quina activitat correspon la resposta i si aquesta ha estat correcta. Amb el text que obtenim de l‘Intent data, mostrem un petit text per pantalla amb Toast.makeText(Context context, CharSequence text, int duracio).

El primer argument serveix per definir el context en què es mostrarà el missatge (generalment serà l’aplicació o l’activitat). Els dos següents són el text que es mostrarà i una constant (LENGTH_LONG o LENGTH_SHORT) que indica durant quant de temps es mostrarà el missatge. El resultat el podeu veure a la figura.

Figura

Passar dades a les activitats

En alguns casos, a més de voler tractar amb les dades que ens retorna l’activitat, també volem passar-li dades des de l’activitat que la invoca. L’objectiu d’aquesta activitat és passar dades a una activitat que estem invocant, i que aquesta ens retorni altres dades de retorn.

Podeu descarregar a continuació el codi corresponent a aquesta activitat:

06_passadades.zip ( 285.6 KB )

La següent aplicació té una pantalla on es demana un nom d’usuari (des d’un desplegable o spinner) i una contrasenya, que es passaran a una altra activitat. Aquesta segona activitat comprovarà el valor del nom i la contrasenya.

Creeu una sèrie de constants d’string al fitxer strings.xml que es troba a la carpeta /res/values del projecte. Per exemple, es poden crear constants per al prompt de l’spinner (la frase que es veu abans de desplegar-lo) i els noms i contrasenyes d’usuari. Aquest seria el contingut d’strings.xml.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3.  
  4. <string name="app_name">PassaDades</string>
  5. <string name="title_activity_login">Login</string>
  6. <string name="nom_prompt">Selecciona un nom</string>
  7.  
  8. <string-array name="noms_array">
  9. <item>Vicenç</item>
  10. <item>Marc</item>
  11. <item>Oswaldo</item>
  12. </string-array>
  13.  
  14. <string-array name="contrasenyes_array">
  15. <item>12345</item>
  16. <item>67890</item>
  17. <item>11111</item>
  18. </string-array>
  19.  
  20. </resources>

El layout de l’activitat principal conté un spinner, una caixa per a la contrasenya i un botó per enviar les dades, tal com es pot veure a la figura.

Figura

El valor del fitxer main.xml que defineix el layout és el següent:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical">
  6.  
  7. <TextView
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:text="Nom"/>
  11.  
  12. <Spinner
  13. android:id="@+id/spinnerNom"
  14. android:layout_width="fill_parent"
  15. android:layout_height="wrap_content"
  16. android:prompt="@string/nom_prompt"
  17. />
  18.  
  19. <TextView
  20. android:id="@+id/textView1"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:text="Contrasenya"/>
  24.  
  25. <EditText
  26. android:id="@+id/editContrasenya"
  27. android:layout_width="fill_parent"
  28. android:layout_height="wrap_content"
  29. android:ems="10"
  30. android:inputType="textPassword">
  31.  
  32. <requestFocus/>
  33. </EditText>
  34.  
  35. <Button
  36. android:id="@+id/buttonEnviar"
  37. android:layout_width="fill_parent"
  38. android:layout_height="wrap_content"
  39. android:text="Enviar"/>
  40. </LinearLayout>

Al callback onCreate() de l’activitat carregueu l’spinner amb les dades de la llista d’strings. Els ítems de l’spinner estan continguts en un adapter. Per introduir els strings dels diferents noms a l’spinner cal fer servir el mètode setAdapter(T adapter). L’adapter el podeu obtenir a partir del recurs string.xml amb el mètode createFromResource(Context context, int textArrayResId, int textViewResId) que crea un nou ArrayAdapter, que s’enllaçarà amb cada ítem de l’spinner. Podeu carregar l’spinner de dades amb el següent codi:

  1. //Omplim les dades de l'spinner
  2. Spinner spinner = (Spinner) findViewById(R.id.spinnerNom);
  3.  
  4. ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
  5. this, R.array.noms_array, android.R.layout.simple_spinner_item);
  6.  
  7. adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  8. spinner.setAdapter(adapter);

Podeu fer que l’activitat principal implementi la interfície OnClickListener i, per tant, únicament necessitareu redefinir el mètode onClick(View v). Per cridar una activitat i passar-li informació extra cal que feu servir el mètode startActivity(Intent intent), però en aquesta ocasió li podeu afegir a l’intent informació extra que serà extreta de l’activitat que estem invocant. Per afegir informació extra existeixen una sèrie de mètodes d’Intent putExtras() sobrecarregats amb diferents arguments. Reviseu la documentació d’intent per veure la definició de tots els mètodes en el següent enllaç de la Guia del desenvolupador d’Android: http://developer.android.com/reference/android/content/Intent.html#putExtras(android.os.Bundle)

La versió que fareu servir en aquest codi és: putExtras (Bundle extras), que té com a únic argument un objecte de la classe Bundle. Un Bundle (literalment de l’anglès, paquet o feix) és una classe que permet emmagatzemar una sèrie de dades (que poden ser de diferent tipus) dins d’un objecte Bundle. Les variables es poden afegir al Bundle simplement donant un nom i la variable que es vol guardar. En certa manera, recorda els arrays amb què es passen variables entre els formularis en PHP: un array de tuples [nom, valor].

Així, el que fareu és crear un Bundle, omplir-lo amb una sèrie de variables i després afegir-lo com a informació extra a l’intent, amb el mètodeputExtras (Bundle extras). Aquest mètode us permet afegir informació a l’intent per enviar-ho a l’activitat que esteu invocant.

El codi del mètode onClick() de l’activitat principal és el següent:

  1. public void onClick(View v) {
  2. //Si el botó és enviar
  3. if(v==btnEnviar) {
  4. //////////
  5. //Intent//
  6. //////////
  7. //Omplim un "intent" amb les dades a passar a l'altre activitat
  8. Intent i = new Intent(this, Login.class);
  9.  
  10. //Creem un "bundle" per afegir dades extra a l'"intent"
  11. Bundle extras = new Bundle();
  12.  
  13. //Afegim el nom
  14. String stNom = spinner.getSelectedItem().toString();
  15. extras.putString("Nom", stNom);
  16.  
  17. //Afegim la contrasenya
  18. EditText txtContrasenya = (EditText) findViewById(R.id.editContrasenya);
  19. String stContrasenya = txtContrasenya.getText().toString();
  20. extras.putString("Contrasenya", stContrasenya);
  21.  
  22. //Afegim el "bundle" amb informació extra a l'"intent"
  23. i.putExtras(extras);
  24.  
  25. //Cridem l'activitat
  26. startActivity(i);
  27.  
  28. }
  29. }

La frase “té un disseny amb uns widgets” no és correcta gramaticalment. Potser es vol dir “El disseny de la segona activitat conté uns widgets que modificarem”?

La segona activitat té un disseny amb uns widgets que modificarem depenent dels valors del Bundle que està dins de l’intent que rep l’activitat. El layout és el següent:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical">
  6.  
  7. <LinearLayout
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content">
  10.  
  11. <TextView
  12. android:id="@+id/textView1"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:text="Nom"
  16. android:textAppearance="?android:attr/textAppearanceLarge"/>
  17.  
  18. <EditText
  19. android:id="@+id/editNom"
  20. android:layout_width="wrap_content"
  21. android:layout_height="wrap_content"
  22. android:layout_weight="1"
  23. android:ems="10">
  24.  
  25. <requestFocus/>
  26. </EditText>
  27.  
  28. </LinearLayout>
  29.  
  30. <LinearLayout
  31. android:layout_width="fill_parent"
  32. android:layout_height="wrap_content">
  33.  
  34. <TextView
  35. android:id="@+id/textView2"
  36. android:layout_width="wrap_content"
  37. android:layout_height="wrap_content"
  38. android:text="Contrasenya correcta"
  39. android:textAppearance="?android:attr/textAppearanceLarge"/>
  40.  
  41. <CheckBox
  42. android:id="@+id/checkBoxOK"
  43. android:layout_width="wrap_content"
  44. android:layout_height="wrap_content"
  45. android:text=""/>
  46.  
  47. </LinearLayout>
  48. </LinearLayout>

Al codi de l’activitat cal que fem dues coses. En primer lloc, obtindrem els textos que estan dins del Bundle que s’ha passat juntament amb l’intent de la crida de l’activitat. En segon lloc, comprovarem si la contrasenya que està guardada a l’array d’strings correspon a la contrasenya que ha introduït l’usuari. Finalment, modificarem els widgets en funció de les dades que rep de l’intent (s’introdueix el nom de l’usuari a la caixa de text i es marca el checkbox si la contrasenya és correcta).

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_login);
  5.  
  6. String stNom;
  7. String stContrasenya;
  8.  
  9. //Obtenim el "bundle" amb la informació extra de l'"intent"
  10. Bundle extras = getIntent().getExtras();
  11.  
  12. //si existeix informació extra a l'"intent"...
  13. if (extras != null) {
  14. //...ens guardem les cadenes
  15. stNom = extras.getString("Nom");
  16. stContrasenya = extras.getString("Contrasenya");
  17.  
  18. //Obtenim la referència a l'etiqueta
  19. TextView txNom = (TextView) findViewById(R.id.editNom);
  20. //Posem el nom de l'usuari a l'etiqueta
  21. txNom.setText(stNom);
  22.  
  23. //Comprovem el nom que envien a l'"intent" a quina posició de l'"array" d'"strings" és
  24. //Obtenim l'"array"
  25. ArrayAdapter<CharSequence> adapterNoms = ArrayAdapter.createFromResource(
  26. this, R.array.noms_array, android.R.layout.simple_spinner_item);
  27.  
  28. int i, iCount;
  29. String stNomLlista;
  30.  
  31. i = 0;
  32. iCount = adapterNoms.getCount();
  33.  
  34. //Fem una cerca a l'"array" de noms
  35. while (i < iCount) {
  36. //Obtenim el nom de la posició de l'"array"
  37. stNomLlista = adapterNoms.getItem(i).toString();
  38.  
  39. //el nom de l'"array" és el mateix que l'enviat per l'"intent"?
  40. if (stNomLlista.compareTo(stNom) == 0) {
  41. //Mirem si la posició de les contrasenyes d'aquesta posició coincideix amb la que s'ha enviat
  42. ArrayAdapter<CharSequence> adapterContrasenyes = ArrayAdapter.createFromResource(
  43. this, R.array.contrasenyes_array, android.R.layout.simple_spinner_item);
  44.  
  45. //obtenim la contrasenya de l'"array"
  46. String stContrasenyaLista = adapterContrasenyes.getItem(i).toString();
  47.  
  48. //la contrasenya de l'"array" és igual que la que s'ha enviat per l'"intent"?
  49. if (stContrasenyaLista.compareTo(stContrasenya) == 0) {
  50. //Posem el "CheckBox" de contrasenya OK marcat
  51. CheckBox chOK = (CheckBox) findViewById(R.id.checkBoxOK);
  52. chOK.setChecked(true);
  53. }
  54. }
  55. i++;
  56. }
  57.  
  58. }
  59. }

El resultat final és el que podeu veure a la figura.

Figura

Cridant aplicacions del sistema

L’objectiu d’aquesta activitat és crear una aplicació que cridi aplicacions del sistema externes a la nostra aplicació.

Podeu descarregar a continuació el codi corresponent a aquesta activitat:

07_appsistema.zip ( 224.7 KB )

Mitjançant la crida d’activitats i els intents podeu navegar dins de les diferents activitats de la vostra aplicació. Un dels aspectes clau de la programació en Android és que des de la nostra aplicació podem invocar activitats d’altres aplicacions. Això inclou també les aplicacions que estan instal·lades per defecte al sistema Android. Per exemple, el sistema ja inclou un navegador web, així que, si voleu navegar un web des d’una pàgina de la vostra aplicació, no caldrà que programeu la vostra pròpia activitat de navegador web: en teniu prou amb cridar l’activitat del sistema. O, per exemple, si voleu obtenir la informació d’un usuari de l’agenda del dispositiu, podeu simplement obtenir-la a través de l’aplicació integrada de contactes, sense haver de programar-la vosaltres de nou.

En aquesta activitat farem servir les API de Google per al GoogleMaps, per tant l’haurem d’executar en una AVD (un dispositiu virtual d’Android, Android Virtual Device) que suporti les API de Google. Per fer-ho, executeu l’AVD Manager (Tools/Android/AVD Manager). Una vegada a l’AVD Manager, creeu un nou AVD i assegureu-vos que el target té suport per a les API de Google, com es pot veure a la figura.

Figura

Una vegada heu creat l’AVD, el podeu executar manualment fent clic al botó Start de l’AVD Manager. Posteriorment podeu executar el projecte sobre aquest AVD amb normalitat.

Els intents a Android es poden crear amb dues parts: acció i dades. El constructor de la classe Intent està sobrecarregat, és a dir, té vàries formes. Una d’elles és Intent (String action, Uri uri); el significat d’aquests arguments és el següent:

  • String action: indica l’acció que es vol que es realitzi (com ara veure el contingut d’un ítem o editar-lo). Podeu trobar un llistat de les diferents accions que es poden portar a terme a android.content.Intent.ACTION_*. Per exemple, algunes de les accions que estan definides aquí són les següents:
    • ACTION_VIEW
    • ACTION_DIAL
    • ACTION_PICK
    • ACTION_CALL
    • ACTION_DELETE
    • ACTION_EDIT
    • ACTION_INSERT
    • ACTION_SEND
  • Uri uri: el segon argument el constitueixen les dades que especifiquen quin element resultarà afectat per l’acció. Per exemple, l’URL del web que voleu visitar, o la persona de la llista de contactes que voleu esborrar. Alguns exemples de dades podrien ser:
    • http://ioc.xtec.cat (URL de web a visitar)
    • tel:+34666123456 (número de telèfon per trucar)
    • geo:41.374812, 2.167997 (coordenades geogràfiques per als mapes)
    • content://contacts (llista de contactes).

Conjuntament, un intent descriu una acció i unes dades sobre les quals aquesta s’ha de realitzar. Per exemple, per trucar per telèfon a una persona faríeu servir el parell [ACTION_DIAL, tel:+666123456]. Això us permet diferenciar entre l’acció i les dades. Podeu fer diferents coses a la llista de contactes. Per veure-la, faríeu servir [ACTION_VIEW, content:contacts], i per escollir un contacte [ACTION_PICK, content:contacts].

Per llançar l’activitat, en primer lloc cal crear l’intent amb l’acció i les dades que voleu fer servir, i posteriorment cal començar l’activitat tal com es pot veure en el següent codi:

  1. Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("http://ioc.xtec.cat/educacio/"));
  2. startActivity(i);

En aquest cas hem fet servir la classe Uri per transformar una adreça web (URL) en un objecte Uri.

Creeu una aplicació on l’activitat principal tingui quatre botons, que serviran per llançar activitats per a:

  • Obrir una adreça web.
  • Obrir GoogleMaps.
  • Cercar una persona a la llista de contactes.
  • Marcar un número.

El fitxer xml que defineix el layout és el següent:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical">
  6.  
  7. <Button
  8. android:id="@+id/btnMapa"
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:text="Mapa"/>
  12.  
  13. <Button
  14. android:id="@+id/btnWeb"
  15. android:layout_width="fill_parent"
  16. android:layout_height="wrap_content"
  17. android:text="Web"/>
  18.  
  19. <Button
  20. android:id="@+id/btnContactes"
  21. android:layout_width="fill_parent"
  22. android:layout_height="wrap_content"
  23. android:text="Contactes"/>
  24.  
  25. <Button
  26. android:id="@+id/btnTrucar"
  27. android:layout_width="fill_parent"
  28. android:layout_height="wrap_content"
  29. android:text="Trucar"/>
  30.  
  31. </LinearLayout>

Al codi de l’activitat principal, l’únic que farà el programa és:

  • Crear els listeners de cada botó.
  • Crear l’intent necessari per invocar l’activitat que farà la tasca que volem.
  • Invocar l’activitat mitjançant startActivity() o startActivityForResult().
  • Implementar la funció de callback onActivityResult(), que agafarà el resultat de tornada de les activitats invocades amb startActivityForResult().

El codi de creació dels listeners és el següent:

  1. //Listener dels botons
  2. btnMapa = (Button) findViewById(R.id.btnMapa);
  3. btnMapa.setOnClickListener(this);
  4.  
  5. btnWeb = (Button) findViewById(R.id.btnWeb);
  6. btnWeb.setOnClickListener(this);
  7.  
  8. btnContactes = (Button) findViewById(R.id.btnContactes);
  9. btnContactes.setOnClickListener(this);
  10.  
  11. btnTrucar = (Button) findViewById(R.id.btnTrucar);
  12. btnTrucar.setOnClickListener(this);

I la crida de les activitats dins del mètode de callback onClick() és el següent:

  1. public void onClick(View v) {
  2. // TODO Auto-generated method stub
  3.  
  4. //Mapa
  5. if(v == btnMapa) {
  6. Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("geo:41.374812, 2.167997"));
  7. startActivity(i);
  8. }
  9. //Web
  10. elseif(v == btnWeb) {
  11. Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("http://ioc.xtec.cat/educacio/"));
  12. startActivity(i);
  13. }
  14. //Contactes
  15. elseif(v == btnContactes)
  16. {
  17. Intent i = new Intent(android.content.Intent.ACTION_PICK);
  18. i.setType(ContactsContract.Contacts.CONTENT_TYPE);
  19. startActivityForResult(i, CODI_CONTACTES);
  20.  
  21. }
  22. //Trucar
  23. elseif(v == btnTrucar) {
  24. Intent i = new Intent(android.content.Intent.ACTION_DIAL, Uri.parse("tel:+012"));
  25. startActivity(i);
  26. }
  27. }

Fixeu-vos en la creació dels intents per les crides a les activitats. Per obrir l’aplicació de mapes, el codi serà el següent:

  1. Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("geo:41.374812, 2.167997"));

Creem un intent amb l’acció de veure (ACTION_VIEW), i com a dades passem les coordenades geogràfiques del lloc on volem obrir el mapa del GoogleMaps.

Per obrir una web, el codi serà el següent:

  1. Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("http://ioc.xtec.cat/educacio/"));

Crearem l’intent amb l’acció de veure i l’URL de la web que volem obrir.

Per seleccionar un contacte de l’agenda de contactes, el codi serà el següent:

  1. Intent i = new Intent(android.content.Intent.ACTION_PICK);
  2.  
  3. i.setType(ContactsContract.Contacts.CONTENT_TYPE);
  4. startActivityForResult(i, CODI_CONTACTES);

Primer crearem l’intent amb l’acció de seleccionar, (ACTION_PICK). Després farem servir el mètode setType (String type), que especifica un tipus de dades MIME específic. Es fa servir per crear intents que únicament especifiquen un tipus de dades o dades en si mateix, per exemple per indicar quin tipus de dades s’han de retornar. Com a argument, el mètode rebrà un String que especifica el tipus de dades que volem especificar per a l’intent. En aquest cas li passem el tipus definit a Android ContactsContract.Contacts.CONTENT_TYPE, que és el tipus MIME que proporciona un directori de persones. Una vegada creat l’intent, cridem startActivityForResult(Intent, int) amb l‘Intent i el requestCode que fareu servir per rebre les dades quan l’activitat invocada les retorni. Si únicament voleu mostrar els contactes que tinguin assignat un número de telèfon, ho podeu fer canviant el tipus a:

  1. i.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);

Per trucar, creem l’intent de la següent manera:

  1. Intent i = new Intent(android.content.Intent.ACTION_DIAL, Uri.parse("tel:+012"));

Així, especifiquem l’acció de marcar i el número de telèfon que volem que es marqui. Però això únicament marcarà el número, no realitzarà la trucada. L’acció per realitzar la trucada és ACTION_CALL. Si la voleu fer servir caldrà que afegiu el permís android.permission.CALL_PHONE en l’aplicació, afegint aquesta línia al fitxer AndroidManifest.xml:

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

L’última cosa que quedaria pendent a l’aplicació seria recollir les dades de l’activitat que heu invocat per seleccionar un usuari de la llista de contactes, ja que l’heu invocat amb startActivityForResult().

  1. //Listener que agafa el valor de retorn de la crida als Activities
  2. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. //Si ha retornatde l'activity decontactes
  4. if(requestCode == CODI_CONTACTES) {
  5. //Si el resultat ha sigutpositiu
  6. if(resultCode == RESULT_OK) {
  7. //Mostrem l'URL delcontacte
  8. Toast.makeText(this, data.getData().toString(), Toast.LENGTH_LONG).show();
  9.  
  10. //Obrimunaaltraactivitat per veure el contacte
  11. Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(data.getData().toString()));
  12. startActivity(i);
  13. }
  14. }
  15. }

Quan seleccionem un contacte de la llista se’ns retorna un URL que conté el contacte, que pot tenir una forma com la següent:

content://com.android.contacts/contacts/loopup/0r1-9876543210/1

Aquesta URL no ens resulta gaire útil, i per tant el que farem és crear un altre Intent per visualitzar els detalls d’aquest contacte amb una altra activitat (és un intent implícit: obrirà una aplicació del dispositiu que pugui veure un contacte).

  1. //Obrim una altra activitat per veure el contacte
  2. Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(data.getData().toString()));
  3. startActivity(i);
Anar a la pàgina anterior:
Programació de dispositius mòbils
Anar a la pàgina següent:
Exercicis d'autoavaluació