Ventanas/Activities en android

Una vez hecho nuestro Hola mundo en Android vamos a jugar con él para complicarlo un poco y aprender algo sobre como hacer las ventanas de nuestra aplicación Android.

Para nuestra aplicación de ejemplo, vamos a hacer dos ventanas. Una es la del "Hola Mundo" que tiene una caja de texto y le pondremos un botón. Al pulsar el botón, abriremos una segunda ventana con nueve botones estilo calculadora, numerados del 1 al 9. Cuando pulsemos cualquiera de los botones, volveremos a la ventana inicial y en su caja de texto pondremos el valor del botón pulsado.

Todo esto nos servirá para ver cómo poner dos ventanas en nuestra aplicación android, ver cómo cambiar de una ventana a otra y cómo recoger resultados de la segunda ventana.

Activities

Normalmente nuestra aplicación Android estará compuesta por una o más pantallas o ventanas, que Android llama Activities. Debemos componer cada una de estas Activities y luego hacer la navegación entre ellas, aparte de implementar la funcionalidad que necesiten.


Diseño de Activities

Las Activities se suelen definir en ficheros xml colocados en el directorio res/layout. Como queremos dos ventanas, necesitamos dos ficheros xml, que llamaremos main.xml y calculadora.xml. El contenido de main.xml será

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    
    <Button
        android:id="@+id/buton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Pulsame">
        
    </Button>
</LinearLayout>

Es similar al del Hola Mundo. Un LinearLayout vertical que pondrá nuestra caja de texto y un botón uno encima de otro. El botón tiene de etiqueta "Pulsame" y le hemos puesto un identificador android:id="@+id/buton" para poder acceder a él desde código. @+id indica a Android que esto es un identificador que debe añadir y que se llamará "buton".

El contenido de calculadora.xml es

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:columnCount="3" >

    <Button
        android:onClick="pulsadoBoton"
        android:text="1" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="2" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="3" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="4" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="5" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="6" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="7" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="8" />

    <Button
        android:onClick="pulsadoBoton"
        android:text="9" />

</GridLayout>

Es un GridLayout que coloca los componentes en forma de matriz todos del mismo tamaño. Dentro hemos metido 9 botones con textos del 1 al 9. Con android:onClick="pulsadoBoton" estamos indicando que cuando se pulsen se debe llamar al método pulsadoBoton() que debemos definir en la clase correspondiente a esta Activity (la vemos más abajo)

Declarando las Activities

En el fichero AndroidManifest.xml debemos indicar a Android que tenemos estas dos Activities (ventanas) y las clases que corresponden a cada una de ellas. También indicamos con el <intent-filter> cual es la ventana principal de nuestra aplicación

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.chuidiang.ejemplos.android"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AndroidHolaMundoActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".CalculadoraActivity"
            android:label="calculadora">
            
        </activity>
    </application>

</manifest>

Salvo que hay una segunda Activity Calculadora, el resto es igual que en el Hola mundo en Android.


Las clases java

La clase java para main.xml es AndroidHolaMundoActivity.java.


Clase de la ventana principal

Su contenido es

package com.chuidiang.ejemplos.android;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class AndroidHolaMundoActivity extends Activity {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      Button boton = (Button) findViewById(R.id.buton);
      boton.setOnClickListener(new OnClickListener() {

         @Override
         public void onClick(View v) {
            Intent intent = new Intent(AndroidHolaMundoActivity.this,
                  CalculadoraActivity.class);
            startActivityForResult(intent, 11);

         }
      });
   }

   @Override
   public void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      TextView tv = (TextView) findViewById(R.id.textView);

      if ((resultCode == RESULT_OK) && (requestCode == 11)) {
         tv.setText(data.getExtras().getCharSequence("DATO"));
      } else {
         if (resultCode != RESULT_OK)
            tv.setText("No es ResultOK");
         else
            tv.setText("No es 11");
      }
   }
}

Veamos los detalles

Las dos primeras líneas super.onCreate() y setContentView() ya las conocemos del Hola mundo en Android.

Vamos a hacer que el botón haga algo. Para ello, primero tenemos que obtenerlo. La forma de hacerlo es llamando al método findViewById() y haciendo el "cast" a Button. Como en el fichero main.xml le pusimos un identificador "buton", se ha generado automáticamente una clase R con todos los identificadores, así que el id lo tenemos en R.id.buton.

Una vez que tenemos el botón, le añadimos un onClickListener por medio del método setOnClickListener(). En el método onClick() de onClickListener pondremos el código de lo que queramos que haga el botón. En este caso, abrir la segunda ventana calculadora.xml

Para abrir una ventana, hay que enviarle un mensaje indicándole que se abra y pasándole lo que queramos pasarle. Este mensaje en una clase Intent que debemos instanciar y en uno de los constructores, el que vamos a usar, admite dos parámetros

  • El contexto de nuestra aplicación. Como nuestra clase AndroidHolaMundoActivity hereda de la clase Activity implementa Context, podemos pasarle directamente el this de la ventana principal AndroidHolaMundoActivity.this
  • La clase de la Activity (ventana) que queremos abrir, en nuestro caso CalculadoraActivity.class

Para arrancar la ventana podemos llamar al método startActivity(Intent) o bien startActivityForResult(Intent,unNumeroCualquiera).

  • startActivity(Intent): Sirve para abrir la ventana sin esperar ningún resultado de ella.
  • startActivityForResult(Intent,unNumeroCualquiera): Si abrimos de esta forma, esperamos que esa ventana nos devuelva algún resultado (quizás algo que ha escrito el usuario). El unNumeroCualquiera que pasamos como segundo parámetro es un número que nos devolverá la segunda ventana de forma que podamos saber a qué petición corresponde. Por ejemplo, podemos abrir dos ventanas, una en espera de un nombre y la segunda en espera de un apellido. Al abrir las ventanas ponemos dos números distintos cualquiera, por ejemplo, un 1 para la ventana de nombre y un 2 para la ventana de apellido. Cuando una de esas ventanas se cierre, por ejemplo la de nombre, nos devolverá el dato "Felipe" y el número correspondiente 1, permitiéndonos identificar qué ese dato que estamos recibiendo es el nombre.

En nuestro ejemplo vamos a abrir la ventana calculadora y esperamos como dato que esta nos devuelva el número de la calculadora que se ha pulsado, así que llamamos a startActivityForResult(intent, 11) siendo el 11 un número cualquiera que nos ha dado la gana. En este ejemplo no tiene demasiado sentido puesto que no vamoa a abrir muchas ventanas a la vez y el único resultado que recibamos será el de esta ventana.

Finalmente, debemos implementar el método public void onActivityResult(int requestCode, int resultCode, Intent data) que es donde recibiremos los datos cuando la ventana calculadora se cierre. Lo vemos con detalle más adelante.


Clase de la ventana secundaria

La ventana secundaria que se asemeja a una calculadora, tiene el siguiente código asociado

package com.chuidiang.ejemplos.android;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class CalculadoraActivity extends Activity {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.calculadora);

   }

   public void pulsadoBoton(View v) {
      Intent data = new Intent();

      if (v instanceof Button)
         data.putExtra("DATO", ((Button) v).getText());
      else
         data.putExtra("DATO", "A saber qué has pulsado");

      setResult(RESULT_OK, data);
      finish();
   }
}

El método onCreate() ya lo conocemos del Hola mundo en Android y es el típico de cualquier ventana. El identificador usado para indicar el contenido es R.layout.calculadora, generado automáticmanete y que hace referencia a nuestro fichero res/layout/calculadora.xml

Puesto que en calculadora.xml pusimos que los botones llamaran a android:onClick="pulsadoBoton", hemos puesto a la clase un método pulsadoBoton(View v). En el parámetro recibiremos el botón que ha sido pulsado. La intención de este método es que coja el texto del botón y se lo pase a la ventana principal y cierre esta ventana secundaria. Veamos los detalles.

Los datos que recogemos y queremos enviar a la ventana principal debemos meterlos en un mensaje Intent, así que instanciamos uno.

Comprobamos que el View v que recibimos corresponde a un botón. En nuestro ejemplo a este método sólo lo llaman los botones, así que esta comprobación realmente no es necesaria. Es la parte del if (v instanceof Button).

Si es un botón, obtenemos el texto del botón con ((Button) v).getText() y lo metemos dentro del mensaje usando el método putExtra(). Este método admite dos parámetros, por un lado un String cualquiera que haga de clave para identificar el dato, en nuestro caso hemos puesto "DATO". El segundo parámetro es el dato en si mismo ((Button) v).getText(). De esta forma, usando varias claves y datos, podríamos pasar a la ventana que nos ha llamado varios datos a la vez. En este caso sólo paseremos uno que es el texto del botón.

Con el método setResult() indicamos cuales son los resultados de esta ventana, es decir, nuestro mensaje Intent data. El método admite un primer parámetro con el que indicaremos a quien reciba el resultado si la operación se ha realizado correctamente (RESULT_OK) o se ha cancelado (RESULT_CANCELED), de forma que el receptor sepa si hay un dato válido dentro del mensaje o no.

Finalmente, cerramos la ventana con finish(). Esto provocará que el mensaje de resultados se pase a la ventana que nos ha abierto a través de su método onActivityResult().

Tratar los resultados de la ventana hija

Volvemos a la clase principal y veamos el contenido del método onActivityResult() que es donde recibimos los resultados de la ventana hija.

   @Override
   public void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      TextView tv = (TextView) findViewById(R.id.textView);

      if ((resultCode == RESULT_OK) && (requestCode == 11)) {
         tv.setText(data.getExtras().getCharSequence("DATO"));
      } else {
         if (resultCode != RESULT_OK)
            tv.setText("No es ResultOK");
         else
            tv.setText("No es 11");
      }
   }

La intención de este método es recoger el texto del botón pulsado (y que está guardado en el data que recibimos) y ponerlo en la caja de texto de esta ventana.

Llamamos al método super.onActivityResult() por costumbre, igual no es necesario. Luego con el método findViewById() obtenemos la caja de texto, de igual forma que hicimos anteriormente con el botón.

Comprobamos que el resultCode es RESULT_OK, lo que quiere decir que la ventana secundaria ha llevado a cabo correctamente su operación. Y comprobamos que el requestCode es el 11 que pasamos en su momento cuando abrimos la ventana secundaria, asegurándonos así que el resultado procede de esa ventana. En este ejemplo no tiene mucho sentido esta comprobación puesto que no hay más ventanas secundarias abiertas.

Si todo es como esperamos, recuperamos el texto del botón con data.getExtras().getCharSequence("DATO") donde "DATO" es la clave que usó la ventana secundaria para guardar el texto del botón. El método setText() de la caja de texto hace que se visualice en ella este texto concreto.

Si no es RESULT_OK o 11, simplemente mostramos en la caja de texto un aviso de esta circunstancia, más que nada para que podamos ver cual es el problema.

Vamos ahora con los Fragments en Android