Cómo crear una app ToDo en Android


Vamos a construir nuestra propia lista de tareas en Android, no es más que varias acciones pendientes que no queremos olvidar y nos permita en cualquier momento marcarlas como resueltas.

Para la realización del siguiente ejemplo pueden auxiliarse en artículos creados anteriormente ya que haremos referencia a algunos temas ya abordados en estos:

  • Bases de Datos locales con SQLite.
  • Trabajo con ListView
  • Trabajo con Menús

Para comenzar creamos un nuevo proyecto y nos dirigimos al visual para insertar los elementos necesarios, en este caso solo insertamos un ListView que va a contener el listado de las tareas. Quedaría algo así:

Preview de la app

Fig1: Preview de la app

// activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal"
  tools:context=".MainActivity" >

  <ListView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/lista"
    android:layout_weight="1" />

</LinearLayout>

Para ahorrar espacio en la App vamos a insertar el botón de adicionar las tareas en el menú principal, para modificar el menú nos dirigimos a su respectivo XML situado en ./res/menu/main.xml. Solo es necesario una entrada en el menú que corresponde con la acción de adicionar una nueva tarea.

// Menú(main.xml)

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

  <item android:id="@+id/adicionar_tarea"
    android:showAsAction="always"
    android:icon="@android:drawable/ic_menu_add"
    android:title="Nueva Tarea" />
</menu> 

Nota: La propiedad android:showAsAction="always" hace que el ítem actué como el propio botón que lanza el menú (ver imágenes debajo, a la izquierda el ítem sin la propiedad).

Menú para adicionar tareas

Fig2: Menú para adicionar tareas

Necesitamos para el trabajo con el ListView otro XML que corresponde con el formato que va a tener cada ítem.

Items del listview

Fig3: Items del listview

Creamos este nuevo XML en /res/layout y lo nombramos formato_lista.
La idea es tener en un solo ítem de la lista un TextView para mostrar la tarea y un botón para marcar la tarea como realizada justo como se muestra en la imagen anterior. El código XML quedaría de la siguiente forma:

// formato_lista.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal" >
  
  <TextView
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="80"
    android:layout_marginLeft="10dp"
    android:text="Vacio"
    android:id="@+id/tarea"
    android:textSize="20dp"
    android:gravity="center_vertical" />

  <Button
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="20"
    android:text="Hecho"
    android:onClick="onHecho"
    android:id="@+id/btnhecho"/>    

</LinearLayout>

Nota: La propiedad layout_width (ancho) del TextView y del botón le asignamos 0dp ya que queremos especificar en porciento (%), la anchura que van a tener los dos elementos con respecto al layout padre que los engloba y con la propiedad layout_weight es donde especificamos el porciento que van a tener, en este caso es 80 y 20 respectivamente, o sea estamos asegurando que en todo tipo de pantalla de móvil la razón entre su dimensión ancho sea la especificada.
Solo resta el trabajo desde java, recuerden que utilizaremos una base de datos local para llevar el control de las tareas y poder guardarlas para su uso posterior en caso de que el usuario cierre la App o apague el dispositivo, etc.

Para separar y organizar el código creamos dos clases adicionales: BaseDatos.java y Tareas.java.

La primera nos sirve para ayudarnos en la creación de la base de datos y la segunda para controlar los métodos de insertar, ver y eliminar las tareas de la misma, cualquier duda consultar el artículo referente a las Bases Datos Locales con SQLite o apoyarse en el Demo al final de la página.

// BaseDatos.java

public class BaseDatos extends SQLiteOpenHelper {
    
  public BaseDatos(Context context) {
    super(context, "listatareas.db", null, 1);    
  }   


  @Override
  public void onCreate(SQLiteDatabase db) {         
    String sql="Create table tareas(_id INTEGER PRIMARY KEY AUTOINCREMENT,
                nombretarea Text);";
    db.execSQL(sql);    
  }


  @Override
  public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
    // Este código se ejecuta si ya está creada la base de datos por eso 
    // no es necesario hacer ninguna operación aquí.            
  }
}
// Tareas.java

public class Tareas {

  /*
   * Se declara el contexto, un objeto de tipo 
   * BaseDatos y otro de tipo SQLiteDatabase
   */

  Context contextoEjecucion;
  BaseDatos creador;
  SQLiteDatabase mDatos;

  public Tareas(Context c) {
    /*Constructor de la clase*/
    contextoEjecucion = c;
  }

  /* Método para insertar una tarea en la base de datos */
  public long Insertar(String nombretarea) {
    creador = new BaseDatos(contextoEjecucion);
    mDatos = creador.getWritableDatabase();

    ContentValues c = new ContentValues();
    c.put("nombretarea", nombretarea);

    return mDatos.insert("tareas", null, c);
  }

  /* Método para devolver un Cursor de la consulta 
   * obtenida con todas las tareas de la BD
   */
  public Cursor ver() {
    String datos = "";
    creador = new BaseDatos(contextoEjecucion);
    mDatos = creador.getReadableDatabase();
    String sql = ("Select * from tareas");
    Cursor cur = mDatos.rawQuery(sql, null);
    return cur;
  }

  /* Eliminamos la tarea que coincida con el parámetro pasado */
  public void BorrarTarea(String tarea){
    creador = new BaseDatos(contextoEjecucion);
    mDatos = creador.getWritableDatabase();
    String sql = ("DELETE FROM tareas WHERE nombretarea = '"+tarea+"'");
    mDatos.execSQL(sql);
  }

  /* Cerrar la biblioteca SQLite */
  public void cerrarSqlite() {
    creador.close();
  }

}

Bien ahora solo resta organizar la lógica de la App desde la Activity Principal (MainActivity.java):

public class MainActivity extends Activity {

  /* Al crearse la Activity se hace una llamada al 
   * método actualizarlista(), implementado posteriormente, 
   * que realiza una consulta a la base de datos listando 
   * todas las tareas que existen e importándolas para el ListView
   */

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    actualizarlista();
  }

  /* Método por defecto generado por el IDE que infla el menú 
   * de la Activity correspondiente
   */
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }
  
  /* Creamos un método para controlar cuando el usuario pulsa 
   * sobre la entrada del menú
   */
  public boolean onOptionsItemSelected(MenuItem item) {
      
    /* En caso de que el usuario seleccione la entrada lanzamos 
     * un cuadro de dialogo para pedir la tarea, dicha ventana va
     * a contener un botón de aceptar, cancelar, un TextView con el 
     * anuncio de Adicionar Tarea y un EditText para que el usuario 
     * entre la tarea
     */

    switch (item.getItemId()) {
      case R.id.adicionar_tarea:
        AlertDialog.Builder ventana = new AlertDialog.Builder(this);
        ventana.setTitle("Adicionar Tarea");
        ventana.setMessage("Tarea:");
        final EditText entrada = new EditText(this);
        ventana.setView(entrada);
        ventana.setPositiveButton("OK", new DialogInterface.OnClickListener() {
          @Override           
          public void onClick(DialogInterface dialogInterface, int i) {                 
            /* Se captura la tarea entrada por el usuario */
            String ntarea = entrada.getText().toString();

            /* Creamos un objeto de tipo Tareas y le pasamos el 
             * Contexto  en el que estamos, posteriormente se inserta 
             * en la BD la tarea, se cierra el SQLite y por último refrescamos 
             * la lista de tareas para observar la nueva tarea.
             */                    
            Tareas bdatos = new Tareas(MainActivity.this);
            bdatos.Insertar(ntarea);  
            bdatos.cerrarSqlite();
              
            actualizarlista();
          }
        });
       
        ventana.setNegativeButton("Cancelar",null);          
        ventana.create().show();
        break;        
    }
    
    return super.onOptionsItemSelected(item);
  }
  
  /* Como se dijo anteriormente este método abre una conexión 
   * con la base de datos donde obtenemos todas las tareas y 
   * la insertamos en la lista con ayuda de los ArrayAdapter, 
   * esta vez utilizamos el SimpleCursorAdapter(<contexto>,
   * <layout>,<cursor>,<origen>,<destino>) 
   * ya que solo tenemos que pasarle por parámetro el contexto, el 
   * xml auxiliar que nos va a definir el formato que va a tener el 
   * ítem de la lista, un cursor con la consulta realizada a la BD, 
   * la columna que queremos mostrar de la consulta realizada y por 
   * último el TextView dentro del xml formato_lista que es donde se
   * va a mostrar la tarea. El adaptador se encarga de posicionar en 
   * cada TextView de la lista un elemento de la columna de origen
   */

  public void actualizarlista()
  {
    Tareas bdatos = new Tareas(this);
    Cursor cursor = bdatos.ver();   
     
    String[] origen = new String[] {"nombretarea"};
    int[] destino = new int[]{R.id.tarea};
    
    ListAdapter adaptador = new SimpleCursorAdapter(this, R.layout.formato_lista,cursor,origen,destino);
    ListView lista = (ListView) findViewById(R.id.lista);
    lista.setAdapter(adaptador);
    bdatos.cerrarSqlite();
  }
  
  /* Al hacer clic sobre el botón Hecho de cualquier ítem 
   * de la lista borramos de la Base de Datos la tarea 
   * correspondiente, para esto capturamos en que ítem fue 
   * realizada la acción para después capturar el TextView 
   * correspondiente y obtener la tarea realizada para poder 
   * pasárselo por parámetro al método BorrarTarea que se creó 
   * en la clase Tareas. Por ultimo actualizamos la lista para 
   * poder observar los cambios.
   */

  public void onHecho(View v)
  {
      View vista = (View) v.getParent();
      TextView tarea = (TextView) vista.findViewById(R.id.tarea);
      String starea = tarea.getText().toString();

      
      Tareas bdatos=new Tareas(this);
      bdatos.BorrarTarea(starea);
      actualizarlista();   
      
  }
}

Espero les resulte de utilidad este artículo, puesto que como pueden ver, a la hora de crear una App es muy común tener que aplicar varios contenidos aprendidos o investigar nuevos.

Cualquier duda o sugerencia, no dudes en dejar tu comentario. Puedes descargar el demo de este ejemplo a continuación.

Descargar Demo