Archivo de la etiqueta: programación

Crear una tabla dinámica, por código en vez de usar XML

A veces necesitas crear una tabla de forma dinámica, por código. Por ejemplo, si quieres mostrar resultados de una base de datos en una pantalla con ScrollView, no deberías hacerlo mediante ListView ni mediante GridView porque incorporan sus propios controles de scroll, lo que interfiere con los de ScrollView. La solución más práctica es poner una tabla. Pero si lo que muestra la tabla procede de una base de datos, a priori no sabes cuántas filas debes poner, con lo que no puedes usar XML sino código. No se si me explico.

Crear una tabla por código es bastante fácil. En primer lugar tienes que crear una tabla vacía en XML. Vacía, sin fila alguna.

<TableLayout
            android:id="@+id/tablacultivos"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:stretchColumns="2"
            android:shrinkColumns="2" >
</TableLayout>
Vale, ahora nos vamos al código. Vamos a crear una tabla con 3 columnas y n filas. Primero enlazamos con la tabla y creamos unas cuantos patrones Layout básicos: para la fila y para cada una de las columnas.
TextView txtCultivos=(TextView) getView().findViewById(R.id.LblCultivos);
TableLayout tabladatos=(TableLayout) getView().findViewById(R.id.tablacultivos);
TableRow.LayoutParams layoutFila=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutFecha=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutMuestra=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutResultado=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
Como ves, vamos a poner las tres columnas y la fila todas iguales: con anchura WRAP_CONTENT y altura WRAP_CONTENT;
Ahora creamos variables para la fila y las columnas.
TableRow fila;
TextView txtFecha;
TextView txtMuestra;
TextView txtResultado;
Es conveniente que reseteemos la tabla a cero. En mi experiencia, si entras en una tabla dinámica todo bien. Pero si sales y vuelves a entrar, las filas se añaden otra vez con lo que aparecen duplicadas. Para evitarlo, empieza reseteando la tabla.
tabladatos.removeAllViews();
Ahora tienes que consultar tu base de datos y obtener un cursor. Eso es material para unos cuantos temas aparte y no te lo voy a explicar aquí. Si no sabes cómo consultar SQLite ni lo que es un cursor, nada de todo esto tiene sentido. Así que vete a tu base de datos, consúltala y obtén un listado en forma de Cursor. ¿Vale?.
De acuerdo, ya tienes un Cursor. Ahora vamos a recorrerlo y al tiempo vamos creando la tabla añadiendo una fila nueva en cada pasada. Observa que para añadir la fila hay que crear los TextView, puedes alterar luego sus propiedades con setGravity, setPadding, etc. También tienes que añadir los parámetros que creaste al principio. Y por último, añades los TextView al layout de fila. Y para terminar añades el layout de Fila a la tabla. Esto se repetirá una vez por pasada a medida que avanzamos por el cursor. Pongo sólo el código relevante.
No me digas que no es fácil.
if (cursor.moveToFirst()) {
         do {
               fila=new TableRow(this); // Si, necesita contexto
               fila.setLayoutParams(layoutFila);

               txtFecha=new TextView(this);
               txtMuestra=new TextView(this);
               txtResultado=new TextView(this);

               txtFecha.setText(cursor.getString(0));
               txtFecha.setGravity(Gravity.RIGHT);
               txtFecha.setPadding(0, 0, 5, 0);
               txtFecha.setLayoutParams(layoutFecha);

               txtMuestra.setText(cursor.getString(1));
               txtMuestra.setPadding(0,0,5,0);
               txtMuestra.setLayoutParams(layoutMuestra);

               txtResultado.setText(cursor.getString(2));
               txtResultado.setLayoutParams(layoutMuestra);
               fila.addView(txtFecha);

               fila.addView(txtMuestra);
               fila.addView(txtResultado);

               tabladatos.addView(fila);
         } while (cursor.moveToNext());
}

Variables globales en Android

Vale, estás haciendo un programa Android y a veces te interesa tener variables globales, accesibles desde cualquier Activity. Una forma muy sencilla de hacerlo es crear una clase que extienda de Application. Con esta clase, podrás almacenar cualquier cosa, desde variables básicas hasta objetos completos, lo que resulta muy práctico. Crea un fichero que contenga una clase tal que así:

public class ClaseGlobal extends Application {
       private final static int versiondb=1;
       private int posicionlistview;
       private int toplistview;
       private FichaPaciente paciente;
       private FichaEnvin envin;

       public void ponPaciente(FichaPaciente p) {
                paciente=p;
       }
       public FichaPaciente dimePaciente() {
                return paciente;
       }
       // etc...
}

Para simplificar, solo he puesto los métodos para meter y extraer un objeto de clase FichaPaciente, pero sería lo mismo para objetos de otra clase o variables básicas.

Para poder utilizar la clase ClaseGlobal sólo tendríamos que instanciarla en cualquier Activity. No se instancia de la forma normal, teniendo en cuenta que funcionará un poco a lo Singleton, es decir, siempre se instanciará el mismo objeto de forma global.

ClaseGlobal global=(ClaseGlobal) getActivity().getApplicationContext();
global.ponPaciente(paciente);

Y ya.

Recordar la posición de un ListView

Si cambias a otra Activity, cuando regreses tu ListView habrá perdido la posición. Eso, a veces puede resultar bastante desconcertante. Una solución nos la cuentan en StackOverflow y es bastante sencilla de aplicar.

Puedes recoger la posición actual cuando abandonas la Activity obteniendo dos valores integer en tu onPause.

@Override 
public void onPause() {
	super.onPause();
	int index=lstListado.getFirstVisiblePosition();

	View v=lstListado.getChildAt(0);
	int top=(v==null) ? 0:(v.getTop()-lstListado.getPaddingTop());

	global.ponPosicion(index);
	global.ponTop(top);
}

Observa que que usamos la variable index para saber cual es la entrada que se veía en primer lugar en el listview. Pero puede ocurrir que esa entrada se viera a medias, así que usamos una segunda variable top para obtener cuánto se mostraba de ella.

Después usamos el método que queramos para guardar ambos valores. En mi caso, he almacenado ambos int usando variables globales, pero ese es otro tema para otra entrada.

Y ahora en el onResume, recogemos ambos valores y con ellos usamos el método setSelectionFromTop para poner el ListView como antes:

@Override
public void onResume() {
        super.onResume();
        int index=global.dimePosicion();
        int top=global.dimeTop();
        lstListado.setSelectionFromTop(index, top);
}
Fácil sí que es.

AutoCompleteTextView

Supongamos que quieres un EditText pero un poco más sofisticado, con sugerencias. Esto es, que según escribes en el EditText te vaya sugiriendo posibilidades que concuerden con lo que vas metiendo. Bueno, pues eso es AutoCompleteTextView. Crear uno es bastante fácil, pero es un widget que tiene su miga.

AutoCompleteTextView se describe en multitud de sitios. Pero pocos te hablan de que tiene un bug por culpa del cual, dependiendo del tema que uses, las sugerencias te salen de color blanco sobre blanco: o sea, no se lee nada. Nosotros vamos a obviarlo especificando nuestra propia plantilla, que es lo primero que vamos a hacer. Hala, crea un fichero XML en layout al que llamaremos item_simple.xml.

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@android:id/text1"
      style="?android:attr/spinnerDropDownItemStyle"
      android:singleLine="true"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:minHeight="40dip"
      android:ellipsize="marquee"
      android:textColor="#222222" />

Vale, ahora necesitas definir tres cosas:

private AutoCompleteTextView editGermen;
ArrayAdapter<String> adaptadorGermen;
String[] germenes;

Lo primero de todo, vamos a rellenar el array de Strings al que hemos llamado germenes, y que es el que alimentará las sugerencias. Por ejemplo, si estamos usando una base de datos SQLite, podemos cargar todos los gérmenes que hayamos introducido en ella. Lo más práctico es llamar a una subrutina desde el onCreate, por ejemplo:

public void rellenaGermenes() {
pacientesdb=new PacientesSQLiteHelper(getActivity(), "Pacientes", null, global.dimeVersionDB());
db=pacientesdb.getReadableDatabase();
String consultaDBInterna="select distinct germen from germenes order by germen";
Cursor c=db.rawQuery(consultaDBInterna, null);
int nGermenes=c.getCount();
germenes=new String[nGermenes];
int x=0;
if (c.moveToFirst()) {
do {
germenes[x]=c.getString(0);
x++;
} while (c.moveToNext());
}
db.close();
pacientesdb.close();
}

Vale, ahora llamamos a esta rutina desde el onCreate, y a continuación definimos el AutoCompleteView, el adaptador (especificando nuestro XML), y los ponemos en contacto:

editGermen=(AutoCompleteTextView) vista.findViewById(R.id.editGermen);
adaptadorGermen=new ArrayAdapter<String>(this, R.layout.item_simple, germenes);
editGermen.setThreshold(3);
editGermen.setAdapter(adaptadorGermen);