Listado completo de tutoriales
49 - Componente ListView (grabar sus datos con la clase SharedPreferences) |
Como sabemos cada vez que iniciamos una aplicación que utiliza un ListView debemos cargar sus elementos. En el concepto anterior vimos que podemos agregar elementos en forma dinámica. Ahora veremos como almacenan los datos mediante la clase SharedPreferences (vimos en un concepto anterior que esta clase nos facilita guardar datos en un archivo XML mediante una clave,valor)
En el momento que inicia la aplicación procederemos a leer el contenido del archivo de preferencias y a añadir los datos al ListView. Durante la ejecución del programa cuando se agreguen o eliminen elementos del ListView también procederemos a hacerlo del archivo de preferencias.
Confeccionaremos una aplicación similar al concepto anterior que muestre un ListView con números telefónicos y sus titulares, que permita agregar y eliminar números durante la ejecución del programa. Le agregaremos lo necesario para que los datos reflejados en el ListView se almacenen en un archivo de preferencias para evitar que se borren cuando se finaliza la aplicación.
Cada vez que arranque el programa procederemos a leer el archivo de preferencias y poblaremos el ListView.
Crear un proyecto llamado "Proyecto054".
La interfaz visual de la aplicación cuando la ejecutemos será parecida a esta:
El archivo XML 'activity_main.xml' es:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/et1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:ems="10" android:hint="nombre" android:inputType="textPersonName" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/et2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:ems="10" android:hint="telefono" android:inputType="textPersonName" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/et1" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="13dp" android:layout_marginLeft="13dp" android:layout_marginTop="13dp" android:onClick="agregar" android:text="Agregar" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/et2" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="presión larga sobre el telefono a borrar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.502" app:layout_constraintStart_toEndOf="@+id/button" app:layout_constraintTop_toBottomOf="@+id/et2" /> <ListView android:id="@+id/list1" android:layout_width="394dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/button" /> </androidx.constraintlayout.widget.ConstraintLayout>
Hemos iniciado la propiedad onClick con el nombre del método a ejecutar al ser presionado:
android:onClick="agregar" />
Por otro lado hemos fijado con "true" la propiedad focusableInTouchMode del RelativeLayout para que no aparezca con foco el primer EditText:
android:focusableInTouchMode="true">
Ahora pasemos al archivo MainActivity.java
package com.tutorialesprogramacionya.proyecto054; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.ListView; import java.util.ArrayList; import java.util.Map; import java.util.StringTokenizer; public class MainActivity extends AppCompatActivity { private ArrayList<String> datos; private ArrayAdapter<String> adaptador1; private ListView lv1; private EditText et1,et2; private SharedPreferences prefe1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); datos =new ArrayList<String>(); leerSharedPreferences(); adaptador1=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, datos); lv1=(ListView)findViewById(R.id.list1); lv1.setAdapter(adaptador1); et1=(EditText)findViewById(R.id.et1); et2=(EditText)findViewById(R.id.et2); lv1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { final int posicion=i; AlertDialog.Builder dialogo1 = new AlertDialog.Builder(MainActivity.this); dialogo1.setTitle("Importante"); dialogo1.setMessage("¿ Elimina este teléfono ?"); dialogo1.setCancelable(false); dialogo1.setPositiveButton("Confirmar", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogo1, int id) { String s=datos.get(posicion); StringTokenizer tok1=new StringTokenizer(s,":"); String nom=tok1.nextToken().trim(); SharedPreferences.Editor elemento=prefe1.edit(); elemento.remove(nom); elemento.commit(); datos.remove(posicion); adaptador1.notifyDataSetChanged(); } }); dialogo1.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogo1, int id) { } }); dialogo1.show(); return false; } }); } private void leerSharedPreferences() { prefe1=getSharedPreferences("datostelefonos", Context.MODE_PRIVATE); Map<String,?> claves = prefe1.getAll(); for(Map.Entry<String,?> ele : claves.entrySet()){ datos.add(ele.getKey()+" : " +ele.getValue().toString()); } } public void agregar(View v) { datos.add(et1.getText().toString()+" : "+et2.getText().toString()); adaptador1.notifyDataSetChanged(); SharedPreferences.Editor elemento=prefe1.edit(); elemento.putString(et1.getText().toString(),et2.getText().toString()); elemento.commit(); et1.setText(""); et2.setText(""); } }
Definimos como atributos el ArrayList que almacena en memoria la lista de teléfonos y titulares llamado datos, el ArrayAdapter que cumple como función ser el puente entre el ArrayList y el ListView, el ListView que muestra los datos, los dos EditText y finalmente la variable prefe1 de la clase SharedPreferences que nos permite almacenar en forma permanente los datos y su posterior recuperación:
private ArrayList<String> datos; private ArrayAdapter<String> adaptador1; private ListView lv1; private EditText et1,et2; private SharedPreferences prefe1;
En el método onCreate creamos el ArrayList:
datos =new ArrayList<String>();
Procedemos a llamar al método leerSharedPreferences() que tiene por objetivo leer el archivo de preferencias que almacena todos las personas y sus teléfonos.
El método de lectura es:
private void leerSharedPreferences() { prefe1=getSharedPreferences("datostelefonos", Context.MODE_PRIVATE); Map<String,?> claves = prefe1.getAll(); for(Map.Entry<String,?> ele : claves.entrySet()){ datos.add(ele.getKey()+" : " +ele.getValue().toString()); } }
Llamamos al método getSharedPreferences pasando el nombre del archivo de preferencias y el modo de apertura:
prefe1=getSharedPreferences("datostelefonos", Context.MODE_PRIVATE);
Creamos un objeto de la clase Map llamando al método getAll del SharedPreferences:
Map<String,?> claves = prefe1.getAll();
Mediante un ciclo recuperamos todas las claves y sus valores del archivo de preferencias y los almacenamos en el ArrayList con el objetivo que luego se puedan visualizar en el ListView:
for(Map.Entry<String,?> ele : claves.entrySet()){ datos.add(ele.getKey()+" : " +ele.getValue().toString()); }
Continuando en el método onCreate creamos el ArrayAdapter y utilizamos el layout más sencillo que es el que muestra solo un TextView en cada Item y lo indicamos mediante el valor android.R.layout.simple_list_item_1:
adaptador1=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, datos);
Obtenemos la referencia del ListView y le asociamos el ArrayAdapter para que pueda recibir los items a mostrar:
lv1=findViewById(R.id.list1); lv1.setAdapter(adaptador1);
Cuando se presiona el botón agregar procedemos a añadir un elemento al ArrayList y llamar seguidamente al método notifyDataSetChanged() del ArrayAdapter para que informe al ListView que actualice los datos en pantalla. Por otro lado en forma paralela procedemos a insertar una entrada en el archivo de preferencias indicando como clave el dato del et1 y como valor el valor del et2 :
public void agregar(View v) { datos.add(et1.getText().toString()+" : "+et2.getText().toString()); adaptador1.notifyDataSetChanged(); SharedPreferences.Editor elemento=prefe1.edit(); elemento.putString(et1.getText().toString(),et2.getText().toString()); elemento.commit(); et1.setText(""); et2.setText(""); }
Cuando se presiona un tiempo prolongado con el dedo un item del ListView se dispara el método onItemLongClick donde procedemos a mostrar un diálogo para que confirme o cancele la eliminación del teléfono seleccionado:
lv1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { final int posicion=i; AlertDialog.Builder dialogo1 = new AlertDialog.Builder(MainActivity.this); dialogo1.setTitle("Importante"); dialogo1.setMessage("¿ Elimina este teléfono ?"); dialogo1.setCancelable(false); dialogo1.setPositiveButton("Confirmar", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogo1, int id) { String s=datos.get(posicion); StringTokenizer tok1=new StringTokenizer(s,":"); String nom=tok1.nextToken().trim(); SharedPreferences.Editor elemento=prefe1.edit(); elemento.remove(nom); elemento.commit(); datos.remove(posicion); adaptador1.notifyDataSetChanged(); } }); dialogo1.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogo1, int id) { } }); dialogo1.show(); return false; } });
Como podemos ver cuando se presiona la opción "Confirmar" del diálogo procedemos a eliminar el elemento del ArrayList y pedir que se refresque la pantalla mediante la llamada del método notifyDataSetChanged del ArrayAdapter:
telefonos.remove(posicion); adaptador1.notifyDataSetChanged();
Por otro lado debemos eliminar el dato del archivo de preferencias, creamos un objeto de la clase StringTokenizer con el objetivo de dividir el nombre y el teléfono que están separados por el caracter de ":" (con la primer llamada a nextToken obtenemos el nombre del titular del teléfono, llamamos al método trim para eliminar el espacio en blanco que hay al final del nombre del titular):
String s=datos.get(posicion); StringTokenizer tok1=new StringTokenizer(s,":"); String nom=tok1.nextToken().trim(); SharedPreferences.Editor elemento=prefe1.edit(); elemento.remove(nom); elemento.commit();
Este proyecto lo puede descargar en un zip desde este enlace: proyecto054.zip