Tip Android #32: usar «sub-activities» con startActivityForResult

01/11/2013
android tip

A raiz de un problema expuesto en los comentarios del tip #29, me he surgido la idea de elaborar este otro explicando cómo ejecutar una Activity desde otra con el único cometido de que la Acitivity creada retorne datos a la Activity que la crea. Como ejemplo se va a utilizar una Activity con un ListView cuyos elementos son introducidos desde otra Activity. Aunque para este ejemplo tan sencillo quizás sea más adecuado utilizar un Dialog con el cuadro de texto, si el formulario fuera más grande el ejemplo sería una solución válida.

La base de este tip será el uso de una de las versiones del método startActivityForResult para abrir la Activity «hija» o sub-activity. Veamos en primer lugar los dos layout que usaremos, el primero con el ListView y el segundo con un campo de texto para introducir los elementos de dicho ListView. Como siempre, en el tip sólo se muestra los elmentos más importantes del ejemplo ya que siempre se prorpociona el ejemplo completo para poder «jugar» con él.

main.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    android:background="@android:color/background_light">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:onClick="editText"
        android:text="@string/editText" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/button1"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" 
        android:paddingLeft="6dp"
        android:paddingRight="6dp"
        android:cacheColorHint="@android:color/background_light"
        android:divider="@drawable/list_divider">

    </ListView>

</RelativeLayout>       

child.xml (interfaz de la subactivity)

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/background_light" >

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp"
        android:ems="10"
        android:hint="@string/text"
        android:singleLine="true" 
        android:textColor="@android:color/black"
        android:textColorHint="@android:color/darker_gray"/>


    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" >

        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="save"
            android:text="@string/save" />

        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="cancel"
            android:text="@string/cancel" />
    </LinearLayout>

</RelativeLayout>

Vamos a hacer lo siguiente:

  1. En la Activity principal que muestra el ListView, iniciaremos otra Activity para que una vez vez finalizada nos retorne un dato, que será un nuevo texto para mostrar en el ListView siempre y cuando el usario haya introducido algún valor.
  2. En la segunda Activity, antes de retornar a la primera, guardaremos los datos que queramos enviar de vuelta, en nuestro caso el texto, en un Intent. Invocaremos también al método setResult para pasar ese Intent y un código de error o similar a la primera Activity. Por último, saldremos de la Activity con un finish.
  3. Una vez finalizada la segunda Activity, se invocará el método onActivityResult que recibirá todos estos parámetros de la segunda Activity. Por lo tanto, tendremos que implementar este método y realizar las operaciones que sean oportunas que en nuestro caso será añadir el texto al ListView.

Lo significativo es que la primera Activity o la «padre» siempre es la misma instancia, por lo que conserva los datos que tenía antes de abrir la segunda pero al volver podemos procesar fácilmente los datos que le envía la siguiente Activity, en lugar de simplemente restaurarse como cuando se pulsa o invoca el back (aunque en este caso, podríamos simular el comportamiento del startACtivityForResult implementando el método onResume, pero mejor optar siempre por la solución proporcionada por Android para esta casuística). Veamos pues el código:

package com.danielme.tipsandroid.startactivityforresult;

import java.util.LinkedList;
import java.util.List;

import android.R.string;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;

import com.danielme.tipsandroid.startactivityforresult.R;

/**
 * 
 * @author danielme.com
 *
 */
public class MainActivity extends ListActivity
{
	
	private List<String> results = new LinkedList<String>();
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		setListAdapter(new ArrayAdapter<String>(this,R.layout.textview, results));

	}
	
	public void editText(View button)
	{
		Intent intent = new Intent(this, SecondActivity.class);
		startActivityForResult(intent, 2);
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		if (RESULT_OK == resultCode)
		{
			results.add(data.getStringExtra("text"));
			((ArrayAdapter<String>) getListAdapter()).notifyDataSetChanged();
		}
		else
		{
			Toast.makeText(this, R.string.cancel, Toast.LENGTH_LONG).show();
		}
	}
}
package com.danielme.tipsandroid.startactivityforresult;

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

import com.danielme.tipsandroid.startactivityforresult.R;

/**
 * 
 * @author danielme.com
 * 
 */
public class SecondActivity extends Activity
{

	private EditText editText = null;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.child);
		editText = (EditText) findViewById(R.id.editText1);		
	}

	public void save(View button)
	{
		String text = editText.getText().toString();
		
		if (text.trim().length() == 0)
		{
			editText.setError(getText(R.string.error));
		}
		else
		{
			Intent intent = getIntent();
			intent.putExtra("text", text);
			setResult(RESULT_OK, intent);
			finish();
		}
	}

	public void cancel(View button)
	{
		setResult(RESULT_CANCELED, new Intent());
		finish();
	}

}

El resultado final en GingerBread:


El proyecto completo para Eclipse ADT y el nuevo Android 4.4 se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.

<< TIPS ANDROID

4 comentarios sobre “Tip Android #32: usar «sub-activities» con startActivityForResult

  1. es interesante pero ocurre error si lo pudieras explicar mejor seria de mucha ayuda, o cuelgas el archivo para estudiarlo seria perfecto

  2. Muy bueno el blog, ahora una consulta. conviene utilizar sub-Activitys o Dialogs, para este tipo de cosas. Siempre hablando del punto de vista de rendimiento

    1. No debería haber problemas en usar subactivities utilizando Parcelables para intercambiar objetos si fuera necesario. El usar un Dialog dentro una Activity o una nueva pantalla con una subactivity dependerá más bien de la usabilidad de cada caso de uso en concreto.

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.