Professional Documents
Culture Documents
Conclusiones
Descargar Cdigo
Apyanos con una seal en tu red social favorita y consigue el cdigo completo.
Me gusta
Tweet
+1 Google
1. Qu Es Un Web Service?
Un Web Service o Servicio Web es un aplicativo que facilita la interoperabilidad entre
varios sistemas independientemente del del lenguaje de programacin o plataforma en que
fueron desarrollados. Este debe tener una interfaz basada en un formato estndar entendible
por las maquinas como lo es XML o JSON.
Por ejemplo
Facebook es un aplicativo web construido con una determinada arquitectura y lenguajes de
programacin basados en el protocolo HTTP. Sin embargo podemos usar esta red social en
nuestro dispositivo Android.
Cmo es posible esto, si la aplicacin Android est construida con lenguaje Java?
A travs de un Web Service construido para gestionar todas aquellas operaciones sobre una
base de datos alojada en los servidores de Facebook. Quiere decir que ambos aplicativos
usan como puente la web para acceder a un solo repositorio de datos.
Como ves, un Web Service se crea con funcionalidades que permitan obtener datos
actualizados en tiempo real. El hecho de que sea dinmico incorpora el uso de un lenguaje
web para la gestin HTTP que en este caso ser Php.
2. Requerimientos De La Aplicacin
Como leste al inicio, la aplicacin I Wish gestiona las metas y sueos de los usuarios
permitindoles tener un registro completo. Bsicamente el alcance del proyecto se resumen
en:
Como usuario de I Wish, deseo mantener los datos de todas mis metas y sueos (se
refiere al CRUD).
Como usuario de I Wish, deseo ver el detalle de cada meta.
Como usuario de I Wish, deseo que cada tem tenga un ttulo, una descripcin, una
fecha lmite de cumplimiento, prioridad y categora. Las categoras posibles son:
Salud, Finanzas, Profesional y Espiritual.
Estos requerimientos no son nada del otro mundo. Bsicamente estas ante una situacin de
listas y detalles. Algo que ya has visto en artculos pasados con gran frecuencia.
El meollo del asunto se encuentra en el Web Service que debes crear con Php y Mysql para
el mantenimiento de los datos. Esta vez no usaremos caching para el soporte de los datos
locales como lo hicimos al crear el lector Rss. Nos enfocaremos en como usar Volley para
realizar las peticiones en el localhost.
3. Wireframing De La Aplicacin
A primera vista I Wish es una aplicacin que se basa en la funcionalidad bsica de un crud.
Tendremos una lista de los elementos que existen, podremos ver el detalle de cada uno,
modificar su contenido e incluso borrarlos.
Teniendo en cuenta este razonamiento, puedes imaginar la aplicacin en primera instancia
de la siguiente forma:
Despus de haber creado t proyecto en Android Studio vas a crear una actividad principal
que contengan un fragmento con una lista. Debido a que vamos a aadir los fragmentos
programticamente no es necesario enfocarnos tanto en los layouts de las actividades.
Incluso puedes usar un solo layout para todas las actividades.
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" />
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fab="http://schemas.android.com/apk/res-auto"
android:id="@+id/fragment_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/reciclador"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="3dp"
android:scrollbars="vertical" />
<com.melnykov.fab.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="@mipmap/ic_add"
fab:fab_colorNormal="@color/accent"
fab:fab_colorPressed="@color/primary"
fab:fab_colorRipple="@color/ripple" />
</RelativeLayout>
Otro aspecto a tener en cuenta es que los mipmaps o drawables que uses para el icono de un
FAB debe tener dimensiones de 24dp, para una buena experiencia de usuario:
El patrn anterior muestra un FAB grande para representar la insercin con unas
dimensiones reglamentarias de 56dpx56dp. El icono que lleva debe mantenerse en
24dpx24dp.
Tambin podemos tener un FAB mini con dimensiones de 40dpx40dp, donde el icono se
mantiene sobre 24dpx24dp.
android:layout_weight="50">
<ImageView
android:id="@+id/cabecera"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="30"
android:layout_marginBottom="28dp" />
<com.melnykov.fab.FloatingActionButton
android:id="@+id/fab"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_gravity="bottom|right"
android:src="@mipmap/ic_edit"
fab:fab_colorNormal="@color/colorNormalMini"
fab:fab_colorPressed="@color/colorPressedMini"
fab:fab_colorRipple="@color/colorRippleMini"
android:layout_marginLeft="16dp"
fab:fab_type="mini"
android:layout_marginBottom="8dp"/>
<TextView
android:id="@+id/titulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Titulo"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_marginBottom="48dp"
android:layout_toRightOf="@+id/fab"
android:layout_alignParentBottom="true"
android:layout_marginLeft="16dp"
android:textColor="@android:color/white" />
</RelativeLayout>
<TextView
android:id="@+id/categoria"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/categoria_label"
android:text="Categora"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/fecha"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/fecha_label"
android:text="Fecha"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/prioridad"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/prioridad_label"
android:text="Prioridad"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/descripcion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/descripcion_label"
android:text="Descripcin"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/descripcion_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Descripcin"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/fecha_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/descripcion"
android:text="Fecha Lmite"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/categoria_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/fecha"
android:text="Categora"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/prioridad_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/categoria"
android:text="Prioridad"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/black" />
</RelativeLayout>
</LinearLayout>
En este caso se us como nodo un LinearLayout con dos RelativeLayout dentro. Esto nos
permite dividir por pesos (weight) la ocupacin de espacio entre ambos layouts y as
mantener una proporcin.
Por ejemplo
El titulo de cada meta recibe texto escrito desde el input del dispositivo, por lo que sabemos
que elEditText es la solucin para este caso. La descripcin es igual, necesita un campo de
texto. La fecha limite puede ser obtenida a travs de un DatePicker y para la categora
que tiene un dominio de varias opciones, puedes usar un Spinner.
Veamos:
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.herprogramacion.iwish.ui.fragmentos.UpdateFragment">
<!-- Titulo-->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/titulo_input"
android:layout_alignParentTop="false"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:hint="Ttulo"
android:minLines="1"
android:maxLines="1"
android:maxLength="55"
android:phoneNumber="false"
android:singleLine="true"
android:paddingTop="16dp"
android:paddingBottom="16dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Fecha"
android:id="@+id/fecha_text"
android:layout_below="@+id/descripcion_input"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:paddingTop="16dp"
android:textColor="@android:color/black" />
android:id="@+id/categoria_texto"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/fecha_ejemplo_text"
android:paddingTop="16dp"
android:textColor="@android:color/black" />
</RelativeLayout>
android:id="@+id/fecha"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fecha"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="false"
android:layout_alignParentStart="false"
android:textColor="@android:color/black"
android:layout_toRightOf="@+id/imageView" />
Crear base de datos: Para implementar la base de datos lo primero que debes hacer es
crear una nueva base de datos en la aplicacin phpMyAdmin que te otorga tu
distribucin XAMPP. Donde le asignaremos el nombre de i_wish.
Ahora crea la tabla meta para que contenga seis columnas en su estructura y adems usa el
formato UTF-8 para soportar acentos. Puedes hacerlo a travs del editor de phpMyAdmin o
con el siguiente comandoCREATE:
CREATE TABLE IF NOT EXISTS meta(
idMeta int(3) PRIMARY KEY AUTO_INCREMENT,
titulo varchar(56) NOT NULL,
descripcion varchar(128) NOT NULL,
'Media',
'2015-10-13',
'Finanzas');
require_once 'mysql_login.php';
class Database
{
/**
* nica instancia de la clase
*/
private static $db = null;
/**
* Instancia de PDO
*/
private static $pdo;
/**
* Retorna en la nica instancia de la clase
* @return Database|null
*/
public static function getInstance()
{
if (self::$db === null) {
/**
* Crear una nueva conexin PDO basada
* en los datos de conexin
* @return PDO Objeto PDO
*/
public function getDb()
{
if (self::$pdo == null) {
self::$pdo = new PDO(
'mysql:dbname=' . DATABASE .
';host=' . HOSTNAME .
';port:63343;',
USERNAME,
PASSWORD,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
);
// Habilitar excepciones
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$pdo;
}
/**
* Evita la clonacin del objeto
*/
final protected function __clone()
{
}
function _destructor()
{
self::$pdo = null;
}
}
?>
Ten en cuenta que la conexin se abre con 4 cadenas descriptivas del entorno que ests
usando declaradas en el archivo mysql_login.php. Con ello me refiero al nombre del host, el
nombre de la base de datos, el usuario con que deseas ingresar y su respectiva contrasea.
Por el momento usaremos el localhost debido a las pruebas que estamos haciendo. El
usuario ya depende de ti, en mi caso uso el usuario por defecto "root" y sin contrasea
alguna.
mysql_login.php
<?php
/**
* Provee las constantes para conectarse a la base de datos
* Mysql.
*/
define("HOSTNAME", "localhost");// Nombre del host
define("DATABASE", "i_wish"); // Nombre de la base de datos
define("USERNAME", "root"); // Nombre del usuario
define("PASSWORD", ""); // Nombre de la constrasea
?>
Adicionalmente debes aadir al cuarto parmetro del constructor de PDO la indicacin SET
NAMES UTF-8para el servidor. Esto permite que los datos de la base de datos vengan
codificados en este formato para evitar problemas de compatibilidad.
Meta.php
<?php
/**
* Representa el la estructura de las metas
* almacenadas en la base de datos
*/
require 'Database.php';
class Meta
{
function __construct()
{
}
/**
* Retorna en la fila especificada de la tabla 'meta'
*
* @param $idMeta Identificador del registro
* @return array Datos del registro
*/
public static function getAll()
{
$consulta = "SELECT * FROM meta";
try {
// Preparar sentencia
$comando = Database::getInstance()->getDb()->prepare($consulta);
// Ejecutar sentencia preparada
$comando->execute();
return $comando->fetchAll(PDO::FETCH_ASSOC);
/**
* Obtiene los campos de una meta con un identificador
* determinado
*
* @param $idMeta Identificador de la meta
* @return mixed
*/
public static function getById($idMeta)
{
// Consulta de la meta
$consulta = "SELECT idMeta,
titulo,
descripcion,
prioridad,
fechaLim,
categoria
FROM meta
WHERE idMeta = ?";
try {
// Preparar sentencia
$comando = Database::getInstance()->getDb()->prepare($consulta);
// Ejecutar sentencia preparada
$comando->execute(array($idMeta));
// Capturar primera fila del resultado
$row = $comando->fetch(PDO::FETCH_ASSOC);
return $row;
/**
* Actualiza un registro de la bases de datos basado
* en los nuevos valores relacionados con un identificador
*
* @param $idMeta
* @param $titulo
identificador
nuevo titulo
// Preparar la sentencia
$cmd = Database::getInstance()->getDb()->prepare($consulta);
return $cmd;
}
/**
* Insertar una nueva meta
*
* @param $titulo
// Preparar la sentencia
$sentencia = Database::getInstance()->getDb()->prepare($comando);
return $sentencia->execute(
array(
$titulo,
$descripcion,
$fechaLim,
$categoria,
$prioridad
)
);
/**
* Eliminar el registro con el identificador especificado
*
* @param $idMeta identificador de la meta
* @return bool Respuesta de la eliminacin
*/
public static function delete($idMeta)
{
// Sentencia DELETE
$comando = "DELETE FROM meta WHERE idMeta=?";
// Preparar la sentencia
$sentencia = Database::getInstance()->getDb()->prepare($comando);
return $sentencia->execute(array($idMeta));
}
}
?>
Recuerda que el mtodo prepare() permite reemplazar los placeholders ('?') a travs
de execute(). Esto protege la operacin de inyecciones que puedan atentar contra la
seguridad de los datos.
require 'Meta.php';
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
if ($metas) {
$datos["estado"] = 1;
$datos["metas"] = $metas;
print json_encode($datos);
} else {
print json_decode(array(
"estado" => 2,
"mensaje" => "Ha ocurrido un error"
));
}
}
El objeto Json que retornaremos tiene un atributo llamado "estado" el cual representa un
cdigo para indicar la calidad del resultado. Si es 1, entonces aadiremos otro atributo
llamado "metas" el cual es un array de objetos con los datos de las metas. Si es 2, entonces
usaremos un atributo "mensaje" para avisar a la aplicacin cliente que ocurri un error en
la operacin a la base de datos.
Una respuesta de xito tendra el siguiente aspecto:
{
"estado":1,
"metas":[
{
"idMeta":"2",
"titulo":"Obtener mi t\u00edtulo de ingenier\u00eda de sistemas",
"descripcion":"Ya solo faltan 2 semestres para terminar mi carrera de ingenier\u00eda. Debo prepararme al
m\u00e1ximo para desarrollar mi tesis de grado",
"prioridad":"Media",
"fechaLim":"2015-05-29",
"categoria":"Profesional"
},
{
"idMeta":"3",
"titulo":"Conquistar a Natasha",
"descripcion":"Natasha es la mujer de vida. Tengo que dec\u00edrselo antes de que acabe el semestre",
"prioridad":"Alta",
"fechaLim":"2015-05-25",
"categoria":"Espiritual"
}
]
}
{
"estado":"2",
"mensaje":"Ha ocurrido un error"
}
El diseo RESTful para Web Services provee reglas supremamente estilizadas para filtrar y
consultar datos de forma ms sencilla que estableciendo filtros manuales.
Paso #6: Crear un script php para consultar el detalle de una meta
El segundo caso requiere que la peticin traiga consigo el identificador de la meta que se
desea ver en detalle. Con este dato es posible usar el mtodo getById() de Meta para
conseguir el array necesario.
Veamos:
<?php
/**
* Obtiene el detalle de una meta especificada por
* su identificador "idMeta"
*/
require 'Meta.php';
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
if (isset($_GET['idMeta'])) {
// Tratar retorno
$retorno = Meta::getById($parametro);
if ($retorno) {
$meta["estado"] = "1";
$meta["meta"] = $retorno;
// Enviar objeto json de la meta
print json_encode($meta);
} else {
// Enviar respuesta de error general
print json_encode(
array(
'estado' => '2',
'mensaje' => 'No se obtuvo el registro'
)
);
}
} else {
// Enviar respuesta de error
print json_encode(
array(
'estado' => '3',
'mensaje' => 'Se necesita un identificador'
)
);
}
}
Para retornar el detalle obviamente primero debes comprobar que el parmetro vino con la
peticin GET y si vino bien definido. Recuerda que la funcin isset() es quin realiza este
trabajo.
Esta vez tenemos tres casos generales posibles. Que la consulta sea un xito y el registro
con el identificador enviado existe. Lo que retorna en un objeto Json con un objeto interno
que tiene los datos de la meta.
{
"estado":"1",
"meta":{
"idMeta":"2",
"titulo":"Obtener mi t\u00edtulo de ingenier\u00eda de sistemas",
"descripcion":"Ya solo faltan 2 semestres para terminar mi carrera de ingenier\u00eda. Debo prepararme al
m\u00e1ximo para desarrollar mi tesis de grado",
"prioridad":"Media",
"fechaLim":"2015-05-29",
"categoria":"Profesional"
}
}
O tambin puede que PDO haya arrojado una excepcin por algn motivo. Por ejemplo un
error de sintaxis, la inexistencia del registro, etc. Con ello envas tu objeto representativo
del estado 2.
{
"estado":"2",
"mensaje":"No se obtuvo el registro"
}
Ahora bien, puede que por alguna razn el parmetro no haya venido en la peticin, o que
pueda que haya venido pero con otro nombre. Para este caso envas tu cdigo 3 indicando
este mensaje.
{
"estado":"3",
"mensaje":"Se necesita un identificador"
}
<?php
/**
* Insertar una nueva meta en la base de datos
*/
require 'Meta.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Insertar meta
$retorno = Meta::insert(
$body['titulo'],
$body['descripcion'],
$body['fechaLim'],
$body['categoria'],
$body['prioridad']);
if ($retorno) {
// Cdigo de xito
print json_encode(
array(
'estado' => '1',
'mensaje' => 'Creacin exitosa')
);
} else {
// Cdigo de falla
print json_encode(
array(
'estado' => '2',
'mensaje' => 'Creacin fallida')
);
}
}
informacin. Para ello usa la funcin json_decode() y pasa como segundo parmetro el
valor de true.
Luego usa el mtodo insert() de Meta y comprueba el resultado. Esta vez no retornas en
filas de la base de datos, as que el estado 1 contiene un mensaje de xito.
{
"estado":"1",
"mensaje":"Creacin xitosa"
}
{
"estado":"2",
"mensaje":"Creacin fallida"
}
require 'Meta.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Actualizar meta
$retorno = Meta::update(
$body['idMeta'],
$body['titulo'],
$body['descripcion'],
$body['fechaLim'],
$body['categoria'],
$body['prioridad']);
if ($retorno) {
// Cdigo de xito
print json_encode(
array(
'estado' => '1',
'mensaje' => 'Actualizacin exitosa')
);
} else {
// Cdigo de falla
print json_encode(
array(
'estado' => '2',
'mensaje' => 'Actualizacin fallida')
);
}
}
{
"estado":"1",
"mensaje":"Actualizacin xitosa"
}
"estado":"2",
"mensaje":"Actualizacin fallida"
}
require 'Meta.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$retorno = Meta::delete($body['idMeta']);
if ($retorno) {
print json_encode(
array(
'estado' => '1',
'mensaje' => 'Eliminacin exitosa')
);
} else {
print json_encode(
array(
'estado' => '2',
'mensaje' => 'Eliminacin fallida')
);
}
Como ves este servicio no es nada complicado. Su respuesta de xito ser vera de la
siguiente forma:
{
"estado":"1",
"mensaje":"Eliminacin xitosa"
}
{
"estado":"2",
"mensaje":"Eliminacin fallida"
}
Crear la peticin personalizada para tratar respuestas Json (el cdigo ya fue tratado
en el artculo de Volley).
Este paso ya hace parte de nuestra rutina para gestionar peticiones HTTP. As que
reutilizars el singleton de artculos pasados para simplificar procesos. La nica diferencia
que tendrs ser la ausencia delImageLoader como atributo. En esta ocasin no usaremos
caching de imgenes, as que es justo dejarlo descansar.
Recuerda incluir la librera Volley en tu proyecto de la forma que ms te parezca.
VolleySingleton.java
import android.content.Context;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
/**
* Creado por Hermosa Programacin.
*
* Clase que representa un cliente HTTP Volley
*/
// Atributos
private static VolleySingleton singleton;
private RequestQueue requestQueue;
private static Context context;
/**
* Retorna la instancia unica del singleton
* @param context contexto donde se ejecutarn las peticiones
* @return Instancia
*/
public static synchronized VolleySingleton getInstance(Context context) {
if (singleton == null) {
singleton = new VolleySingleton(context.getApplicationContext());
}
return singleton;
}
/**
* Obtiene la instancia de la cola de peticiones
* @return cola de peticiones
*/
public RequestQueue getRequestQueue() {
if (requestQueue == null) {
requestQueue = Volley.newRequestQueue(context.getApplicationContext());
}
return requestQueue;
}
/**
* Aade la peticin a la cola
* @param req peticin
* @param <T> Resultado final de tipo T
*/
public <T> void addToRequestQueue(Request<T> req) {
getRequestQueue().add(req);
}
Para acceder a las URLs del web service con aislamiento, crea una clase para referenciar
constantes de la aplicacin. All aadirs todas las direcciones para evitar mltiples
declaraciones:
/**
* Clase que contiene los cdigos usados en "I Wish" para
* mantener la integridad en las interacciones entre actividades
* y fragmentos
*/
public class Constantes {
/**
* Transicin Home -> Detalle
*/
public static final int CODIGO_DETALLE = 100;
/**
* Transicin Detalle -> Actualizacin
*/
public static final int CODIGO_ACTUALIZACION = 101;
/**
* URLs del Web Service
*/
public static final String GET = "http://10.0.3.2:63343/I%20Wish/obtener_metas.php";
public static final String GET_BY_ID = "http://10.0.3.2:63343/I%20Wish/obtener_meta_por_id.php";
public static final String UPDATE = "http://10.0.3.2:63343/I%20Wish/actualizar_meta.php";
public static final String DELETE = "http://10.0.3.2:63343/I%20Wish/borrar_meta.php";
public static final String INSERT = "http://10.0.3.2:63343/I%20Wish/insertar_meta.php";
/**
* Clave para el valor extra que representa al identificador de una meta
*/
public static final String EXTRA_ID = "IDEXTRA";
/*
Atributos
*/
private String idMeta;
private String titulo;
private String descripcion;
private String prioridad;
private String fechaLim;
private String categoria;
public Meta(String idMeta, String titulo, String descripcion, String prioridad, String fechaLim, String categoria)
{
this.idMeta = idMeta;
this.titulo = titulo;
this.descripcion = descripcion;
this.prioridad = prioridad;
this.fechaLim = fechaLim;
this.categoria = categoria;
}
/**
* Compara los atributos de dos metas
* @param meta Meta externa
* @return true si son iguales, false si hay cambios
*/
public boolean compararCon(Meta meta) {
return this.titulo.compareTo(meta.titulo) == 0 &&
this.descripcion.compareTo(meta.descripcion) == 0 &&
this.fechaLim.compareTo(meta.fechaLim) == 0 &&
this.categoria.compareTo(meta.categoria) == 0 &&
this.prioridad.compareTo(meta.prioridad) == 0;
}
}
Si te fijas, tenemos un mtodo para comparar una meta con otra para determinar si son
iguales o no. Este mtodo ser de gran ayuda al momento de validar si hay cambios en los
datos de los formularios cuando el usuario interacta con ellos. Lo que permitir determinar
si hay que lanzar dilogos de confirmacin antes de aplicar acciones.
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.modelo.Meta;
import com.herprogramacion.iwish.ui.actividades.DetailActivity;
import java.util.List;
/**
* Adaptador del recycler view
*/
public class MetaAdapter extends RecyclerView.Adapter<MetaAdapter.MetaViewHolder>
implements ItemClickListener {
/**
/*
Contexto donde actua el recycler view
*/
private Context context;
@Override
public int getItemCount() {
return items.size();
}
@Override
public MetaViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_list, viewGroup, false);
return new MetaViewHolder(v, this);
}
@Override
public void onBindViewHolder(MetaViewHolder viewHolder, int i) {
viewHolder.titulo.setText(items.get(i).getTitulo());
viewHolder.prioridad.setText(items.get(i).getPrioridad());
viewHolder.fechaLim.setText(items.get(i).getFechaLim());
viewHolder.categoria.setText(items.get(i).getCategoria());
}
/**
* Sobrescritura del mtodo de la interfaz {@link ItemClickListener}
*
* @param view
item actual
@Override
public void onClick(View v) {
listener.onItemClick(v, getAdapterPosition());
}
}
}
interface ItemClickListener {
void onItemClick(View view, int position);
}
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.ui.fragmentos.MainFragment;
/**
* Actividad principal que contiene un fragmento con una lista.
* Recuerda que la nueva librera de soporte reemplaz la clase
* {@link android.support.v7.app.ActionBarActivity} por
* {@link AppCompatActivity} para el uso de la action bar
* en versiones antiguas.
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
La comunicacin inicial con el servidor es la lectura de todas las metas que se han guardado
hasta el momento. Con ellas poblaremos la lista a penas inicie la aplicacin. Por lo que
debemos dirigirnos al fragmento principal y generar una peticin GET hacia el servidor
en onCreateView().
Veamos:
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.google.gson.Gson;
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.modelo.Meta;
import com.herprogramacion.iwish.tools.Constantes;
import com.herprogramacion.iwish.ui.MetaAdapter;
import com.herprogramacion.iwish.ui.actividades.InsertActivity;
import com.herprogramacion.iwish.web.VolleySingleton;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;
/**
* Fragmento principal que contiene la lista de las metas
*/
public class MainFragment extends Fragment {
/*
Etiqueta de depuracion
*/
private static final String TAG = MainFragment.class.getSimpleName();
/*
Adaptador del recycler view
*/
private MetaAdapter adapter;
/*
Instancia global del recycler view
*/
private RecyclerView lista;
/*
instancia global del administrador
*/
private RecyclerView.LayoutManager lManager;
/*
Instancia global del FAB
*/
com.melnykov.fab.FloatingActionButton fab;
private Gson gson = new Gson();
public MainFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return v;
}
/**
* Carga el adaptador con las metas obtenidas
* en la respuesta
*/
public void cargarAdaptador() {
// Peticin GET
VolleySingleton.
getInstance(getActivity()).
addToRequestQueue(
new JsonObjectRequest(
Request.Method.GET,
Constantes.GET,
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
// Procesar la respuesta Json
procesarRespuesta(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error Volley: " + error.getMessage());
}
}
)
);
}
/**
* Interpreta los resultados de la respuesta y as
* realizar las operaciones correspondientes
*
* @param response Objeto Json con la respuesta
*/
private void procesarRespuesta(JSONObject response) {
try {
// Obtener atributo "estado"
String estado = response.getString("estado");
switch (estado) {
case "1": // EXITO
// Obtener array "metas" Json
} catch (JSONException e) {
e.printStackTrace();
}
Recuerda que el adaptador recibe una serie de metas en formato List<Meta>, por lo que
usaremos la clase Arrays para convertir el arreglo de metas a lista. Con eso listo ya es
posible instanciar el adaptador y asignarlo al recycler.
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.tools.Constantes;
import com.herprogramacion.iwish.ui.fragmentos.DetailFragment;
/**
* Esta actividad contiene un fragmento que muestra el detalle
* de las metas.
*/
public class DetailActivity extends AppCompatActivity {
/*
Valor extra que identifica a la meta a detallar
*/
private static final String EXTRA_ID = "IDMETA";
/**
* Instancia global de la meta a detallar
*/
/**
* Inicia una nueva instancia de la actividad
*
* @param activity Contexto desde donde se lanzar
* @param idMeta Identificador de la meta a detallar
*/
public static void launch(Activity activity, String idMeta) {
Intent intent = getLaunchIntent(activity, idMeta);
activity.startActivityForResult(intent, Constantes.CODIGO_DETALLE);
}
/**
* Construye un Intent a partir del contexto y la actividad
* de detalle.
*
* @param context Contexto donde se inicia
* @param idMeta Identificador de la meta
* @return Intent listo para usar
*/
public static Intent getLaunchIntent(Context context, String idMeta) {
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra(EXTRA_ID, idMeta);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getSupportActionBar() != null) {
// Dehabilitar titulo de la actividad
getSupportActionBar().setDisplayShowTitleEnabled(false);
// Retener instancia
if (getIntent().getStringExtra(EXTRA_ID) != null)
idMeta = getIntent().getStringExtra(EXTRA_ID);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, DetailFragment.createInstance(idMeta), "DetailFragment")
.commit();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_detail, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
Este cdigo tiene varias cosas interesantes. En primera instancia el uso de un mtodo
esttico llamadolaunch(), el cual construye una instancia de la actividad de detalle y la
inicia a travs de un Intentconstruido a partir del contexto que el adaptador proveer.
La actividad detalle se basa en el identificador de la meta, por lo que idMeta es un atributo
que permitir retener esa instancia, cuando sea pedida con getIntent().
A los fragmentos que hemos iniciado dinmicamente se les est asignando una etiqueta que
los diferencie de los otros. Esto es de suprema importancia, ya que necesitamos obtener sus
instancias cuando la actividad se comunique con ellos.
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.google.gson.Gson;
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.modelo.Meta;
import com.herprogramacion.iwish.tools.Constantes;
import com.herprogramacion.iwish.ui.actividades.UpdateActivity;
import com.herprogramacion.iwish.web.VolleySingleton;
import org.json.JSONException;
import org.json.JSONObject;
/**
* A placeholder fragment containing a simple view.
*/
public class DetailFragment extends Fragment {
/*
Etiqueta de valor extra
*/
private static final String EXTRA_ID = "IDMETA";
/**
* Etiqueta de depuracin
*/
private static final String TAG = DetailFragment.class.getSimpleName();
/*
Instancias de Views
*/
private ImageView cabecera;
private TextView titulo;
private TextView descripcion;
private TextView prioridad;
private TextView fechaLim;
private TextView categoria;
private ImageButton editButton;
private String extra;
private Gson gson = new Gson();
public DetailFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_detail, container, false);
// Obtencin de views
cabecera = (ImageView) v.findViewById(R.id.cabecera);
titulo = (TextView) v.findViewById(R.id.titulo);
descripcion = (TextView) v.findViewById(R.id.descripcion);
prioridad = (TextView) v.findViewById(R.id.prioridad);
fechaLim = (TextView) v.findViewById(R.id.fecha);
categoria = (TextView) v.findViewById(R.id.categoria);
return v;
}
/**
* Obtiene los datos desde el servidor
*/
public void cargarDatos() {
Request.Method.GET,
newURL,
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
// Procesar respuesta Json
procesarRespuesta(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error Volley: " + error.getMessage());
}
}
)
);
}
/**
* Procesa cada uno de los estados posibles de la
* respuesta enviada desde el servidor
*
* @param response Objeto Json
*/
private void procesarRespuesta(JSONObject response) {
try {
// Obtener atributo "mensaje"
String mensaje = response.getString("estado");
switch (mensaje) {
case "1":
//Parsear objeto
Meta meta = gson.fromJson(object.toString(), Meta.class);
break;
case "2":
case "3":
String mensaje3 = response.getString("mensaje");
Toast.makeText(
getActivity(),
mensaje3,
Toast.LENGTH_LONG).show();
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
cambios con el segundo action button. Incluso puedes incluir la eliminacin entre los action
buttos.
As que lo primero es crear un archivo de men para poblar la action bar:
menu_form.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.herprogramacion.iwish.ui.actividades.UpdateActivity">
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.google.gson.Gson;
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.modelo.Meta;
import com.herprogramacion.iwish.tools.Constantes;
import com.herprogramacion.iwish.web.VolleySingleton;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
/**
* Fragmento con formulario para actualizar la meta
*/
public class UpdateFragment extends Fragment {
/*
Etiqueta de depuracin
*/
private static final String TAG = UpdateFragment.class.getSimpleName();
/*
Etiqueta de valor extra para modo edicin
*/
private static final String EXTRA_ID = "IDMETA";
/*
Controles
*/
private EditText titulo_input;
private EditText descripcion_input;
private Spinner prioridad_spinner;
private TextView fecha_text;
private Spinner categoria_spinner;
/*
Valor del argumento extra
*/
private String idMeta;
/**
* Es la meta obtenida como respuesta de la peticin HTTP
*/
private Meta metaOriginal;
/**
* Instancia Gson para el parsing Json
*/
private Gson gson = new Gson();
public UpdateFragment() {
}
/**
* Crea un nuevo fragmento basado en un argumento
*
* @param extra Argumento de entrada
* @return Nuevo fragmento
*/
public static Fragment createInstance(String extra) {
UpdateFragment detailFragment = new UpdateFragment();
Bundle bundle = new Bundle();
bundle.putString(EXTRA_ID, extra);
detailFragment.setArguments(bundle);
return detailFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
fecha_text.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
}
}
);
if (idMeta != null) {
cargarDatos();
}
return v;
}
/**
* Obtiene los datos desde el servidor
*/
private void cargarDatos() {
// Aadiendo idMeta como parmetro a la URL
String newURL = Constantes.GET_BY_ID + "?idMeta=" + idMeta;
@Override
public void onResponse(JSONObject response) {
// Procesa la respuesta GET_BY_ID
procesarRespuestaGet(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error Volley: " + error.getMessage());
}
}
)
);
}
/**
* Procesa la respuesta de obtencin obtenida desde el sevidor
*/
private void procesarRespuestaGet(JSONObject response) {
try {
String estado = response.getString("estado");
switch (estado) {
case "1":
JSONObject meta = response.getJSONObject("meta");
// Guardar instancia
metaOriginal = gson.fromJson(meta.toString(), Meta.class);
// Setear valores de la meta
cargarViews(metaOriginal);
break;
case "2":
String mensaje = response.getString("mensaje");
// Mostrar mensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
// Enviar cdigo de falla
getActivity().setResult(Activity.RESULT_CANCELED);
// Terminar actividad
getActivity().finish();
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* Carga los datos iniciales del formulario con los
* atributos de un objeto {@link Meta}
*
* @param meta Instancia
*/
private void cargarViews(Meta meta) {
// Seteando valores de la respuesta
titulo_input.setText(meta.getTitulo());
descripcion_input.setText(meta.getDescripcion());
fecha_text.setText(meta.getFechaLim());
break;
}
}
break;
}
}
/**
* Compara los datos actuales con aquellos que se obtuvieron
* por primera vez en la respuesta HTTP
*
* @return true si los datos no han cambiado, de lo contrario false
*/
public boolean validarCambios() {
return metaOriginal.compararCon(obtenederDatos());
}
/**
* Retorna en una nueva meta creada a partir
* de los datos del formulario actual
*
* @return Instancia {@link Meta}
*/
private Meta obtenederDatos() {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true); // Contribucin a la AB
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case android.R.id.home:// CONFIRMAR
if (!validarCambios())
guardarMeta();
else
// Terminar actividad, ya que no hay cambios
getActivity().finish();
return true;
break;
}
;
return super.onOptionsItemSelected(item);
}
/**
* Guarda los cambios de una meta editada.
* <p>
* Si est en modo insercin, entonces crea una nueva
* meta en la base de datos
*/
private void guardarMeta() {
map.put("idMeta", idMeta);
map.put("titulo", titulo);
map.put("descripcion", descripcion);
map.put("fechaLim", fecha);
map.put("categoria", categoria);
map.put("prioridad", prioridad);
){
@Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json; charset=utf-8");
headers.put("Accept", "application/json");
return headers;
}
@Override
public String getBodyContentType() {
return "application/json; charset=utf-8" + getParamsEncoding();
}
}
);
/**
* Procesa todos las tareas para eliminar
* una meta en la aplicacin. Este mtodo solo se usa
* en la edicin
*/
public void eliminarMeta() {
// Procesar la respuesta
procesarRespuestaEliminar(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error Volley: " + error.getMessage());
}
}
){
@Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json; charset=utf-8");
headers.put("Accept", "application/json");
return headers;
}
@Override
public String getBodyContentType() {
return "application/json; charset=utf-8" + getParamsEncoding();
}
}
);
}
/**
* Procesa la respuesta de eliminacin obtenida desde el sevidor
*/
private void procesarRespuestaEliminar(JSONObject response) {
try {
// Obtener estado
String estado = response.getString("estado");
// Obtener mensaje
String mensaje = response.getString("mensaje");
switch (estado) {
case "1":
// Mostrar mensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
// Enviar cdigo de xito
getActivity().setResult(203);
// Terminar actividad
getActivity().finish();
break;
case "2":
// Mostrar mensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
// Enviar cdigo de falla
getActivity().setResult(Activity.RESULT_CANCELED);
// Terminar actividad
getActivity().finish();
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
/**
* Procesa la respuesta de actualizacin obtenida desde el sevidor
*/
private void procesarRespuestaActualizar(JSONObject response) {
try {
// Obtener estado
String estado = response.getString("estado");
// Obtener mensaje
String mensaje = response.getString("mensaje");
switch (estado) {
case "1":
// Mostrar mensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
// Enviar cdigo de xito
getActivity().setResult(Activity.RESULT_OK);
// Terminar actividad
getActivity().finish();
break;
case "2":
// Mostrar mensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
// Enviar cdigo de falla
getActivity().setResult(Activity.RESULT_CANCELED);
// Terminar actividad
getActivity().finish();
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
/**
* Actualiza la fecha del campo {@link fecha_text}
*
* @param ano Ao
* @param mes Mes
* @param dia Da
*/
public void actualizarFecha(int ano, int mes, int dia) {
// Setear en el textview la fecha
fecha_text.setText(ano + "-" + (mes + 1) + "-" + dia);
}
/**
* Muestra un dilogo de confirmacin, cuyo mensaje esta
* basado en el parmetro identificador de Strings
*
* @param id Parmetro
*/
private void mostrarDialogo(int id) {
DialogFragment dialogo = ConfirmDialogFragment.
createInstance(
getResources().
getString(id));
dialogo.show(getFragmentManager(), "ConfirmDialog");
}
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.ui.fragmentos.ConfirmDialogFragment;
import com.herprogramacion.iwish.ui.fragmentos.DatePickerFragment;
import com.herprogramacion.iwish.ui.fragmentos.InsertFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getSupportActionBar() != null)
getSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_done);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_form, menu);
return true;
}
@Override
public void onDateSelected(int year, int month, int day) {
InsertFragment insertFragment = (InsertFragment)
getSupportFragmentManager().findFragmentByTag("InsertFragment");
if (insertFragment != null) {
insertFragment.actualizarFecha(year, month, day);
}
}
@Override
public void onDialogPositiveClick(DialogFragment dialog) {
InsertFragment insertFragment = (InsertFragment)
getSupportFragmentManager().findFragmentByTag("InsertFragment");
if (insertFragment != null) {
finish(); // Finalizar actividad descartando cambios
}
}
@Override
public void onDialogNegativeClick(DialogFragment dialog) {
InsertFragment insertFragment = (InsertFragment)
getSupportFragmentManager().findFragmentByTag("InsertFragment");
if (insertFragment != null) {
// Nada por el momento
}
}
}
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.herprogramacion.iwish.R;
import com.herprogramacion.iwish.tools.Constantes;
import com.herprogramacion.iwish.web.VolleySingleton;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
/**
* Fragmento que permite al usuario insertar un nueva meta
*/
public class InsertFragment extends Fragment {
/**
* Etiqueta para depuracin
*/
private static final String TAG = InsertFragment.class.getSimpleName();
/*
Controles
*/
EditText titulo_input;
EditText descripcion_input;
Spinner prioridad_spinner;
TextView fecha_text;
Spinner categoria_spinner;
public InsertFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Habilitar al fragmento para contribuir en la action bar
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflando layout del fragmento
View v = inflater.inflate(R.layout.fragment_form, container, false);
fecha_text.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
DialogFragment picker = new DatePickerFragment();
picker.show(getFragmentManager(), "datePicker");
}
}
);
return v;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Remover el action button de borrar
menu.removeItem(R.id.action_delete);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case android.R.id.home:// CONFIRMAR
if (!camposVacios())
guardarMeta();
else
Toast.makeText(
getActivity(),
"Completa los campos",
Toast.LENGTH_LONG).show();
return true;
return super.onOptionsItemSelected(item);
}
/**
* Guarda los cambios de una meta editada.
* <p>
* Si est en modo insercin, entonces crea una nueva
* meta en la base de datos
*/
public void guardarMeta() {
map.put("titulo", titulo);
map.put("descripcion", descripcion);
map.put("fechaLim", fecha);
map.put("categoria", categoria);
map.put("prioridad", prioridad);
){
@Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json; charset=utf-8");
headers.put("Accept", "application/json");
return headers;
}
@Override
public String getBodyContentType() {
return "application/json; charset=utf-8" + getParamsEncoding();
}
}
);
/**
* Procesa la respuesta obtenida desde el sevidor
*
* @param response Objeto Json
*/
private void procesarRespuesta(JSONObject response) {
try {
// Obtener estado
String estado = response.getString("estado");
// Obtener mensaje
String mensaje = response.getString("mensaje");
switch (estado) {
case "1":
// Mostrar mensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
// Enviar cdigo de xito
getActivity().setResult(Activity.RESULT_OK);
// Terminar actividad
getActivity().finish();
break;
case "2":
// Mostrar mensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
// Enviar cdigo de falla
getActivity().setResult(Activity.RESULT_CANCELED);
// Terminar actividad
getActivity().finish();
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
/**
* Valida si los campos {@link titulo_input} y {@link descripcion_input}
* se han rellenado
*
* @return true si alguno o dos de los campos estn vacios, false si ambos
* estn completos
*/
public boolean camposVacios() {
String titulo = titulo_input.getText().toString();
String descripcion = descripcion_input.getText().toString();
/**
* Actualiza la fecha del campo {@link fecha_text}
*
* @param ano Ao
* @param mes Mes
* @param dia Da
*/
public void actualizarFecha(int ano, int mes, int dia) {
// Setear en el textview la fecha
fecha_text.setText(ano + "-" + (mes + 1) + "-" + dia);
}
/**
* Muestra un dilogo de confirmacin
*/
public void mostrarDialogo() {
DialogFragment dialogo = ConfirmDialogFragment.
createInstance(
getResources().
getString(R.string.dialog_discard_msg));
dialogo.show(getFragmentManager(), "ConfirmDialog");
}
Esta vez hemos creado un mtodo llamado guardarMeta() basado en la URL del servicio
de insercin y los datos que el usuario haya completado. Si te fijas en el procesamiento de
los eventos sobre la action bar, puedes ver que existe la posibilidad de guardar y descartar
los datos.
Ambos se basan en la validacin de los campos del formulario que requieren texto escrito
por parte del usuario. Para ello se cre el mtodo camposVacios(). Dependiendo de su
retorno as mismo procederemos.
Esto quiere decir que el usuario no puede guardar una meta sin completar alguno de los
campos. Ni tampoco puede intentar descartar cambios sin ver un dilogo si ya ha escrito
algn dato.
Conclusiones
Usar un Web Service en Php permite compartir datos entre tus aplicativos externos y tus
aplicaciones android para mantener un proyecto integral. Sin embargo el uso de un estilo de
comunicacin elegante como REST es un excelente complemento para estructurar una
buena API.