Hemos visto hasta ahora como mediante la llamada a la función 'mutableStateListOf' podemos crear una lista que permite sincronizar sus datos con Compose. Vimos que si agregamos o eliminamos elementos, los mismos se ven reflejados en pantalla en forma automática al utilizar funciones composables.
Veremos como podemos almacenar los datos de la lista dinámica en una estructura de datos muy sencilla que son los archivos de texto.
Cuando el programa se inicia procederemos a ver si existe el archivo de texto, en dicho caso procedemos a leerlo y cargar el mismo en la lista.
Se deben disponer dos controles para el ingreso del nombre de un contacto y su mail. Al presionar un botón almacenar dichos valores en una lista con estado y mostrar todos los contactos en pantalla, además añadirlos al final de un archivo de texto. Agregar un botón que lo pueda eliminar de la lista.
Finalmente cada vez que se inicie el programa si el archivo de texto existe proceder a leer los datos de los contactos.
Creamos el proyecto llamado 'Compose12'.
La interfaz visual a implementar es similar al concepto anterior, con la salvedad que los datos no se pierden cuando se detiene el programa:
El código que debemos implementar para poder eliminar elementos de una lista que mantiene el estado es:
package com.tutorialesprogramacionya.compose12 import android.content.Context import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.Button import androidx.compose.material.Divider import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import java.io.File class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) leerDatos() setContent { AdministrarContactos() } } fun leerDatos() { val file = File(filesDir, "contactos.txt") if (file.exists()) { val lista = file.readLines() var indice = 0 while (indice < lista.size) { contactos.add(Contacto(lista.get(indice), lista.get(indice + 1))) indice += 2 } } } } data class Contacto(val nombre: String, val mail: String) val contactos = mutableStateListOf<Contacto>() @Composable fun AdministrarContactos() { val activity = LocalContext.current var nombre by remember { mutableStateOf("") } var mail by remember { mutableStateOf("") } Column() { OutlinedTextField(value = nombre, onValueChange = { nombre = it }, label = { Text("Nombre de contacto") }, modifier = Modifier .fillMaxWidth() .padding(5.dp) ) OutlinedTextField(value = mail, onValueChange = { mail = it }, label = { Text("mail") }, modifier = Modifier .fillMaxWidth() .padding(5.dp) ) Button(onClick = { val nuevoContacto = Contacto(nombre, mail) contactos.add(nuevoContacto) val path = activity.getFilesDir() val file = File(path, "contactos.txt") file.appendText("${nombre}\n${mail}\n") nombre = "" mail = "" }) { Text(text = "Agregar", modifier = Modifier.fillMaxWidth()) } LazyColumn() { itemsIndexed(contactos) { indice, contacto -> MostrarContacto(indice, contacto) } } } } @Composable fun MostrarContacto(indice: Int, contacto: Contacto) { val context = LocalContext.current Text(text = contacto.nombre) Text(text = contacto.mail) Image(painter = painterResource(id = android.R.drawable.ic_delete), contentDescription = "", modifier = Modifier.clickable { contactos.removeAt(indice) grabarCambios(context) }) Divider( modifier = Modifier .fillMaxWidth() .width(4.dp), color = Color.Black ) } fun grabarCambios(context: Context) { val file = File(context.filesDir, "contactos.txt") file.delete() for (contacto in contactos) { file.appendText("${contacto.nombre}\n${contacto.mail}\n") } }
Inmediatamente se inicia el Activity procedemos a llamar a un método donde se leerá el archivo de texto:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) leerDatos() setContent { AdministrarContactos() } }
El método 'leerDatos' verifica si existe el archivo 'contactos.text', en caso afirmativo llamamos al método readLines que nos retorna una lista con un elemento por cada línea del archivo de texto.
Como veremos posteriormente guardamos el nombre del contacto en una línea y su mail en otra. Procedemos mediante un while a extraer dos elementos consecutivos de la lista, crear un objeto de la clase Contacto y lo insertamos en nuestra lista dinámica para que se muestren automáticamente en pantalla:
fun leerDatos() { val file = File(filesDir, "contactos.txt") if (file.exists()) { val lista = file.readLines() var indice = 0 while (indice < lista.size) { contactos.add(Contacto(lista.get(indice), lista.get(indice + 1))) indice += 2 } } }
La función lambda que agrega un elemento en la lista dinámica, también agrega dos líneas al final del archivo de texto:
Button(onClick = { val nuevoContacto = Contacto(nombre, mail) contactos.add(nuevoContacto) val path = activity.getFilesDir() val file = File(path, "contactos.txt") file.appendText("${nombre}\n${mail}\n") nombre = "" mail = "" }) {
El proceso de borrado es más costoso, debido a que tenemos que recrear el archivo de texto nuevamente (lo borramos al archivo y volvemos a cargar con todos los elementos de la lista dinámica que ya no tiene el eliminado):
fun grabarCambios(context: Context) { val file = File(context.filesDir, "contactos.txt") file.delete() for (contacto in contactos) { file.appendText("${contacto.nombre}\n${contacto.mail}\n") } }
Este proyecto lo puede descargar en un zip desde este enlace: Compose12.zip