03-Intercambio de Datos Entre Actividades
03-Intercambio de Datos Entre Actividades
03-Intercambio de Datos Entre Actividades
DATOS ENTRE
ACTIVIDADES
ANDROID STUDIO
INDICE
2
ENVIAR DATOS A UNA SEGUNDA
ACTIVIDAD
Vamos a hacer un ejemplo para ver mejor los pasos a seguir. Primero crearemos un nuevo
proyecto llamado Enviar y Devolver Información, desde una Empty Activity.
En este ejemplo vamos a hacer una actividad que abra otra, pero pasándole la
información, para que la segunda actividad sea capaz de trabajar con la información
recibida.
En el layout borramos el Texview por defecto y arrastramos un Password del grupo Text
y un Button para desencriptar.
Seleccionamos los dos elementos a la vez y con la tecla cmd hacemos clic en las dos y
ponemos un ancho de 0dp match-constraint en layout_width. El primero lo anclamos a
la parte superior con distancia de 32 y le damos un margen izquierdo y derecho de 16px.
Le ponemos como identificador txtPasswordMain con la propiedad hint el texto
password.
3
Con el botón hacemos lo mismo, lo anclamos a la parte superior (que será el campo de
Password), le damos margen izquierdo y derecho de 16px, el id btnDesencriptarMain y el
texto DESENCRIPTAR.
package com.cieep.a03_enviarydevolverinformacin;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Después creamos una función para inicializar las variables y la llamamos en el método
onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inicializarVariables();
}
4
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inicializarVariables();
}
});
}
Necesitamos crear la segunda actividad. Para ello vamos a la carpeta del proyecto dentro
de java, botón derechoànewàactivityàEmpty Activity
Con la actividad creada vamos al Main y añadimos las siguientes líneas dentro de
setOnClickListener()
5
Intent intent = new Intent(MainActivity.this,
DesencriptarActivity.class);
Podemos hacer la metáfora que el Intent es el vehículo conductor como el Google Maps al
que le especificamos origen y destino. Si queremos llevarnos ropa al viaje necesitamos
una maleta o mochila mágica que no tiene fin y cabe cualquier tipo de cosa.
La mochila es el Bundle, dentro puedo poner lo que quiera, pero con una especificación
le tengo que decir a la mochila que tipo de dato estoy poniendo. Esto lo hago con un put
que tiene dos argumentos, una key y un valor, le paso el nombre de la variable y el valor
de la variable. Esto lo necesitaré para saber qué información vamos a sacar.
Es importante mantener una lógica, porque luego, para sacar la información se tiene que
llamar exactamente igual.
Ahora falta arrancar la actividad, pero antes hay que cargar la maleta en el coche con un
putExtras.
Los Bundles no son obligatorios, aunque si recomendables para reaprovechar ese grupo
de datos en otras actividades.
btnDesencriptar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
DesencriptarActivity.class);
Bundle bundle = new Bundle();
bundle.putString("PASS", password);
intent.putExtras(bundle);
startActivity(intent);
}
});
6
Vamos ahora a hacer el código de la segunda actividad. ¿Que se necesita para abrir una
actividad? Un Intent, toda actividad se crea a partir de un Intent que es el que define toda
la información de esa actividad. El Main es la única actividad que no se crea por un Intent,
aunque esto no es del todo cierto, ya que esta información está en el manifest
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.03EnviaryDevolverInformación"
tools:targetApi="31">
<activity
android:name=".DesencriptarActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Si quisiera cambiar la actividad inicial tendría que cambiar estas líneas a la actividad que
quiero que sea la primera, y cambiar el exported a true en la actividad que ahora
queremos que sea la principal.
¿Porque esta explicación? Para que se entienda que toda actividad ha sido creada por un
Intent y por ese motivo siempre podremos hacer un get al Intent que lo ha creado.
7
que la ha llamado. Para hacerlo bien, sabemos seguro que Intent existe, pero no podemos
estar seguros que el Bundle existe, por eso ponemos el if.
package com.cieep.a03_enviarydevolverinformacin;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_desencriptar);
if(bundle != null){
String password = bundle.getString("PASS");
Toast.makeText(this, password,
Toast.LENGTH_SHORT).show();
}
}
}
Hay un error desde el principio, no hay error de compilación, sino de lógica. Tenemos que
ejecutar y ver que está pasando y porque está pasando.
El Toast sale vacío, el error está en el onCreate() de la actividad principal, porque cuando
se crea la actividad aún no se ha escrito nada en el texto.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inicializarVariables();
8
String password = txtPassword.getText().toString();
btnDesencriptar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
DesencriptarActivity.class);
Bundle bundle = new Bundle();
bundle.putString("PASS", password);
intent.putExtras(bundle);
startActivity(intent);
}
});
}
Ahora vamos a montar dentro del paquete principal un nuevo paquete llamado modelos,
para crear nuestras clases de datos, crearemos una java class llamada Usuario.
package com.cieep.a03_enviarydevolverinformacin.modelos;
9
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Usuario{" +
"email='" + email + '\'' +
", password='" + password + '\'' +
'}';
}
}
Le cambiamos el id por txtEmailMain, lo hacemos igual de ancho que los otros y en el hint
le ponemos el texto email.
LA MALAà igual que hemos definido una variable String para el Password hacemos otra
para el Email. Porque es la mala, porque con un modelo de datos de dos elementos no
cuesta, pero si es un modelo de 20 elementos no es una buena solución.
btnDesencriptar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String email = txtEmail.getText().toString();
String password = txtPassword.getText().toString();
Intent intent = new Intent(MainActivity.this,
DesencriptarActivity.class);
10
Bundle bundle = new Bundle();
bundle.putString("PASS", password);
bundle.putString("EMAIL", email);
intent.putExtras(bundle);
startActivity(intent);
}
});
}
}
Funciona, pero cuando el objeto tiene más de un par de atributos no es muy práctico.
LA BUENA à Vamos a guardar el objeto en un fichero binario para después ser capaz de
reconstruirlo. A este proceso se le llama serializar un objeto. Si queremos enviar un
objeto la clase se tiene que poder serializar, por este motivo la clase usuario tiene que
implementar la interfaz Serializable.
Con esto, en lugar de enviar dos Strings como antes el Bundle tiene que enviar un
putSerializable, con una clave y un objeto.
11
En la segunda actividad eliminamos el Email y el Password y modificamos el usuario
pasándole al Bundle con getSerializable y le hacemos un cast a Usuario para que sepa
que el modelo de datos que recibe es de tipo usuario.
12
ACTIVIDAD SECUNDARIA
DEVUELVE DATOS A LA ACTIVIDAD
PRINCIPAL
package com.cieep.a03_enviaryrecibirinformacin.modelos;
@Override
public String toString() {
return "Direccion{" +
"calle='" + calle + '\'' +
", numero=" + numero +
", ciudad='" + ciudad + '\'' +
'}';
}
}
13
Creamos una actividad de tipo Empty que sea capaz de crear objetos de tipo Direccion
14
Añadimos el identificador txtCalleCreateDir, le borramos el texto y le ponemos como hint
el texto Calle.
package com.cieep.a03_enviaryrecibirinformacin;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_dir);
inicializaVistas();
btnCrear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Direccion direccion = new
Direccion(txtCalle.getText().toString(),
Integer.parseInt(txtNumero.getText().toString()),
txtCiudad.getText().toString());
}
});
15
Para poder devolver el objeto dirección hace falta un Bundle. Para poder serializarlo la
clase Direccion debe implementar la interfaz Serializable.
Toda información que viaje va dentro de un Bundle(maleta) y todo Bundle ira dentro de
un Intent(coche).
Como es un “viaje de ida y vuelta” y cuando salgo le digo de donde salgo y a donde voy,
cuando vuelva no será necesario que le diga nada, por lo que creamos un Intent vacío, al
que le pondremos la “maleta”.
Para volver al destino la actividad actual tiene que destruirse, como va a terminar
tenemos que decirle el motivo de la terminación. Si el usuario le da al botón de atrás
automáticamente hace lo mismo que Cancelar. Si el usuario le dio al botón de Guardar
tenemos que hacer un setResult para especificar que todo ha ido bien y le enviamos los
datos.
btnCrear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Direccion direccion = new
Direccion(txtCalle.getText().toString(),
Integer.parseInt(txtNumero.getText().toString()),
txtCiudad.getText().toString());
16
Vamos a poner un botón de CrearDireccion en la vista del MainActivity con id
btnCrearDirMain y texto Crear Direccion
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inicializarVariables();
btnDesencriptar.setOnClickListener(new
View.OnClickListener() {
@Override
public void onClick(View view) {
String email = txtEmail.getText().toString();
String password = txtPassword.getText().toString();
Intent intent = new Intent(MainActivity.this,
DesencriptarActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("USER", new Usuario(email,
password));
intent.putExtras(bundle);
startActivity(intent);
}
});
btnCrearDir.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}
Para abrir actividades hemos utilizado el startActivity(), pero tiene un “hermano mayor”
capaz de hacer más cosas que esta deprecated, que es el startActivityForResult() lo que
hace es decir a Android que abra una actividad, pero cuando esa actividad se termine se
ejecutará una función para recuperar los datos que me devuelva.
17
Vamos a hacer una metáfora con el envío de una carta. Podemos hacer una carta y hacer
un envío normal y luego despreocuparnos. Pero también podemos hacer una carta con
acuse de recibo y dependiendo si llega o no actuar en consecuencia.
Tenemos que generar algún tipo de tag o índice para poder identificar qué actividad ha
terminado y con eso actuar en consecuencia.
La forma más simple de hacerlo es usando constantes, de forma que el número de las
constantes sea diferente.
btnCrearDir.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,
CreateDirActivity.class);
startActivityForResult(intent, DIRECCIONES);
}
});
Este startActivityForResult() hará que se ejecute un método que tienen todas las
actividades que será onActivityResult(). Aunque lo marca como deprecated hace poco
que lo está, y habrá muchas aplicaciones que aún lo utilicen. Por este motivo vamos a
verlo de esta forma y luego veremos la nueva forma que lo sustituye.
@Override
protected void onActivityResult(int requestCode, int resultCode,
@Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Este método recibe un requestCode para que sepamos que ventana se ha cerrado, un
resultCode para que sepamos en qué estado se ha cerrado y un Intent que puede ser
Nullable porque puede que vuelva vacío.
18
Para poner comentarios de función /** sobre la función + INTRO
/**
* Se activa al retornar de un starActivityForResult y dispara las
acciones necesarias
* @param requestCode -> identificador de la ventana que se ha
cerrado "el tipo de dato que retorna"
* @param resultCode -> el modo en que se ha cerrado la ventana
* @param data -> Datos retornados (Intent con un Bundle dentro)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode,
@Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Lo primero que debería hacer esta función es averiguar que ventana se ha cerrado, y
luego averiguar si se cerró por Guardar, por último comprobar si hay un Intent.
/**
* Se activa al retornar de un starActivityForResult y dispara las
acciones necesarias
* @param requestCode -> identificador de la ventana que se ha
cerrado "el tipo de dato que retorna"
* @param resultCode -> el modo en que se ha cerrado la ventana
* @param data -> Datos retornados (Intent con un Bundle dentro)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode,
@Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == DIRECCIONES){
if(resultCode == RESULT_OK){
if(data != null){
Bundle bundle = data.getExtras();
Direccion dir = (Direccion)
bundle.getSerializable("DIR");
Toast.makeText(this, dir.toString(),
Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "No hay datos",
Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(this, "Ventana Cancelada",
Toast.LENGTH_SHORT).show();
}
}
19
Este método está obsoleto porque las buenas prácticas de utilizar constantes no son
usadas por los programadores todo lo que se debería y porque si pueden volver muchas
actividades podemos hacer un método de 1000 o 2000 líneas para dar respuesta a cada
una de ellas de forma independiente.
Creamos tareas en segundo plano, para poder hacer eso se ha creado un nuevo tipo de
datos, un objeto que es el ActivityResultLauncher. Definimos como quiero que se lance la
actividad y que quiero que haga en al caso hipotético que requiera un resultado.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inicializarVariables();
inicializarLaunches();
btnDesencriptar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String email = txtEmail.getText().toString();
String password = txtPassword.getText().toString();
Intent intent = new Intent(MainActivity.this,
DesencriptarActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("USER", new Usuario(email,
password));
intent.putExtras(bundle);
startActivity(intent);
}
});
btnCrearDir.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,
CreateDirActivity.class);
20
startActivityForResult(intent, DIRECCIONES);
}
});
}
Bundle bundle =
result.getData().getExtras();
Direccion dir = (Direccion)
bundle.getSerializable("DIR");
Toast.makeText(MainActivity.this,
dir.toString(), Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(MainActivity.this, "No
hay datos", Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(MainActivity.this, "Ventana
Cancelada", Toast.LENGTH_SHORT).show();
}
}
}
);
}
btnCrearDir.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,
CreateDirActivity.class);
// startActivityForResult(intent, DIRECCIONES);
launcherDirecciones.launch(intent);
}
});
21
EJERCICIOS
1. Hacer una actividad que permita escribir una frase y que tenga dos botones que
vayan a la misma actividad, pero que uno resuelva cuantos caracteres tiene la
frase y otro que diga cuantas palabras tiene esa misma frase.
b. 3 botones, uno para pasar a una actividad nueva que permite crear coches
(marca, modelo y color) , otro que permite crear motos (marca, modelo y
cilindrada) y otro que permite guardar bicis (marca y pulgadas). Cuando
se cree el objeto seleccionado se cerrará la actividad volviendo a la
principal y actualizando el número de objetos totales de esa lista.
22