Almacenar información en Android

Este es uno de los capítulos del tutorial Como programar tu primera aplicación Android en Ubuntu. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.

Introducción

Uno de los problemas al que nos tendremos que enfrentar habitualmente cuando estamos desarrollando aplicaciones Android, es como guardar la información que utiliza nuestra aplicación, una base de datos, un archivo de texto, etc. Lo menos recomendable es que la información que tenemos que utilizar esté en el código de la aplicación, puesto que esto dificulta considerablemente las labores de mantenimiento de la aplicación, así que lo mejor es utilizar algún tipo de archivo externo.

De entre las distintas posibilidades existentes, finalmente me he decantado por utilizar un archivo json, antes que una base de datos. El motivo es la sencillez, dado que no necesito crear la base de datos, ni durante el funcionamiento de la aplicación tengo que escribir información, solo necesito leer, se trata de un archivo que no se va a modificar por el usuario.

Una vez terminado este artículo, ya tendrás una aplicación que será capaz de convertir unidades (excepto la temperatura).

2013-01-13 20.21.48.png

En Github y otras apreciaciones

Dado que empieza a aparecer bastante código, he decidido crear un repositorio en GitHub para aquel que lo quiera descargar para seguir el desarrollo de la aplicación o bien para simplemente jugar con eĺ, pueda hacerlo fácilmente.

Además he modificado ligeramente el aspecto de la aplicación para que sea lo mas parecida a la que estamos desarrollando en los artículos de Ubuntu Phone OS, dado que me parece una interfaz mucho mas clara y limpia.

2013-01-13 21.53.48.png

Guardando información

El lugar adecuado para almacenar la información es el directorio, «raw» dentro de «res». Este directorio no existe con lo que tendrás que crearlo, y copiar la información, el archivo «equival» a este directorio:

0201_Selección.png

La estructura del archivo equival

Para facilitar el acceso a las diferentes unidades y magnitudes, he definido la estructura del archivo json de esta forma:

{
    "magnitudes": {
        "nombre de una magnitud": {
            "nombre": "nombre de una magnitud", 
            "unidades": {
                "simbolo de la unidad": {
                    "factor": valor, 
                    "nombre": "nombre de la unidad", 
                    "simbolo": "simbolo de la unidad"
                }, 
                "simbolo de otra unidad": {
                    "factor": valor, 
                    "nombre": "nombre de otra unidad", 
                    "simbolo": "simbolo de otra unidad"
                }
        }
    }
}

Esto nos permite acceder fácilmente a una magnitud o unidad determinada fácilmente.

Leer el archivo

La lectura de archivos de texto es realmente muy sencilla, indico el código a continuación:

InputStream is = getResources().openRawResource(R.raw.equival);
Writer writer = new StringWriter();
char[] buffer = new char[1024];
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
    writer.write(buffer, 0, n);
}
String jsonString = writer.toString();

Indicar que «R.raw.equival», lo crea el propio «Eclipse» cuando añadimos el archivo «equival» al directorio, y el resto de código es el habitual para leer cualquier archivo de texto en java.

Trabajando con json

Lo siguiente es poder trabajar fácilmente con el archivo json que hemos creado. Esto también es relativamente sencillo. Indico el código a continuación:

List SpinnerArray =  new ArrayList();
JSONObject myjson = new JSONObject(jsonString);
JSONObject mg = myjson.getJSONObject("magnitudes");
Iterator<?> iterator = mg.keys();
SpinnerArray =  new ArrayList();
while(iterator.hasNext()){
    SpinnerArray.add((String) iterator.next());
}
Collections.sort(SpinnerArray);

Con la última línea ordeno las magnitudes para que aparezcan por orden alfabético y sea mas sencillo ordenarlas. Y por último, me queda asignarlo al «Spinner» e cuestión:


magnitudes = (Spinner) findViewById(R.id.spinner1);
List SpinnerArray = this.get_magnitudes();
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, SpinnerArray);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
magnitudes.setAdapter(adapter);

Con estas sencillas líneas de código, he leído todas las magnitudes que tenemos disponibles, y se las he asignado al correspondiente Spinner. El siguiente paso es hacer lo mismo pero con las unidades. Esto es igual de sencillo, pero para facilitar el trabajo de conversión de unidades, he creado una sencilla clase que guarda tres datos, como son el nombre de la unidad, su símbolo y un factor de conversión. Además le he dotado de algunas características interesantes, como es que sea ordenable, implementando su correspondiente interfaz, y sobrescribiendo «toString», para que en el Spinner se vea el valor del símbolo. Total, que quedaría la siguiente clase:

public class Unit implements Comparable {
	private String simbolo;
	private String nombre;
	private double factor;
	public Unit(String simbolo, String nombre, double factor){
		this.setSimbolo(simbolo);
		this.setNombre(nombre);
		this.setFactor(factor);
	}
	public String getSimbolo() {
		return simbolo;
	}
	public void setSimbolo(String simbolo) {
		this.simbolo = simbolo;
	}
	public String getNombre() {
		return nombre;
	}
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	public double getFactor() {
		return factor;
	}
	public void setFactor(double factor) {
		this.factor = factor;
	}
	@Override
	public int compareTo(Unit another) {
		return this.getSimbolo().compareToIgnoreCase(another.getSimbolo());
	}
	@Override
	public String toString(){
		return this.getSimbolo();
	}
	@Override
	public boolean equals(Object object){
		Unit aunit = (Unit)object;
		return aunit.getSimbolo().equals(this.getSimbolo());
	}

}

Una vez implementada esta clase, ya podemos leer las unidades correspondientes a una magnitud determinada, con muy pocas líneas de código:

JSONObject myjson = new JSONObject(jsonString);
JSONObject mg = myjson.getJSONObject("magnitudes").getJSONObject(magnitud);			
Iterator<?> iterator = mg.getJSONObject("unidades").keys();
SpinnerArray =  new ArrayList();
while(iterator.hasNext()){
    String asimbolo = (String)iterator.next();
    JSONObject mu = mg.getJSONObject("unidades").getJSONObject(asimbolo);
    String anombre = (String)mu.getString("nombre");
    double afactor = mu.getDouble("factor");
    Unit aunit = new Unit(asimbolo,anombre,afactor);
    SpinnerArray.add(aunit);
}
Collections.sort(SpinnerArray);

y se lo podemos asignar a sus correspondientes Spinner. Esto lo hacemos cuando se produce el evento de seleccionar un elemento en el Spinner magnitud:

magnitudes.setOnItemSelectedListener(new OnItemSelectedListener(){
    @Override
    public void onItemSelected(AdapterView<?> arg0, View arg1,
        int arg2, long arg3) {
	String fu = String.valueOf(magnitudes.getSelectedItem());
	set_values_on_spinner(fu);
	convert_from();
    }
    @Override
    public void onNothingSelected(AdapterView<?> arg0) {
    }
});

para lo que utilizamos la función que hemos implementado «set_values_on_spinner(String magnitud)», que tiene el siguiente aspecto, similar a la que habíamos escrito para poner las magnitudes en el spinner correspondiente.

public void set_values_on_spinner(String magnitud){
    List SpinnerArray = this.get_unidades(magnitud);
    ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, SpinnerArray);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    unit_from.setAdapter(adapter);		
    unit_to.setAdapter(adapter);
}

¿Como y cuando convertir?

Tal y como he comentado anteriormente, mi objetivo era que esta aplicación fuera lo mas similar a la de Ubuntu Phone OS, así que he limpiado la interfaz, y el siguiente paso, consistía en convertir al cambiar los valores o las unidades. Esto es probablemente lo que mas tiempo me ha llevado, mas que nada por que sabía lo que quería hacer pero no me había planteado seriamente como hacerlo, así que he estado un buen rato con prueba y error hasta que he dado con el método mas adecuado.

Para hacer las conversiones he utilizado el siguiente código, que se produce al realizar un cambio en el texto:

value_to.addTextChangedListener(new TextWatcher(){
    @Override
    public void afterTextChanged(Editable s) {
    }
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
    int after) {
    }
    @Override
    public void onTextChanged(CharSequence s, int start, int before,
    int count) {
	if(value_to.hasFocus()||unit_to.hasFocus()){
            convert_from();
	}
    }
});

Ahí es donde está el asunto, y es que solo se realiza la conversión cuando tiene el «foco», de otra manera se metía en un bucle infinito que terminaba por tirar la aplicación bajo.

Por último, para realizar la conversión, el código es realmente sencillo:

Unit fu = (Unit)unit_from.getSelectedItem();
Unit tu = (Unit)unit_to.getSelectedItem();
double fv = convert2double(value_from.getText().toString());
double tv = 0.0;
tv = fv*fu.getFactor()/tu.getFactor();
value_to.setText(Double.toString(tv));

Lo mismo se tendría que hacer para convertir a «value_from»

Conclusiones

Con esto queda completamente lista la aplicación y es completamente funcional, aunque el interfaz de usuario ha quedado limpio, no es excesivamente manejable. Necesitamos por un lado que la aplicación guarde la última magnitud y unidades que hemos utilizado para que la próxima vez que entremos en la aplicación estén disponibles fácilmente. Y por otro lado, tal y como indiqué en el artículo anterior, «Como programar tu primera aplicación Android en Ubuntu (II)«, nos queda que las magnitudes aparezcan en la parte izquierda de la ventana, y se puedan seleccionar fácilmente.

Te recomiendo que visites, y descargues, el repositorio que he creado en GitHub, porque te dará una visión de conjunto de lo que hemos hecho hasta el momento, y de como funciona la aplicación. Así mismo, te permitirá modificar la aplicación y probar por ti mismo nuevas funcionalidades.

2 comentarios en “Almacenar información en Android

  1. buenos tutoriales, estoy dando le seguimiento a estoy curos y compartiéndolo en un grupo de ubuntu en google+ y en otra comunidad que estoy y an sido muy bien aceptado, espero que sigas adelante con esto tutorias que hay muchas personas interesados en aprender el dema. y grasias por este trabajo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *