Búsqueda personalizada

Blog personal para aprender a programar en Android desde cero.

jueves, 22 de abril de 2010

Notepad Tutorial - Ejercicio 2

Ejercicio 2

En este ejercicio añadiremos una segunda Activity para crear, editar y eliminar notas.






Paso 1

Abrimos el proyecto Notepadv2 en Eclipse. El punto de inicio de este proyecto es la solución del ejercicio 1:

  1. File > New > Android Project
  2. Seleccionamos Create project from existing source
  3. Seleccionamos el directorio Notepadv2 que nos hemos descargado antes
  4. La mayoría de propiedades del proyecto se rellenan automáticamente. Seleccionamos el Target, en Android Labs nos recomiendan seleccionar la versión del sdk menor para que sea compatible con la mayor cantidad de dispositivos.
  5. Pulsamos en Finish

Como veis son los mismo pasos que para el ejercicio 1.


Paso 2


En primer lugar, vamos a crear el menú contextual que permitirá a los usuarios borrar las notas una a una. Miramos la clase Notepadv2.
Para que cada elemento de la lista en el ListView sea accesible desde el menú de contexto, llamamos al método registerForContextMenu() pasándole el ListView:

                registerForContextMenu (getListView ());

Como nuestra Activity hereda de la clase ListActivity, getListView()nos devuelve el objeto local ListView. Ahora, cada elemento del ListView activa el menú contextual.


Ahora completamos el método onCreateContextMenu(), que será similar al menú de opciones. Añadiremos una línea para poder eliminar una nota desde el menú:


        public boolean onCreateContextMenu(Menu menu, View v
                ContextMenuInfo menuInfo) {
                        super.onCreateContextMenu(menu, v, menuInfo);
                        menu.add(0, DELETE_ID, 0, R.string.menu_delete);
        }



Paso 3

Ahora tenemos que controlar lo que hacemos cuando seleccionamos la opción del menú contextual. El método que tenemos que implementar es:

             public boolean onContextItemSelected(MenuItem item) {
                      switch(item.getItemId()) {
                           case DELETE_ID:
                                 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
                                 mDbHelper.deleteNote(info.id);
                                 fillData();
                                 return true;
                      }
                      return super.onContextItemSelected(item);
              }

Para saber que ítem tenemos que eliminar lo hacemos con un objeto de tipo AdapterContextMenuInfo, que obtenemos con el método getMenuInfo().



Paso 4

Todavía en la clase Notepadv2 creamos el siguiente método:

        private void createNote() {

             Intent i = new Intent(this, NoteEdit.class);
             startActivityForResult(i, ACTIVITY_CREATE);
        }


El método startActivityForResult() lanza el Intent devolvuendo un valor, en este caso un int.



Paso 5

Rellenamos el método:

         @Override
            protected void onListItemClick(ListView l, View v, int position, long id) {
               super.onListItemClick(l, v, position, id);
                  Cursor c = mNotesCursor;
                  c.moveToPosition(position);
                  Intent i = new Intent(this, NoteEdit.class);
                  i.putExtra(NotesDbAdapter.KEY_ROWID, id);
                  i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
                              c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
                  i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
                      c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
                  startActivityForResult(i, ACTIVITY_EDIT);
            }


Este método se invoca cada vez que seleccionamos un elemento de la lista. Con Intent.putExtra() añadimos la información de la nota, para así poderla editar más tarde.




Paso 6

Los métodos anteriores createNote() y onListItemClick() se llaman de manera asíncrona. Para controlar el retorno de la llamada lo haremos sobreescribiendo el método onActivityResult(). Esto lo podemos hacer ya que hemos lanzado el Intent con startActivityForResult().


         @Override
           protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
                   super.onActivityResult(requestCode, resultCode, intent);
                   Bundle extras = intent.getExtras();
                   switch(requestCode) {
                         case ACTIVITY_CREATE:
                                 String title = extras.getString(NotesDbAdapter.KEY_TITLE);
                                 String body = extras.getString(NotesDbAdapter.KEY_BODY);
                                 mDbHelper.createNote(title, body);
                                 fillData();
                                 break;
                         case ACTIVITY_EDIT:
                                 Long rowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
                                 if (rowId != null) {
                                       String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
                                       String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
                                       mDbHelper.updateNote(rowId, editTitle, editBody);
                                 }
                                 fillData();
                                 break;
                         }
                   }
           }


Diferenciamos entre la modificación o creación de una nota. En la creación recogemos del Bundle el title y el body, y llamamos a mDbHelper.createNote(). En la modificación primero tenemos que conseguir el Id de la fila, después también conseguimos el title y el body del Bundle y llamamos a mDbHelper.updateNote().




Paso 7

En este paso simplemente echamos un vistazo al fichero note_edit.xml. Es el layout más complejo del ejercicio pero únicamente vemos que de nuevo sólo está layout_weight=1. Este parámetro nos indica la "importancia" o peso del elemento en la vista. Por defecto el valor es 0.
Este es el layout de la pantalla de edición de una nota.


Paso 8

Para poder la pantalla de edición de las notas, creamos un clase que llamamos NoteEdit que herede de Activity y sobreescribimos el método onCreate(Bundle):


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.note_edit);
    
        mTitleText = (EditText) findViewById(R.id.title);
        mBodyText = (EditText) findViewById(R.id.body);
    
        Button confirmButton = (Button) findViewById(R.id.confirm);
    
        mRowId = null;
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            String title = extras.getString(NotesDbAdapter.KEY_TITLE);
            String body = extras.getString(NotesDbAdapter.KEY_BODY);
            mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
        
            if (title != null) {
                mTitleText.setText(title);
            }
            if (body != null) {
                mBodyText.setText(body);
            }
        }
    
        confirmButton.setOnClickListener(new View.OnClickListener() {


            public void onClick(View view) {
                // TODO
            }
          
        });
    }


Paso 9

Ahora completamos el método onClick() que nos faltaba por rellenar:

            public void onClick(View view) {
                Bundle bundle = new Bundle();
            
                bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
                bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
                if (mRowId != null) {
                    bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
                }
              
                Intent mIntent = new Intent();
                mIntent.putExtras(bundle);
                setResult(RESULT_OK, mIntent);
                finish();
            }



Paso 10

El último paso es añadir nuestra Activity al AndroidManifest.xml para que la Activity sea visible para Android:








  1. Doble click en el fichero AndroidManifest.xml para abrirlo.
  2. Click en la pestaña Application del editor de Manifest.
  3. En la sección Application Nodes pulsamos en Add...
  4. Nos aseguramos que tenemos seleccionado Activity y click en OK.
  5. De nuevo en la sección Application Nodes pulsamos en la nueva Activity, y a la derecha escribimos .NoteEdit en el campo Name*. Pulsamos Intro.


También podemos añadirlo manualmente en el fichero AndroidManifest.xml de la siguiente manera:

          <activity android:name=".NoteEdit"></activity>



Y a correr!



















































martes, 23 de marzo de 2010

Notepad Tutorial - Ejercicio 1

En Android Labs nos proponen un tutorial bastante completo, Notepad Tutorial, un gestor de notas.

El tutorial se sigue en forma de ejercicios, 3 más uno extra. De cada ejercicio tenemos un código base y su solución por si tenemos algún problema.

Nos descargamos el código base y las soluciones, y nos preparamos el entorno para empezar con el ejercicio 1.


Ejercicio 1

En este ejercicio 1 construiremos una lista de notas a la que podremos añadir nuevas notas, pero no editaralas.


Paso 1

Abrimos el proyecto Notepadv1 en Eclipse. Este proyecto es el punto de inicio para el ejercicio 1.

  1. File > New > Android Project
  2. Seleccionamos Create project from existing source
  3. Seleccionamos el directorio Notepadv1 que nos hemos descargado antes
  4. La mayoría de propiedades del proyecto se rellenan automáticamente. Seleccionamos el Target, en Android Labs nos recomiendan seleccionar la versión del sdk menor para que sea compatible con la mayor cantidad de dispositivos.
  5. Pulsamos en Finish

Yo no he tenido problemas, pero si en vuestro caso tenéis algún problema con el AndroidManifest.xml, con el botón derecho en el proyecto seleccionamos Android Tools > Fix Project Properties, para que dejen de salirnos.


Paso 2

Echamos un vistazo a la clase NotesDbAdapter, que nos servira para acceder a la base de datos SQLite que utilizaremos para guardar las notas.

Al principio de la clase tenemos unas cuantas constantes declaradas:

    private static final String DATABASE_CREATE =
            "create table notes (_id integer primary key autoincrement, "
                    + "title text not null, body text not null);";

Esta es la consulta que crea la tabla de notas, que contiene 3 campos, _id, title y body, de los que también tenemos sus constantes:

  public static final String KEY_TITLE = "title";
    public static final String KEY_BODY = "body";
    public static final String KEY_ROWID = "_id";

También tenemos una constante que identifica la clase a la hora de escribir en log:

  private static final String TAG = "NotesDbAdapter";

El constructor de la clase NotesDbAdapter toma como parámetro un objeto de clase Context. Esto es para poder acceder a propiedades y métodos del sistema (Android).

En el método open() creamos un instancia de la clase DatabaseHelper, que es un clase local que implementa SQLiteOpenHelper. Con el método getWritableDatabase() obtenemos la base de datos.

Con el método close() simplemente cerramos la conexión con la base de datos.

Al método createNote() le pasamos un String title y otro body para crear una nueva nota en la base de datos. Si se crea bien, devolveremos un long que sera el _id del nuevo registro creado en la tabla.

Con deleteNote() podemos eliminar un registro de la tabla pasándole el _id.

Para obtener todos los registros de la tabla usaremos fetchAllNotes(). Este método nos retorna un Cursor (según Android Labs el sistema es más eficiente que con collections). Dentro del método llamamos a SQLiteDatabase.query() con los parámetros: nombre de la tabla (String), columnas que queremos recuperar (array de Strings, y otros campos que dejamos a null para obtener todos los datos sin agrupar, ni ordernar. (Ver detalles en SQLiteDatabase).

fetchNote() funciona de una forma similar que fetchAllNotes(), pero le pasamos un _id para obtener un registro determinado.

Para actualizar un nota utilizaremos updateNote(), al que le pasaremos _id de la nota que modificar, y los valores actualizados.


Paso 3

Abrimos el fichero res/layout/notepad_list.xml y vemos un LinearLayout vacío. Si alguien no identifica o conoce algo que se pase por HelloLinearLayout.


Paso 4
.
Copiamos el siguiente cógido en res/layout/notepad_list.xml para crear la vista del listado de notas

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ListView android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>
 </LinearLayout>


Es una vista bastante sencilla, en la que tenemos un ListView y un TextView. Suponiendo que ya conocemos un poco de vistas (si no, mirar HelloLinearLayout), lo importante son los id de estos dos elementos. Vemos que empiezan los dos por @android:id/, esto quiere decir que nos lo proporciona la plataforma Android. Pues usaremos 'automáticamente' @android:id/list cuando tengamos notas y @android:id/empty cuando no haya notas.


Paso 5

Para cada fila o item de la lista también deberemos generar una vista. Creamos un fichero nuevo en res/layout y lo llamamos notes_row.xml. El contenido será:

<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/text1"  
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

Podemos observar que en el id tenemos los carácteres @+. El + indica que los elementos que usan esta vista se generan dinámicamente y el id se creará automáticamente si todavía no existe.


Paso 6

Abrimos la clase Notepadv1 y le cambiamos la clase padre Activity por ListActivity. Esta clase añade funcionalidad extra para trabajar con listas.

Si miramos el código vemos que tenemos un atributo mNoteNumber que todavía no usamos, pero que haremos servir para numerar las notas.

También vemos que sobreescribimos tres métodos:

  1. onCreate: llamado cuando la Activity empieza. Una especie de main
  2. onCreateOptionsMenu: llamado cuando el usuario pulsa en botón de menu
  3. onOptionsItemSelected: cuando pulsamos en el botón de menú, normalmente lanzamos una lista de acciones, como por ejemplo "Crear Nota". Pues este método es llamado cuando el usuario pulsa en una opción de estas.

Paso 7

El método onCreate() debe quedarnos así:

@Override
         public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.notepad_list);
                mDbHelper = new NotesDbAdapter(this);
                mDbHelper.open();
                fillData();
         }

Después de las dos líneas típicas de un onCreate() (super.onCreate() y setContenView()), creamos la instancia del NotesDbAdapter, al que le pasamos el context. Abrimos la conexión y rellenamos los datos con fillData(), método que todavía no hemos implementado.


Paso 8

En el método onCreateOptionsMenu() copiamos:

@Override
         public boolean onCreateOptionsMenu(Menu menu) {
              boolean result = super.onCreateOptionsMenu(menu);
              menu.add(0, INSERT_ID, 0, R.string.menu_insert);
              return result;
         }

Para que el código compile debemos añadir en el res/value/strings.xml:

Add Item

Y añadimos en la clase Notepadv1 la siguiente constante:

public static final int INSERT_ID = Menu.FIRST;


De esta manera hemos añadido un botón ("Add Item") que aparecerá cuando pulsemos en el botón de menú del dispositivo.


Paso 9

En onOptionsItemSelected() copiamos:

@Override
        public boolean onOptionsItemSelected(MenuItem item) {
              switch (item.getItemId()) {
                 case INSERT_ID:
                 createNote();
                 return true;
              }
    
              return super.onOptionsItemSelected(item);
         }

Este método se ejecutará cuando pulsemos en un opción del menú. Recibe como parámetros un MenuItem, el cuál comprobamos si es la primera opción ("Add  Note") para crear una nota nueva con createNote(). Finalmente llamaremos al método de la superclase con super.onOptionsItemSelected().


Paso 10

Para este ejercicio 1 implementaremos un versión poco útil del método createNote(). Simplemente crearemos una nota nueva vacía asignándole un titulo incluyendo un contador. La nota no se podrá modificar de momento.

private void createNote() {
             String noteName = "Note " + mNoteNumber++;
             mDbHelper.createNote(noteName, "");
             fillData();
        }

Paso 11

Por último tenemos que implementar el método fillData(). El código es:

private void fillData() {
              Cursor c = mDbHelper.fetchAllNotes();
              startManagingCursor(c);


              String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
              int[] to = new int[] { R.id.text1 };
      
              SimpleCursorAdapter notes =
                    new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
              setListAdapter(notes);
        }

Para rellenar los datos usamos un SimpleCursorAdapter. Cuando creamos un objeto SimpleCursorAdapter le pasamos los siguientes parámetros:

  • this --> Contexto
  • int layout --> identificador del layout que contiene la lista de elementos a rellenar
  • Cursor --> Cursor de la base de datos
  • String[] --> Array de las columnas a rellenar
  • int[] --> Array de las vistas/layouts que mostrarán los datos


Para poder trabajar con el cursor, la clase Activity nos da el método startManagingCursor(Cursor c).

Paso 12

Ejecutamos la aplicación




lunes, 15 de marzo de 2010

Hacer capturas con Nexus One en Ubuntu


Por Internet existen muchas páginas con las instrucciones que hay que seguir para poder realizar capturas de pantalla, o debuggar, con tu teléfono. Pero después de seguir las instrucciones de varias páginas, yo no lo conseguía para mi Nexus One y Ubuntu.
Pero por fin he encontrado el cómo y voy a explicarlo (fuente).


  • En un terminal linux ejecutamos lsusb y obtendremos un pantalla del estilo a ésta:



  • Conectamos el N1 al ordenador y lo ponemos en modo Depuración USB. Ajustes -- Aplicaciones -- Desarrollo -- Depuración USB


  • En un terminal de linux volvemos a ejecutar lsusb y podremos que se ha añadido un dispositivo a la lista:
  • Ahora necesitamos crear una regla para nuestro dispositivo. Tenemos que crear un fichero en /etc/udev/rules.d/. Por ejemplo:
  • El contenido del fichero tiene que ser en mi caso (en negrita los valores que vemos con lsusb):
SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4e12", MODE="0666"


  • Le damos permisos de acceso y ejecución a ese fichero:
sudo chmod a+rx /etc/udev/rules.d/51.android.rules
  • Recargamos el servicio udev:
sudo service udev reload
  • Ahora si arrancamos el servicio adb vemos que no tenemos permisos (imágen de la fuente):
  • Ahora reiniciamos el servidor. Primero hacemos el kill-server y después (imágen de la fuente):
  • Por último listamos los dispositivos y veremos algo al estilo:

A partir de ahora podremos hacer capturas y debuggar desde Eclipse


O desde Dalvik Debug Monitor que ejecutamos con /tools/ddms: