Hola javafilos taringueros de America y del Mundo!!!
Bienvenidos a un nuevo aporte!
Bienvenidos a un nuevo aporte!
En mas de una ocasión puede resultar útil en una aplicación el guardar datos al ser cerrada para cargarlos cada vez que volvemos a abrirla, a esto se le llama persistencia de datos, y una buena manera de solucionarlo es mediante el uso de bases de datos (Llamese MySql, Acces, Etc.), la cual puede ser una solución optima para el manejo de datos, pero...
Y si no tenemos ni idea de como se usa una base de datos, o peor, si sabemos usarla pero también sabemos que son un lío al momento de hacer portable nuestra aplicación (distribuirla), ya que aveces el usuario debe instalar la base en su sistema, Etc.
Por estos motivos y mas es que una buena y simple solución es la de guardar datos en archivos de texto plano (txt), los cuales luego podremos recuperar cuando los precisemos.
Ahora bien, cuando guardamos datos en un archivo de texto, o bien el archivo ya lo creamos de antemano o lo creamos cuando guardamos los datos, como sea la cosa es que al usuario se le va a generar un archivo en el sistema, depende del tipo de aplicación que estemos creando podemos o bien permitirle al usuario elegir el lugar donde se creará ese archivo o podemos guardarlo nosotros en un lugar x defecto (normalmente en una carpeta junto al ejecutable).
Bien, si la opción que elegimos es la de darle la opción al usuario donde guardar el txt, si nuestra aplicación no tiene dependencias (ejecutables necesarios que deben estar junto a el para que funcione), nuestra aplicación sera totalmente portable y puede si quiere poner nuestro jar "pelado" en el escritorio.
Para elegir la ubicación de un archivo se usa:
[color=#000000][color=#000000][color=#000000][color=#000000]
File file = null;
javax.swing.JFileChooser fc = new javax.swing.JFileChooser();
if(fc.showSaveDialog==0){
file = fc.getSelectedFile();
}
[/color][/color][/color][/color]
Pero no es el caso de nuestro ejemplo...
Si nosotros no queremos preguntarle al usuario donde guardar los datos, xq lo cosideramos innecesario, podemos crear una carpeta que contenga dentro nuestro ejecutable y junto a el el archivo que usamos para la persistencia. Esto puede ser un tanto incómodo en el sentido de la practicidad y/o "éstetica" x decirlo de alguna manera, y aunq no hay problema ninguno con esto, mientras nuestro usuario ejecute nuestra aplicación dentro de esa carpeta no pasa nada pero si mueve el ejecutable pueden comenzar los errores.
Una buena manera de solucionar todo esto es además haciendo un instalador para nuestra aplicación, el mas sencillo de usar que encontré se llama CreateInstall y pueden descargar la versión gratis desde aquí:
Lo que logramos con esta aplicación es que nuestro programa se instale en la ruta que nosotros elijamos (x Ej.: C:Archivos de programa/...) creando una carpeta que contenga nuestra aplicación y sus dependencias, además de crear un acceso directo en el escritorio con un icono personalizado, el uso de este programa por ahora corre por su cuenta, tal vez en un próximo post ampliaré sobre el instalador, x ahora solo veremos como guardar y leer los datos en una aplicación común y corriente.
Como ejemplo haremos una aplicación muy sencilla, un JFrame con tres JTextField, en los TextFields escribiremos tres datos y estos se guardarán en nuestro txt al momento de dar cerrar a nuestra ventana, motivo x el cual sobreescribiremos el escuchador del evento de cerrar la ventana para que sea en ese momento que se guarden los datos.
Pero basta de palabrerio y vamos al código:
Lo primero es crear una clase (la que x prolijidad extendí de JFrame) y agregar a ésta los tres textFields:
[color=#000000][color=#000000][color=#000000][color=#000000]
/* Aplicación de prueba para guardar datos en txt
*
[email protected]
*/
public class NewMain extends javax.swing.JFrame {
javax.swing.JTextField text;
javax.swing.JTextField text1;
javax.swing.JTextField text2;
java.awt.Container content;
java.awt.Component c; //Para referenciar al Frame dentro de la clase "anonima" al cerrar...
public NewMain() {//Constructor de la clase
inicio( ) ;//"llamada" al método para agregar los componentes
}
private void inicio() {//método para agregar componentes
c=this;
content = getContentPane();//Para ahorrar código y x prolijidad...
//Inicializamos los componentes
text = new javax.swing.JTextField();
text1 = new javax.swing.JTextField();
text2 = new javax.swing.JTextField();
//Les damos tamaño, posición espacial y los agregamos al contenedor del JFrame
text.setBounds(20, 20, 120, 40);
text.setBorder(javax.swing.BorderFactory.createTitledBorder("Nombre"));//Borde con título
content.add(text);
text1.setBounds(20, 65, 120, 40);
text1.setBorder(javax.swing.BorderFactory.createTitledBorder("Telefono"));
content.add(text1);
text2.setBounds(20, 110, 120, 40);
text2.setBorder(javax.swing.BorderFactory.createTitledBorder("Mail"));
content.add(text2);
setSize(170, 250);//Tamaño del JFrame
setLayout(null);//Sin agrupación de componentes
setLocationRelativeTo(null);//Centrado en pantalla del JFrame
// Agregamos al JFrame un "Escuchador" de
// eventos de ventana, al que se le pasa una clase "anonima" como parámetro, esto nos sirve
// para que suceda lo que nosotros queramos cuando el usuario de cerrar en el botón x defecto de
// la barra de título.
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] argumentos) {
new NewMain().setVisible(true);//Instanciamos nuestra clase y como esta
//hereda todos los métodos de un JFrame ya mismo le indicamos que lo haga visible
}
}
[/color] [/color][/color][/color]
Bien, hasta aquí nada ni raro ni complicado verdad?
Con el código anterior nuestra ventana se ve así:

Ahora lo que haremos será escribir los métodos que nos van a permitir guardar y recuperar los datos.
Antes que nada nos adelantamos y creamos en la carpeta de nuestro proyecto un directorio que será donde se guardará nuestro txt con los datos, a este directorio podemos llamarle x ejemplo "Datos".
Una vez compilada nuestra aplicación esta carpeta deberá estar siempre junto a nuestro ejecutable (.jar):

Para crear nuestro método encargado de guardar los datos (que será de tipo "void" ) lo primero que haremos es
crear un nuevo fichero al que le pasamos la ruta relativa a nuestro directorio "Datos", ya que en este caso como he dicho estará siempre junto a nuestro ejecutable, podemos llamarle "Data".
Luego creamos un BufferedWriter, se suele ponerle de nombre "bw" (pueden nombrarlo cono quieran) que será el encargado de almacenar transitoriamente los datos para luego escribirlos en el fichero de texto mediante un FileWriter (le llamé "fw" ) que lleva como parámetro el fichero que creamos.
Hecho esto usamos el método write(String s) del BufferedWriter que lleva cómo parámetro un String.
Esto puede realizarse solo con un FileWriter pero tiene una API bastante limitada y como este programa es solo un ejemplo y confío en que ustedes lo usarán para aplicaciones mas complejas pues lo haremos de la manera óptima.
Bien, lo que haremos al guardar los datos será recoger el Texto de nuestros TextFields y agregar entre ellos un separador, en este caso una simple coma ("," ), para facilitarnos luego la lectura de los datos.
Por último cerramos la escritura para que se guarden los datos con el método close( ) del BW.
Todo esto lanza una excepción de tipo "IOException" x si no se pueden localizar los archivos, por lo que nos vemos obligados a capturarla con try-catch.
Nuestro método debería verse así (previa creación de una variable detipo File "fichero" usada x los dos métodos):
[color=#000000][color=#000000][color=#000000][color=#000000]
public void guardaDatos() {
try {
BufferedWriter bw;//Instancia de BW
bw = new BufferedWriter(new FileWriter(fichero));//Inicialización de "BW" con "FW" como parámetro con "fichero" como parámetro
bw.write(text.getText() + "," + text1.getText() + "," + text2.getText());//Escribimos lo que haya en los TextFields
//y entre ello agregamos una coma (",").
bw.close();//Cerramos para guardar los datos en el fichero
} catch (IOException ex) {
print("No se pudieron guardar los datos!n" + ex.getMessage());
}
}
[/color][/color][/color][/color]
Para probarlo solo debemos "llamar" al método dentro del "Escuchador" que creamos para la acción del botón cerrar.
Para lo que además podemos preguntarle al usuario si antes de salir desea guardar los datos mediante un JOptionPane.showOptionDialog con las opciones: "Si", "No", "Cancelar":
[color=#000000][color=#000000][color=#000000][color=#000000]
addWindowListener(new java.awt.event.WindowAdapter() {
[email protected]
public void windowClosing(java.awt.event.WindowEvent e) {
int seleccion = JOptionPane.showOptionDialog(
c, "¿Desea guardar los datos antes de salir?", "Atención!", 1, 3, null, new Object[]{"Si", "No","Cancelar"}, "Si");
switch(seleccion){
case 0://Si elegimos "Si"
guardaDatos();//Llamamos al método que escribe los datos
System.exit(0);//Y cerramos el programa
break;
case 1://Si elegimos "No"
System.exit(0);//Cerramos el programa
break;
//Si elegimos "Cancelar", pues nada...
}
}
});
[/color][/color][/color][/color]
Bien, ahora es el momento de crear el método que leerá los datos.
En principio creamos un arreglo de String[] para luego usar los datos y creamos también una variable auxiliar de tipo String que guardará los datos de forma temporal y dentro de un "if" que analizará si el archivo existe (para evitar excepciones la primera vez que se ejecuta nuestra app), crearemos un objeto de la vieja y peluda clase "Scanner", la cual nos brinda una amplia API muy útil al momento de leer datos (puede hacerse con "BufferedReader" pero "Scanner" es mas completa), luego leemos dentro de un while que analiza si existen mas lineas a leer (en nuesro caso escribiremos una sola linea pero insisto Uds. podrán hacerlo a su manera y/o necesidad), y dentro de este bucle guardamos los datos en la variable auxiliar "linea" y luego con el método "split(String s)" separaremos nuestros datos en variables de tipo "ector" usando el separador que usamos para guardarlos ( "," ) , y guardamos esos datos en el arreglo de String.
Hecho esto inmediatamente mostramos los datos en los TextFields, aquí el método completo:
[color=#000000][color=#000000][color=#000000][color=#000000]
private void leeDatos() {
String linea = null;//Para guardar los datos de forma temporal
if (fichero.exists()) {//Si se ha creado el fichero
try {
Scanner lector = new Scanner(fichero);
while(lector.hasNextLine()){
linea=lector.next();
}
datos = linea.split(",");
text.setText(datos[0]);
text1.setText(datos[1]);
text2.setText(datos[2]);
} catch (FileNotFoundException ex) {
print("No se pudo abrir el archivo!"+ex.getMessage());
}
}
}
[/color] [/color][/color][/color]
print es un método que me gusta hacer para mostrar datos en consola:
[color=#000000][color=#000000][color=#000000][color=#000000]
public static void print(String msj) {
System.out.println(msj);
}
[/color][/color][/color][/color]
Y pues está todo pronto, la clase completa se ve así:
[color=#000000][color=#000000][color=#000000][color=#000000]
import java.io.*;
import java.util.Scanner;
import javax.swing.JOptionPane;
/*
* Aplicación de prueba para guardar y leer datos en txt
*
[email protected]
[/color][/color][/color][/color]
[color=#000000][color=#000000][color=#000000][color=#000000]
[email protected]
*/
public class DatosFrame extends javax.swing.JFrame {
String[] datos;
javax.swing.JTextField text;
javax.swing.JTextField text1;
javax.swing.JTextField text2;
java.awt.Container content;//Contenedor para agregar componentes
java.awt.Component c;//Variable para referenciar a "this"
File fichero = new File("Datos/Data.txt");//Archivo usado para guardar y leer los datos, con ruta
//relativa a nuestro .jar
public DatosFrame() {
inicio();//Llamada al método que inicia y agrega componentes y "setea" nuestro Frame
leeDatos();//Llamada al método que lee los datos
}
private void inicio() {
//Inicializamos los componentes
c = this;//Para usar en el JOptionPane dentro de la clase anónima
content = getContentPane();//Para ahorrar código y x prolijidad...
text = new javax.swing.JTextField();
text1 = new javax.swing.JTextField();
text2 = new javax.swing.JTextField();
//Les damos tamaño, posición espacial y los agregamos al contenedor del JFrame
text.setBounds(20, 30, 120, 40);
text.setBorder(javax.swing.BorderFactory.createTitledBorder("Nombre"));//Borde con título
content.add(text);
text1.setBounds(20, 75, 120, 40);
text1.setBorder(javax.swing.BorderFactory.createTitledBorder("Telefono"));
content.add(text1);
text2.setBounds(20, 120, 120, 40);
text2.setBorder(javax.swing.BorderFactory.createTitledBorder("Mail"));
content.add(text2);
setSize(170, 250);//Tamaño del JFrame
setLayout(null);//Sin agrupación de componentes
setLocationRelativeTo(null);//Centrado en pantalla del JFrame
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);//Para que el botón cerrar ("X") no haga nada...
setTitle("Ejemplo");//Titulo del Frame
/*
* Agregamos al JFrame un "Escuchador" de eventos de ventana, al que se
* le pasa una clase "anonima" como parámetro,y sobreescribimos el
* método "windowClosing()" esto nos sirve para que suceda lo que
* nosotros queramos cuando el usuario dé cerrar en el botón x defecto
* de la barra de título.
*/
addWindowListener(new java.awt.event.WindowAdapter() {
[email protected]
public void windowClosing(java.awt.event.WindowEvent e) {
int seleccion = JOptionPane.showOptionDialog(
c, "¿Desea guardar los datos antes de salir?", "Atención!", 1, 3, null, new Object[]{"Si", "No","Cancelar"}, "Si");
switch(seleccion){
case 0://Si elegimos "Si"
guardaDatos();//Llamamos al método que escribe los datos
System.exit(0);//Y cerramos el programa
break;
case 1://Si elegimos "No"
System.exit(0);//Cerramos el programa
break;
//Si elegimos "Cancelar", pues nada...
}
}
});
}
public void guardaDatos() {
try {
BufferedWriter bw;//Instancia de BW
bw = new BufferedWriter(new FileWriter(fichero));//Inicialización de "BW" con "FW" como parámetro con "fichero" como parámetro
bw.write(text.getText() + "," + text1.getText() + "," + text2.getText());//Escribimos lo que haya en los TextFields
//y entre ello agregamos una coma (",").
bw.close();//Cerramos para guardar los datos en el fichero
} catch (IOException ex) {
print("No se pudieron guardar los datos!n" + ex.getMessage());
}
}
private void leeDatos() {
String linea = null;//Para guardar los datos de forma temporal
if (fichero.exists()) {//Si se ha creado el fichero
try {
Scanner lector = new Scanner(fichero);
while(lector.hasNextLine()){
linea=lector.next();
}
datos = linea.split(",");
text.setText(datos[0]);
text1.setText(datos[1]);
text2.setText(datos[2]);
} catch (FileNotFoundException ex) {
print("No se pudo abrir el archivo!"+ex.getMessage());
}
}
}
public static void print(String msj) {
System.out.println(msj);
}
public static void main(String[] argumentos) {
new DatosFrame().setVisible(true);//Instanciamos nuestra clase y como ésta
//hereda todos los métodos de un JFrame ya mismo le indicamos que lo haga visible
}
}
[/color][/color][/color][/color]
ya podemos ejecutar nuestra aplicacion, y si llenamos los campos y guardamos al cerrar, al abrir los datos son cargados automaticamente.
Insisto esto es un ejemplo básico pero se puede guardar todo tipo de datos de esta manera, desde variables númericas, hasta vectores e incluso listas y rutas de archivos e imágenes o URL´s, espero que les sea de útilidady como siempre:
Dudas, criticas, consultas, comentarios y/o puntos bienvenidos!