Acceder a base de datos desde aplicación Android

Hoy voy a explicar cómo se incluye una base de datos SQLite en una aplicación Android como se accede a la BBDD y como se muestra la información por pantalla en una lista y como se muestra mostrar el detalle.

Primero vamos a descargar SQLite DataBase Browser, que es una aplicación que nos permitirá gestionar una base de datos SQLite. Instalamos la aplicación y ejecutamos la aplicación.

Desde la pantalla se debe pulsar el botón New Database, que abrirá el explorador de archivos donde podremos indicar el directorio donde queremos guardar la base de datos y el nombre de la base de datos, que en nuestro caso será “TareasDB“.

SQLiteBrowser01

 

Al pulsar aceptar, se nos muestra una nueva ventana para definir la tabla que vamos a utilizar. El nombre de la tabla que crearemos será “Tareas” que tendrá los siguientes atributos:

  • due
  • deleted
  • completed
  • notes
  • iddb
  • id
  • title
  • etag

SQLiteBrowser02

Una vez hecho esto vamos a insertar 3 registros para visualizarlos desde nuestra aplicación:


insert into Tareas (id, title, notes) values ('1', 'Crear lista', 'Crear una nueva lista con los datos');
insert into Tareas (id, title, notes) values ('2', 'Refrescar detalle', 'Refrescar el detalle al pulsar sobre un nuevo valor de la lista');
insert into Tareas (id, title, notes) values ('3', 'Insertar nuevo valor', 'Crear botón para insertar un nuevo valor');

Una vez ejecutado el script sql se confirman los datos pulsando el botón “Write Changes”:

SQLiteBrowser03

Ahora guardamos los cambios y abrimos el Android Studio para crear la aplicación Tutoriales y el nuevo módulo basedatos01. El package name lo informaremos con el valor com.tutoriales.basedatos01 y la versión mínima del SDK será la 16.

El nuevo módulo lo crearemos utilizando la plantilla “Master/Detail Flow”. Cambiamos el valor del Object Kind informado con el valor Tarea y el de Object Kind Plural informado con el valor Tareas.

Con esta plantilla se generan por defecto los layouts y las clases necesarias para consultar un maestro detalle cargado desde valores introducidos en el código. Nuestro trabajo será modificar todo aquello que sea necesario para el tratamiento de la base de datos.

Lo primero será crear un directorio assets dentro del directorio src/main donde copiaremos el fichero TareasDB creado desde el SQLiteBrowser.

AndroidStudio01

Dentro del package com.tutoriales.basedatos01 crearemos los siguientes directorios:

  • bbdd (contiene el manejador de la base de datos).
  • model (contiene la representación de la tabla tareas).
  • service (actuará como capa para recuperar la información de la BBDD).

A la izquierda se puede ver la estructura de directorios junto a las clases por defecto que se han creado.

Se puede ver que el directorio DummyContent no está porque lo he eliminado ya que la clase DummyContent se reemplazará por las clases Tareas y TareasContent.

Creamos la clase com.tutoriales.basedatos01.bbdd.BaseDatosHelper que extenderá de la clase SQLiteOpenHelper. Esta clase se ha creado a partir de las entradas que compartió Nomedhel en su blog blog.findemor.es.
<pre>


public class BaseDatosHelper extends SQLiteOpenHelper

La clase se puede dividir en dos partes, en la primera se verificaría si hay que guardar el fichero TareasDB en el dispositivo móvil y se abriría la base. En la segunda parte se tratarían las consultas en la BBDD.

Para empezar todas las BBDD de las aplicaciones se almacenan en una ruta especifica dentro del dispositivo que sería:

/data/data/[aplicación, en nuestro caso, com.tutoriales.basedatos01]/databases/

Ahora mostraré le código de la primera parte de la clase en la que se verifica si existe nuestro fichero TareasDB en el directorio apropiado y en caso contrario se copia. Una vez tengamos el fichero se abrirá la BBDD en modo lectura / escritura (SQLiteDatabase.OPEN_READWRITE).


// La carpeta por defecto donde Android espera encontrar la Base de Datos de
// tu aplicacion
private static String DB_PATH = "/data/data/com.tutoriales.basedatos01/databases/";
private static String DB_NAME = "TareasDB";
private SQLiteDatabase myDataBase;

private final Context myContext;

/**
* Constructor
*
* Guarda una referencia al contexto para acceder a la carpeta assets de la
* aplicacion y a los recursos
*
* @param contexto
**/
public BaseDatosHelper(Context contexto) {
super(contexto, DB_NAME, null, 1);
this.myContext = contexto;
}

/**
* Crea una base de datos vacia en el sistema y la sobreescribe con la que
* hemos puesto en Assets
**/
public void crearDataBase() throws IOException {

boolean dbExist = comprobarBaseDatos();

if (dbExist) {
// Si ya existe no hacemos nada
} else {
// Si no existe, creamos una nueva Base de datos en la carpeta por
// defecto de nuestra aplicacion,
// de esta forma el Sistema nos permitira sobreescribirla con la que
// tenemos en la carpeta Assets
this.getReadableDatabase();
try {
copiarBaseDatos();
} catch (IOException e) {
throw new Error("Error al copiar la Base de Datos");
}
}
}

/**
* Comprobamos si la base de datos existe
*
* @return true si existe, false en otro caso
**/
private boolean comprobarBaseDatos() {
SQLiteDatabase checkDB = null;
try {
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);
} catch (SQLiteException e) {
// No existe
}

if (checkDB != null) {
checkDB.close();
}

return checkDB != null ? true : false;
}

/**
* Copia la base de datos desde la carpeta Assets sobre la base de datos
* vacia recien creada en la carpeta del sistema, desde donde es accesible
**/
private void copiarBaseDatos() throws IOException {

// Abrimos la BBDD de la carpeta Assets como un InputStream
InputStream myInput = myContext.getAssets().open(DB_NAME);

// Carpeta de destino (donde hemos creado la BBDD vacia)
String outFileName = DB_PATH + DB_NAME;

// Abrimos la BBDD vacia como OutputStream
OutputStream myOutput = new FileOutputStream(outFileName);

// Transfiere los Bytes entre el Stream de entrada y el de Salida
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}

// Cerramos los ficheros abiertos
myOutput.flush();
myOutput.close();
myInput.close();
}

/**
* Abre la base de datos
**/
public void abrirBaseDatos() throws SQLException {
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);

}

/**
* Cierra la base de datos
**/
@Override
public synchronized void close() {
if (myDataBase != null)
myDataBase.close();

super.close();
}

@Override
public void onCreate(SQLiteDatabase db) {
// No usamos este metodo
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// No usamos este metodo
}

En esta segunda parte, se define el nombre de las tablas y sus atributos. Se hace así para evitar afectaciones por cambios en el modelo.

Una vez definido el modelo se crea el método getTareas que recuperará los datos que contenga la tabla Tareas.


// Podemos anyadir metodos publicos que accedan al contenido de la base de
// datos,
// para realizar consultas, u operaciones CRUD (create, read, update,
// delete)

private final String TABLE_TAREAS = "Tareas";
private final String TABLE_KEY_IDDB = "iddb";
private final String TABLE_KEY_ID = "id";
private final String TABLE_KEY_TITLE = "title";
private final String TABLE_KEY_ETAG = "etag";
private final String TABLE_KEY_COMPLETED = "completed";
private final String TABLE_KEY_DELETED = "deleted";
private final String TABLE_KEY_DUE = "due";
private final String TABLE_KEY_NOTES = "notes";
/*
* Obtiene todas las tareas desde la Base de Datos
*/
public ArrayList<Tarea> getTareas() {
ArrayList<Tarea> listaTareas = new ArrayList<Tarea>();
try {
Cursor c = myDataBase.query(TABLE_TAREAS, new String[] {
TABLE_KEY_IDDB, TABLE_KEY_ID, TABLE_KEY_TITLE,
TABLE_KEY_COMPLETED,TABLE_KEY_DELETED, TABLE_KEY_DUE,
TABLE_KEY_ETAG, TABLE_KEY_NOTES}, null,
null, null, null, null);

// Iteramos a traves de los registros del cursor
c.moveToFirst();
while (c.isAfterLast() == false) {
Tarea tarea = new Tarea();
tarea.setId(c.getString(1));
tarea.setTitle(c.getString(2));
tarea.setEtag(c.getString(6));
tarea.setNotes(c.getString(7));
listaTareas.add(tarea);
c.moveToNext();
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}

return listaTareas;
}

Creamos la clase com.tutoriales.basedatos01.model.Tarea que será un POJO con la estructura de la tabla Tareas. Se sobreescribirá el método toString para que devuelve el atributo title. Esto se hace así para que sea este valor el que se muestre en la lista de tareas de la aplicación.

package com.tutoriales.basedatos01.model;
/**
 * Created by dcrespi on 20/05/13.
 */
public class Tarea {

 private String iddb= "";
 private String id = "";
 private String title = "";
 private String completed = "";
 private int deleted = 0;
 private String due = "";
 private String etag = "";
 private String notes = "";

 public Tarea(){

 }

 public Tarea(String iddb, String id, String title, String completed, int deleted, String due, String etag, String notes){
 this.iddb = iddb;
 this.id = id;
 this.title = title;
 this.completed=completed;
 this.deleted=deleted;
 this.due = due;
 this.etag = etag;
 this.notes = notes;
 }

 public String getIddb() {
 return iddb;
 }

 public String getId() {
 return id;
 }

 public String getTitle() {
 return title;
 }

 public String getEtag() {
 return etag;
 }

 public void setIddb(String iddb) {
 this.iddb = iddb;
 }

 public void setId(String id) {
 this.id = id;
 }

 public void setTitle(String title) {
 this.title= title;
 }

 public void setEtag(String etag) {
 this.etag = etag;
 }

 public String getNotes() {
 return notes;
 }

 public void setNotes(String notes) {
 this.notes = notes;
 }

 public String getCompleted() {
 return completed;
 }

 public void setCompleted(String completed) {
 this.completed = completed;
 }

 public int getDeleted() {
 return deleted;
 }

 public void setDeleted(int deleted) {
 this.deleted = deleted;
 }

 public String getDue() {
 return due;
 }

 public void setDue(String due) {
 this.due = due;
 }

 @Override
 public String toString() {
 return title;
 }
}

Creamos la clase com.tutoriales.basedatos01.TareasContent para que haga la llamada que recupere los valores de las tareas de la base de datos y los cargue en los objetos ITEMS e ITEM_MAP. Para el propósito actual utilizaré el constructor para hacer la llamada a BaseDatosHelper.getTareas(), aunque no es lo más apropiado.

package com.tutoriales.basedatos01.service;

import android.app.Activity;

import com.tutoriales.basedatos01.bbdd.BaseDatosHelper;
import com.tutoriales.basedatos01.model.Tarea;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.IOException;

/**
 * Helper class for providing sample content for user interfaces created by
 * Android template wizards.
 * <p>
 */
public class TareasContent {

 BaseDatosHelper miBBDDHelper;

 public void crearBBDD(Activity activity) {
 miBBDDHelper = new BaseDatosHelper(activity);
 try {
 miBBDDHelper.crearDataBase();
 } catch (IOException ioe) {
 throw new Error("Unable to create database");
 }
 }
 /**
 * An array of sample (dummy) items.
 */
 public static List<Tarea> ITEMS = new ArrayList<Tarea>();

 /**
 * A map of sample (dummy) items, by ID.
 */
 public static Map<String, Tarea> ITEM_MAP = new HashMap<String, Tarea>();

 public TareasContent (Activity activity){
 crearBBDD(activity);
 miBBDDHelper.abrirBaseDatos();
 ITEMS = miBBDDHelper.getTareas();

 for (Tarea tarea : ITEMS) {
 ITEM_MAP.put(tarea.getId(), tarea);
 }
 }
}

La actividad principal es TareaListActivity, que se encargará de mostrar la lista de Tareas que hemos cargado en la tabla de Base de datos. La clase está comentada para que se pueda saber que hace su código.
Mediante el callback sabemos si se va a mostrar la información en una única pantalla con dos fragments (tablet) o si será una lista donde al seleccionarla se mostrará una nueva ventana con el detalle (smartphone).

package com.tutoriales.basedatos01;

import android.app.Activity;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.tutoriales.basedatos01.model.Tarea;
import com.tutoriales.basedatos01.service.TareasContent;

/**
 * Un ListFragment representando una lista de Tareas. Este fragment
 * también soporta los dispositivos tablets permitiendo listas de elementos al tener un
 * estado 'activated' de la seleccion. This helps indicate which item is
 * currently being viewed in a {@link TareaDetailFragment}.
 * <p>
 * Los Activities que contienen este Fragment DEBEN implementar el {@link Callbacks}
 * interface.
 */
public class TareaListFragment extends ListFragment {

 TareasContent tareasContent;
 /**
 * La serializacion (saved instance state) Bundle key representa la
 * posicion del elemento activado. Solo usado en tablets.
 */
 private static final String STATE_ACTIVATED_POSITION = "activated_position";

 /**
 * El actual objeto callback del Fragment, que se notifica los elemntos de lista
 * clickados.
 */
 private Callbacks mCallbacks = sDummyCallbacks;

 /**
 * La posicion del elemento actualmente activado. Solo usado por tablets.
 */
 private int mActivatedPosition = ListView.INVALID_POSITION;

 /**
 * Una callback interface que todos los activities contienen este fragment deben
 * implementar. Este mecanismo permite que los activities sean notificados de las selecciones
 * de elementos.
 */
 public interface Callbacks {
 /**
 * Callback para cuando un elemento se seleccione.
 */
 public void onItemSelected(String id);
 }

 /**
 * Una implementacion por defecto de {@link Callbacks} interface que no hace
 * nada. Usado solo cuando este fragment no tenga asociada un activity.
 */
 private static Callbacks sDummyCallbacks = new Callbacks() {
 @Override
 public void onItemSelected(String id) {
 }
 };

 /**
 * Constructor vacio obligatorio para el gestor de fragments para crear instancias del
 * fragment (e.g. upon screen orientation changes).
 */
 public TareaListFragment() {
 }

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);

 tareasContent = new TareasContent(getActivity());

 setListAdapter(new ArrayAdapter<Tarea>(
 getActivity(),
 android.R.layout.simple_list_item_activated_1,
 android.R.id.text1,
 tareasContent.ITEMS));
 }

 @Override
 public void onViewCreated(View view, Bundle savedInstanceState) {
 super.onViewCreated(view, savedInstanceState);

 // Restaura la posicion del elemento previamente serializado activado.
 if (savedInstanceState != null
 && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
 setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
 }
 }

 @Override
 public void onAttach(Activity activity) {
 super.onAttach(activity);

 // Activities debe contener este codigo para implementar el callbacks.
 if (!(activity instanceof Callbacks)) {
 throw new IllegalStateException("Activity must implement fragment's callbacks.");
 }

 mCallbacks = (Callbacks) activity;
 }

 @Override
 public void onDetach() {
 super.onDetach();

 // Reinicia callbacks interface activo para la implementacion ficticia.
 mCallbacks = sDummyCallbacks;
 }

 @Override
 public void onListItemClick(ListView listView, View view, int position, long id) {
 super.onListItemClick(listView, view, position, id);

 // Notifica al callbacks interface activo (la activity, si el fragment es incluido a uno)
 // que un articulo ha sido seleccionado.
 mCallbacks.onItemSelected(tareasContent.ITEMS.get(position).getId());
 }

 @Override
 public void onSaveInstanceState(Bundle outState) {
 super.onSaveInstanceState(outState);
 if (mActivatedPosition != ListView.INVALID_POSITION) {
 // Serializa y persiste la posicion del elemento activado.
 outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
 }
 }

 /**
 * Activa el modo activate-on-click. Cuando este modo esta activo, la lista de elmentos
 * se le dara el estado 'activated' cuando se toca.
 */
 public void setActivateOnItemClick(boolean activateOnItemClick) {
 // Al establecer CHOICE_MODE_SINGLE, ListView dara automaticamente a los elementos
 // el estado 'activated' al tocarse.
 getListView().setChoiceMode(activateOnItemClick
 ? ListView.CHOICE_MODE_SINGLE
 : ListView.CHOICE_MODE_NONE);
 }

 private void setActivatedPosition(int position) {
 if (position == ListView.INVALID_POSITION) {
 getListView().setItemChecked(mActivatedPosition, false);
 } else {
 getListView().setItemChecked(position, true);
 }

 mActivatedPosition = position;
 }
}

La clase TareaDetailActivity muestra el detalle de la tarea seleccionada.

package com.tutoriales.basedatos01;

import android.content.Intent;
import android.os.Bundle;
import android.app.Activity;
import android.view.MenuItem;

/**
 * Un activity representando la ventana del detalle de una tarea. Este
 * activity solo se usa en dispositivos telefonicos. En dispositivos del tamanyo tablet,
 * los detalles del elemento son representados al lado de la lissta de elementos
 * en el {@link TareaListActivity}.
 * <p>
 * Este activity es mas que nada un activity 'shell' que no contiene nada
 * mas que un {@link TareaDetailFragment}.
 */
public class TareaDetailActivity extends Activity {

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

 // Mostrar el boton Up de la barra de accion
 getActionBar().setDisplayHomeAsUpEnabled(true);

 // savedInstanceState no es nulo cuando hay un estado salvado de ese fragment
 // por configuraciones anteriores del activity
 // (e.g. cuando se rota la ventana de vertical a apaisado).
 // En este caso, el fragment se volvera a agregar automaticamente
 // en el contenedor por lo que na sera necesario anyadirlo manualmente.
 // Para mas informacion, ver el Fragments API guide en:
 //
 // http://developer.android.com/guide/components/fragments.html
 //
 if (savedInstanceState == null) {
 // Crea el fragment de detalle y lo anya al activity
 // usando una transaccion fragment.
 Bundle arguments = new Bundle();
 arguments.putString(TareaDetailFragment.ARG_TASK_ID,
 getIntent().getStringExtra(TareaDetailFragment.ARG_TASK_ID));
 TareaDetailFragment fragment = new TareaDetailFragment();
 fragment.setArguments(arguments);
 getFragmentManager().beginTransaction()
 .add(R.id.tarea_detail_container, fragment)
 .commit();
 }
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
 int id = item.getItemId();
 if (id == android.R.id.home) {
 // Este ID representa el Home o el boton Up. En el caso de este
 // activity, el boton Up se visualizara. Para
 // mas detalles, ver el Navigation pattern on Android Design:
 //
 // http://developer.android.com/design/patterns/navigation.html#up-vs-back
 //
 navigateUpTo(new Intent(this, TareaListActivity.class));
 return true;
 }
 return super.onOptionsItemSelected(item);
 }
}

Las clases TareaDetailFragment y TareaListFragment se utilizarán para tratar el contenido de los fragments.

TareaListFragment:


package com.tutoriales.basedatos01;

import android.app.Activity;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.tutoriales.basedatos01.model.Tarea;
import com.tutoriales.basedatos01.service.TareasContent;

/**
* Un ListFragment representando una lista de Tareas. Este fragment
* también soporta los dispositivos tablets permitiendo listas de elementos al tener un
* estado 'activated' de la seleccion. This helps indicate which item is
* currently being viewed in a {@link TareaDetailFragment}.
* <p>
* Los Activities que contienen este Fragment DEBEN implementar el {@link Callbacks}
* interface.
*/
public class TareaListFragment extends ListFragment {

TareasContent tareasContent;
/**
* La serializacion (saved instance state) Bundle key representa la
* posicion del elemento activado. Solo usado en tablets.
*/
private static final String STATE_ACTIVATED_POSITION = "activated_position";

/**
* El actual objeto callback del Fragment, que se notifica los elemntos de lista
* clickados.
*/
private Callbacks mCallbacks = sDummyCallbacks;

/**
* La posicion del elemento actualmente activado. Solo usado por tablets.
*/
private int mActivatedPosition = ListView.INVALID_POSITION;

/**
* Una callback interface que todos los activities contienen este fragment deben
* implementar. Este mecanismo permite que los activities sean notificados de las selecciones
* de elementos.
*/
public interface Callbacks {
/**
* Callback para cuando un elemento se seleccione.
*/
public void onItemSelected(String id);
}

/**
* Una implementacion por defecto de {@link Callbacks} interface que no hace
* nada. Usado solo cuando este fragment no tenga asociada un activity.
*/
private static Callbacks sDummyCallbacks = new Callbacks() {
@Override
public void onItemSelected(String id) {
}
};

/**
* Constructor vacio obligatorio para el gestor de fragments para crear instancias del
* fragment (e.g. upon screen orientation changes).
*/
public TareaListFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

tareasContent = new TareasContent(getActivity());

setListAdapter(new ArrayAdapter<Tarea>(
getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1,
tareasContent.ITEMS));
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

// Restaura la posicion del elemento previamente serializado activado.
if (savedInstanceState != null
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
}
}

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);

// Activities debe contener este codigo para implementar el callbacks.
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}

mCallbacks = (Callbacks) activity;
}

@Override
public void onDetach() {
super.onDetach();

// Reinicia callbacks interface activo para la implementacion ficticia.
mCallbacks = sDummyCallbacks;
}

@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);

// Notifica al callbacks interface activo (la activity, si el fragment es incluido a uno)
// que un articulo ha sido seleccionado.
mCallbacks.onItemSelected(tareasContent.ITEMS.get(position).getId());
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serializa y persiste la posicion del elemento activado.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}

/**
* Activa el modo activate-on-click. Cuando este modo esta activo, la lista de elmentos
* se le dara el estado 'activated' cuando se toca.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
// Al establecer CHOICE_MODE_SINGLE, ListView dara automaticamente a los elementos
// el estado 'activated' al tocarse.
getListView().setChoiceMode(activateOnItemClick
? ListView.CHOICE_MODE_SINGLE
: ListView.CHOICE_MODE_NONE);
}

private void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
}

mActivatedPosition = position;
}
}

TareaDetailFragment


package com.tutoriales.basedatos01;

import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.tutoriales.basedatos01.service.TareasContent;
import com.tutoriales.basedatos01.model.Tarea;

/**
* Un fragment representando una sola ventana de detalle de Tarea.
* Este fragment estara contenido en una {@link TareaListActivity}
* para modo de dos paneles (en tablets) o en una {@link TareaDetailActivity}
* para telefonos.
*/
public class TareaDetailFragment extends Fragment {
/**
* Representa el argumento de identificador de tarea.
*/
public static final String ARG_TASK_ID = "task_id";

/**
* El contenido de la tarea que esta representado.
*/
private Tarea mItem;

/**
* El constructor vacio obligatorio para el controlador fragment para instanciar el
* fragment (e.g. upon screen orientation changes).
*/
public TareaDetailFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (getArguments().containsKey(ARG_TASK_ID)) {
// Carga el conteido de la tarea especificado por el argumento.
// En un escenario en el mundo real, usar un Loader
// para cargar el contenido del proveedor de contenido.
mItem = TareasContent.ITEM_MAP.get(getArguments().getString(ARG_TASK_ID));
}

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_tarea_detail, container, false);

// Visualizar el contenido de la tarea.
if (mItem != null) {
((TextView) rootView.findViewById(R.id.tarea_notes)).setText(mItem.getNotes());
((TextView) rootView.findViewById(R.id.tarea_title)).setText(mItem.getTitle());
}

return rootView;
}
}

Tendremos cuatro layouts.
El activity_tarea_twopane.xml, representa una lista junto a otro layout para el detalle.

<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:layout_marginLeft="16dp"
 android:layout_marginRight="16dp"
 android:baselineAligned="false"
 android:divider="?android:attr/dividerHorizontal"
 android:orientation="horizontal"
 android:showDividers="middle"
 tools:context=".TareaListActivity">

 <!--
 This layout is a two-pane layout for the Tareas
 master/detail flow. See res/values-large/refs.xml and
 res/values-sw600dp/refs.xml for an example of layout aliases
 that replace the single-pane version of the layout with
 this two-pane version.

 For more on layout aliases, see:
 http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters
 -->

 <fragment
 android:id="@+id/tarea_list"
 android:name="com.tutoriales.basedatos01.TareaListFragment"
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="1"
 tools:layout="@android:layout/list_content" />

 <FrameLayout
 android:id="@+id/tarea_detail_container"
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="3" />

</LinearLayout>
El activity_tarea_list.xml, representa una lista para smartphone.
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/tarea_list"
 android:name="com.tutoriales.basedatos01.TareaListFragment"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_marginLeft="16dp"
 android:layout_marginRight="16dp"
 tools:context=".TareaListActivity"
 tools:layout="@android:layout/list_content" />
El activity_tarea_detail.xml representa el frame para el detalle.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/tarea_detail_container"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".TareaDetailActivity"
 tools:ignore="MergeRootFrame" />
El fragment_tarea_detal.xml representa los elementos que se visualizaran en el detalle. En este caso dos elementos de texto, uno para el título y otro para las notas.
<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"
 tools:context=".TareaDetailFragment"
 android:id="@+id/layoutVertical"
 android:orientation="vertical"
 >

<TextView
 android:id="@+id/tarea_title"
 style="?android:attr/textAppearanceLarge"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:padding="16dp"
 android:textIsSelectable="true"
 />

<TextView
 android:id="@+id/tarea_notes"
 style="?android:attr/textAppearanceLarge"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:padding="16dp"
 android:textIsSelectable="true"
 />
</LinearLayout>
El contenido de string.xml es el siguiente:
<?xml version="1.0" encoding="utf-8"?>
<resources>

 <string name="app_name">Tutoriales</string>
 <string name="title_tarea_detail">Tarea Detail</string>
</resources>
El styles.xml:
<resources>

 <!-- Base application theme. -->
 <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
 <!-- Customize your theme here. -->
 </style>

</resources>
Se genera el mismo refs.xml para tamaños grandes en los directorios values-large y values-sw600dp:
<resources>
 <!--
 Layout alias to replace the single-pane version of the layout with a
 two-pane version on Large screens.

 For more on layout aliases, see:
 http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters
 -->
 <item type="layout" name="activity_tarea_list">@layout/activity_tarea_twopane</item>
</resources>

ListaDetalleBD01ListaDetalleBD02

Espero que os sirva y os sea útil.

img_ccTodo el material publicado en este Blog, salvo las obras que no pertenecen a su autor, se difunden bajo licencia CC by-SA de Creative Commons, por lo que eres libre de copiar, distribuir y comunicar este contenido de forma publica, hacer un uso comercial del mismo, etc., siempre que lo hagas bajo las condiciones de la licencia indicada, y que reconozcas a su autor e indiques un enlace al contenido original o en su defecto a la pagina principal de este blog.

Anuncios
    • SEBASTIAN SAEZ MANSILLA
    • 30/10/14

    Podrías subir el código completo del proyecto?
    tengo algunas dudas de cómo acceder de la ventana principal a todos los datos obtenidos de la base de datos

  1. 15/09/14

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: