1. Qu Es Un Web Service?
Un Web Service o Servicio Web es un aplicativoque facilita lainteroperabilidad 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 laaplicacin 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.
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:
Basado en el boceto que acabas de crear ya puedes identificar que la cantidad de actividades, fragmentos, dilogos y formularios que
necesitas. As que veamos la siguiente lista de materiales a crear:
Actividad principal con un fragmento de lista.
Layout personalizado para items.
Actividad con fragmento de detalle.
Actividad con fragmento de formulariopara insercin.
Actividad con fragmento de formulariopara edicin.
En este tutorial usaremos actividades basadas en fragmentos, ya que muchos lectores han preguntado cmo hacer para comunicar fragmentos
con actividades y viceversa.
<LinearLayoutxmlns: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"/>
El fragmento pudiese heredar de ListFragment pero debido a que vamos a usar un RecyclerView , el diseo es diferente. La idea es aadir el
recycler para recubrir toda la actividad y adems aadir un Floating Action Button en la parte inferior derecha con el fin de que el usuario
aada nuevas metas.
Para aadir el FAB (Floating Action Button) podemos hacer uso de una de las siguientes libreras que existen en la web:
Floating Action Button Library For Android
FloatingActionButton de makovkastar
Future Simple
Incluso podras basarte en el ejemplo del sitio de android devepers llamado FloatingActionButtonBasic. Todo depende de ti. Cada librera trae
la explicacin de su implementacin, as que no hay excusas.
Por mi parte, en este ejemplo usar la librera de makovkastar, ya que necesitamos fabs muy simples. Para ello incluimos la siguiente
dependencia deGradle:
compile'com.melnykov:floatingactionbutton:1.3.0'
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fab="http://schemas.android.com/apk/resauto"
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>
Se usa una etiqueta <com.melnykov.fab.FloatingActionButton> para implementar el FAB. Simplemente se ubica en la parte inferior derecha y le
aadimos los colores correspondientes a su interaccin.
Donde colorNormal es el color que tiene en estado natural; colorPressed es aquel que se proyecta cuando lo presionamos rapidamente y
colorRipple se evidencia cuando mantienes un click largo sobre l.
Otro aspecto a tener en cuenta es que los mipmaps o drawables que uses para elicono de un FABdebe 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.
<?xmlversion="1.0"encoding="utf8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fab="http://schemas.android.com/apk/resauto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!Partesuperior>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
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>
<!Datosdelameta>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="70"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<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="FechaLmite"
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 el EditText 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 quetiene un
dominio de varias opciones, puedes usar un Spinner .
Veamos:
<RelativeLayoutxmlns: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"/>
<!Descripcin>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/descripcion_input"
android:layout_below="@+id/titulo_input"
android:layout_centerHorizontal="true"
android:hint="Descripcin"
android:maxLength="128"
android:nestedScrollingEnabled="true"
android:paddingTop="16dp"
android:paddingBottom="16dp"/>
<!EtiquetaFecha>
<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"/>
<!Fecha>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="2015/05/17"
android:id="@+id/fecha_ejemplo_text"
android:layout_below="@+id/fecha_text"/>
<!Categora>
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/categoria_spinner"
android:entries="@array/entradas_categoria"
android:layout_below="@+id/categoria_texto"/>
<!EtiquetaCategora>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Categora"
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"/>
<!EtiquetaPrioridad>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Prioridad"
android:id="@+id/prioridad_text"
android:layout_below="@+id/categoria_spinner"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:textColor="@android:color/black"
android:paddingTop="16dp"/>
<!Prioridad>
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/prioridad_spinner"
android:layout_below="@+id/prioridad_text"
android:entries="@array/entradas_prioridad"/>
</RelativeLayout>
<?xmlversion="1.0"encoding="utf8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
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">
<!Titulo>
<TextView
android:id="@+id/titulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Titulo"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_below="@+id/fecha"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="16dp"/>
<!Categora>
<TextView
android:id="@+id/categoria"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="Categora"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/accent"/>
<!Fecha>
<TextView
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"/>
<!Prioridad>
<TextView
android:id="@+id/prioridad"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Prioridad"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginTop="8dp"
android:layout_below="@+id/titulo"
android:textStyle="italic"/>
<!Iconoparalafecha>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:src="@mipmap/ic_calendar"
android:layout_marginRight="3dp"/>
</RelativeLayout>
Si deseas aprender a crear Web Services con diseo REST, entonces te recomiendo este excelente curso online con Laravel.
Para desarrollar este aplicativo usar el entorno de desarrollo XAMPP, el cual provee automticamente una configuracin deunservidor
Apache local, el intrprete de Php y el gestor Mysql.
Sin embargo tu puedes usar las herramientas que desees para gestionar pruebas locales. Lo importante es que puedas correr Mysql e
interpretar scripts de Php.
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 UTF8 para soportar acentos. Puedes
hacerlo a travs del editor de phpMyAdmin o conel siguiente comando CREATE :
CREATETABLEIFNOTEXISTSmeta(
idMetaint(3)PRIMARYKEYAUTO_INCREMENT,
titulovarchar(56)NOTNULL,
descripcionvarchar(128)NOTNULL,
prioridadenum('Alta','Media','Baja','')NULLDEFAULT'Alta',
fechaLimdateNOTNULL,
categoriaenum('Salud','Finanzas','Espiritual','Profesional','Material')NOTNULLDEFAULT'Finanzas'
)
Luego aade 5 registros de ejemplo en la tabla que permitan probar el funcionamiento en la aplicacin android ms adelante.
INSERTINTO`i_wish`.`meta`(`idMeta`,`titulo`,`descripcion`,`prioridad`,`fechaLim`,`categoria`)
VALUES(NULL,
'ComprarMazda6',
'Deseoadquirirunautomazda6paramidesplazamientoenlaciudad.Deboinvestigarcmoconseguirmasfuentesdeingresos',
'Media',
'20151120',
'Material'),(NULL,
'Obtenermittulodeingenieradesistemas',
'Yasolofaltan2semestresparaterminarmicarreradeingeniera.Deboprepararmealmximoparadesarrollarmitesisdegrado',
'Alta',
'20160617',
'Profesional'),(NULL,
'ConquistaraNatasha',
'Natashaeslamujerdevida.Tengoquedecrseloantesdequeacabeelsemestre',
'Alta',
'20150525',
'Espiritual'),(NULL,
'Tenerunpesode70kg',
'Actualmentepeso92kgyestoyensobrepeso.Sinembargovoyaseguirunarutinadeejerciciosyunrgimenalimenticio',
'Baja',
'20160513',
'Salud'),(NULL,
'Incrementarun30%misingresos',
'Conseguirunafuentedeingresosalternativaquerepresentenun30%delosingresosquereciboactualmente.',
'Media',
'20151013',
'Finanzas');
<?php
/**
*ClasequeenvuelveunainstanciadelaclasePDO
*paraelmanejodelabasededatos
*/
require_once'mysql_login.php';
classDatabase
{
/**
*nicainstanciadelaclase
*/
privatestatic$db=null;
/**
*InstanciadePDO
*/
privatestatic$pdo;
finalprivatefunction__construct()
{
try{
//CrearnuevaconexinPDO
self::getDb();
}catch(PDOException$e){
//Manejodeexcepciones
}
}
/**
*Retornaenlanicainstanciadelaclase
*@returnDatabase|null
*/
publicstaticfunctiongetInstance()
{
if(self::$db===null){
self::$db=newself();
}
returnself::$db;
}
/**
*CrearunanuevaconexinPDObasada
*enlosdatosdeconexin
*@returnPDOObjetoPDO
*/
publicfunctiongetDb()
{
if(self::$pdo==null){
self::$pdo=newPDO(
'mysql:dbname='.DATABASE.
';host='.HOSTNAME.
';port:63343;',
USERNAME,
PASSWORD,
array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SETNAMESutf8")
);
//Habilitarexcepciones
self::$pdo>setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}
returnself::$pdo;
}
/**
*Evitalaclonacindelobjeto
*/
finalprotectedfunction__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
/**
*Proveelasconstantesparaconectarsealabasededatos
*Mysql.
*/
define("HOSTNAME","localhost");//Nombredelhost
define("DATABASE","i_wish");//Nombredelabasededatos
define("USERNAME","root");//Nombredelusuario
define("PASSWORD","");//Nombredelaconstrasea
?>
Adicionalmente debes aadir al cuarto parmetro del constructor de PDO la indicacin SETNAMESUTF8 para el servidor. Esto permite que los
datos de la base de datos vengan codificados en este formato para evitar problemas de compatibilidad.
<?php
/**
*Representaellaestructuradelasmetas
*almacenadasenlabasededatos
*/
require'Database.php';
classMeta
{
function__construct()
{
}
/**
*Retornaenlafilaespecificadadelatabla'meta'
*
*@param$idMetaIdentificadordelregistro
*@returnarrayDatosdelregistro
*/
publicstaticfunctiongetAll()
{
$consulta="SELECT*FROMmeta";
try{
//Prepararsentencia
$comando=Database::getInstance()>getDb()>prepare($consulta);
//Ejecutarsentenciapreparada
$comando>execute();
return$comando>fetchAll(PDO::FETCH_ASSOC);
}catch(PDOException$e){
returnfalse;
}
}
/**
*Obtieneloscamposdeunametaconunidentificador
*determinado
*
*@param$idMetaIdentificadordelameta
*@returnmixed
*/
publicstaticfunctiongetById($idMeta)
{
//Consultadelameta
$consulta="SELECTidMeta,
titulo,
descripcion,
prioridad,
fechaLim,
categoria
FROMmeta
WHEREidMeta=?";
try{
//Prepararsentencia
$comando=Database::getInstance()>getDb()>prepare($consulta);
//Ejecutarsentenciapreparada
$comando>execute(array($idMeta));
//Capturarprimerafiladelresultado
$row=$comando>fetch(PDO::FETCH_ASSOC);
return$row;
}catch(PDOException$e){
//Aqupuedesclasificarelerrordependiendodelaexcepcin
//parapresentarloenlarespuestaJson
return1;
}
}
/**
*Actualizaunregistrodelabasesdedatosbasado
*enlosnuevosvaloresrelacionadosconunidentificador
*
*@param$idMetaidentificador
*@param$titulonuevotitulo
*@param$descripcionnuevadescripcion
*@param$fechaLimnuevafechalimitedecumplimiento
*@param$categorianuevacategoria
*@param$prioridadnuevaprioridad
*/
publicstaticfunctionupdate(
$idMeta,
$titulo,
$descripcion,
$fechaLim,
$categoria,
$prioridad
)
{
//CreandoconsultaUPDATE
$consulta="UPDATEmeta".
"SETtitulo=?,descripcion=?,fechaLim=?,categoria=?,prioridad=?".
"WHEREidMeta=?";
//Prepararlasentencia
$cmd=Database::getInstance()>getDb()>prepare($consulta);
//Relacionaryejecutarlasentencia
$cmd>execute(array($titulo,$descripcion,$fechaLim,$categoria,$prioridad,$idMeta));
return$cmd;
}
/**
*Insertarunanuevameta
*
*@param$titulotitulodelnuevoregistro
*@param$descripciondescripcindelnuevoregistro
*@param$fechaLimfechalimitedelnuevoregistro
*@param$categoriacategoriadelnuevoregistro
*@param$prioridadprioridaddelnuevoregistro
*@returnPDOStatement
*/
publicstaticfunctioninsert(
$titulo,
$descripcion,
$fechaLim,
$categoria,
$prioridad
)
{
//SentenciaINSERT
$comando="INSERTINTOmeta(".
"titulo,".
"descripcion,".
"fechaLim,".
"categoria,".
"prioridad)".
"VALUES(?,?,?,?,?)";
//Prepararlasentencia
$sentencia=Database::getInstance()>getDb()>prepare($comando);
return$sentencia>execute(
array(
$titulo,
$descripcion,
$fechaLim,
$categoria,
$prioridad
)
);
}
/**
*Eliminarelregistroconelidentificadorespecificado
*
*@param$idMetaidentificadordelameta
*@returnboolRespuestadelaeliminacin
*/
publicstaticfunctiondelete($idMeta)
{
//SentenciaDELETE
$comando="DELETEFROMmetaWHEREidMeta=?";
//Prepararlasentencia
$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.
aquellos posibles caminos que puedan generarse como una peticin fallida, la falla de autenticacin, la no existencia del recurso, la no
disponibilidad del servidor, etc. En resumen, contempla todas las fallas tanto del lado del servidor (cdigos5xx) como las del cliente (cdigos
4xx).
No obstante este ejemplo se basa en el comportamiento ideal de nuestro servidor local. Donde solo reportaremos aquellas anomalas que
sucedan en la base de datos, asumiendo que la respuesta siempre tendr un cdigo de estado 200. Esto permitir trackear si nuestro web
service est operando bien la base de datos.
Adems de ello PDO puede retornar en excepciones por distintas causas que puedes estandarizar para el envo de mensajes. Pero este trabajo
te queda a t
AhoraCmo envo una respuesta de vuelta a la aplicacin Android?
Es justo donde entra Json para actuar como formato de comunicacin. En cada respuesta enviaremos una seria de elementos Json que puedan
ser interpretados del lado del cliente. Esto te ser posible usando las funciones json_encode() y json_decode() . La primera parsea un tipo de
datoa un string en formato json y la segunda es el procedimiento contrario.
Veamos nuestro servicio de obtencin:
obtener_metas.php
<?php
/**
*Obtienetodaslasmetasdelabasededatos
*/
require'Meta.php';
if($_SERVER['REQUEST_METHOD']=='GET'){
//ManejarpeticinGET
$metas=Meta::getAll();
if($metas){
$datos["estado"]=1;
$datos["metas"]=$metas;
printjson_encode($datos);
}else{
printjson_decode(array(
"estado"=>2,
"mensaje"=>"Haocurridounerror"
));
}
}
El objeto Json que retornaremos tiene un atributo llamado "estado" el cualrepresenta un cdigo para indicar la calidad del resultado. Si es
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":"Obtenermit\u00edtulodeingenier\u00edadesistemas",
"descripcion":"Yasolofaltan2semestresparaterminarmicarreradeingenier\u00eda.Deboprepararmealm\u00e1ximoparadesarrollarmit
"prioridad":"Media",
"fechaLim":"20150529",
"categoria":"Profesional"
},
{
"idMeta":"3",
"titulo":"ConquistaraNatasha",
"descripcion":"Natashaeslamujerdevida.Tengoquedec\u00edrseloantesdequeacabeelsemestre",
"prioridad":"Alta",
"fechaLim":"20150525",
"categoria":"Espiritual"
}
]
}
{
"estado":"2",
"mensaje":"Haocurridounerror"
}
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
/**
*Obtieneeldetalledeunametaespecificadapor
*suidentificador"idMeta"
*/
require'Meta.php';
if($_SERVER['REQUEST_METHOD']=='GET'){
if(isset($_GET['idMeta'])){
//ObtenerparmetroidMeta
$parametro=$_GET['idMeta'];
//Tratarretorno
$retorno=Meta::getById($parametro);
if($retorno){
$meta["estado"]="1";
$meta["meta"]=$retorno;
//Enviarobjetojsondelameta
printjson_encode($meta);
}else{
//Enviarrespuestadeerrorgeneral
printjson_encode(
array(
'estado'=>'2',
'mensaje'=>'Noseobtuvoelregistro'
)
);
}
}else{
//Enviarrespuestadeerror
printjson_encode(
array(
'estado'=>'3',
'mensaje'=>'Senecesitaunidentificador'
)
);
}
}
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":"Obtenermit\u00edtulodeingenier\u00edadesistemas",
"descripcion":"Yasolofaltan2semestresparaterminarmicarreradeingenier\u00eda.Deboprepararmealm\u00e1ximoparadesarrollarmitesi
"prioridad":"Media",
"fechaLim":"20150529",
"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":"Noseobtuvoelregistro"
}
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":"Senecesitaunidentificador"
}
<?php
/**
*Insertarunanuevametaenlabasededatos
*/
require'Meta.php';
if($_SERVER['REQUEST_METHOD']=='POST'){
//DecodificandoformatoJson
$body=json_decode(file_get_contents("php://input"),true);
//Insertarmeta
$retorno=Meta::insert(
$body['titulo'],
$body['descripcion'],
$body['fechaLim'],
$body['categoria'],
$body['prioridad']);
if($retorno){
//Cdigodexito
printjson_encode(
array(
'estado'=>'1',
'mensaje'=>'Creacinexitosa')
);
}else{
//Cdigodefalla
printjson_encode(
array(
'estado'=>'2',
'mensaje'=>'Creacinfallida')
);
}
}
La primera instruccin es comprobar la peticin POST obtenida. Luego de ello conviertes el cuerpo de la peticin a un arreglo de strings. Esto es
posible consultando el flujo con file_get_contents() , que convierte un archivo a string. Obviamentees necesario queuses la convencin
php://input para accederal cuerpo de la peticin POST.
Ahora, el resultado que obtengas con file_get_contents() debe estar en formato Json, por lo que convertiremos esos datos a un arreglo
asociativo que nos permita acceder a la 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":"Creacinxitosa"
}
{
"estado":"2",
"mensaje":"Creacinfallida"
}
<?php
/**
*Actualizaunametaespecificadaporsuidentificador
*/
require'Meta.php';
if($_SERVER['REQUEST_METHOD']=='POST'){
//DecodificandoformatoJson
$body=json_decode(file_get_contents("php://input"),true);
//Actualizarmeta
$retorno=Meta::update(
$body['idMeta'],
$body['titulo'],
$body['descripcion'],
$body['fechaLim'],
$body['categoria'],
$body['prioridad']);
if($retorno){
//Cdigodexito
printjson_encode(
array(
'estado'=>'1',
'mensaje'=>'Actualizacinexitosa')
);
}else{
//Cdigodefalla
printjson_encode(
array(
'estado'=>'2',
'mensaje'=>'Actualizacinfallida')
);
}
}
{
"estado":"1",
"mensaje":"Actualizacinxitosa"
}
{
"estado":"2",
"mensaje":"Actualizacinfallida"
}
<?php
/**
*Eliminaunametadelabasededatos
*distinguidaporsuidentificador
*/
require'Meta.php';
if($_SERVER['REQUEST_METHOD']=='POST'){
//DecodificandoformatoJson
$body=json_decode(file_get_contents("php://input"),true);
$retorno=Meta::delete($body['idMeta']);
if($retorno){
printjson_encode(
array(
'estado'=>'1',
'mensaje'=>'Eliminacinexitosa')
);
}else{
printjson_encode(
array(
'estado'=>'2',
'mensaje'=>'Eliminacinfallida')
);
}
}
Como ves este servicio no es nada complicado. Su respuesta de xito ser vera de la siguiente forma:
{
"estado":"1",
"mensaje":"Eliminacinxitosa"
}
{
"estado":"2",
"mensaje":"Eliminacinfallida"
}
importandroid.content.Context;
importcom.android.volley.Request;
importcom.android.volley.RequestQueue;
importcom.android.volley.toolbox.Volley;
/**
*CreadoporHermosaProgramacin.
*
*ClasequerepresentaunclienteHTTPVolley
*/
publicfinalclassVolleySingleton{
//Atributos
privatestaticVolleySingletonsingleton;
privateRequestQueuerequestQueue;
privatestaticContextcontext;
privateVolleySingleton(Contextcontext){
VolleySingleton.context=context;
requestQueue=getRequestQueue();
}
/**
*Retornalainstanciaunicadelsingleton
*@paramcontextcontextodondeseejecutarnlaspeticiones
*@returnInstancia
*/
publicstaticsynchronizedVolleySingletongetInstance(Contextcontext){
if(singleton==null){
singleton=newVolleySingleton(context.getApplicationContext());
}
returnsingleton;
}
/**
*Obtienelainstanciadelacoladepeticiones
*@returncoladepeticiones
*/
publicRequestQueuegetRequestQueue(){
if(requestQueue==null){
requestQueue=Volley.newRequestQueue(context.getApplicationContext());
}
returnrequestQueue;
}
/**
*Aadelapeticinalacola
*@paramreqpeticin
*@param<T>ResultadofinaldetipoT
*/
public<T>voidaddToRequestQueue(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:
/**
*Clasequecontieneloscdigosusadosen"IWish"para
*mantenerlaintegridadenlasinteraccionesentreactividades
*yfragmentos
*/
publicclassConstantes{
/**
*TransicinHome>Detalle
*/
publicstaticfinalintCODIGO_DETALLE=100;
/**
*TransicinDetalle>Actualizacin
*/
publicstaticfinalintCODIGO_ACTUALIZACION=101;
/**
*URLsdelWebService
*/
publicstaticfinalStringGET="http://10.0.3.2:63343/I%20Wish/obtener_metas.php";
publicstaticfinalStringGET_BY_ID="http://10.0.3.2:63343/I%20Wish/obtener_meta_por_id.php";
publicstaticfinalStringUPDATE="http://10.0.3.2:63343/I%20Wish/actualizar_meta.php";
publicstaticfinalStringDELETE="http://10.0.3.2:63343/I%20Wish/borrar_meta.php";
publicstaticfinalStringINSERT="http://10.0.3.2:63343/I%20Wish/insertar_meta.php";
/**
*Claveparaelvalorextraquerepresentaalidentificadordeunameta
*/
publicstaticfinalStringEXTRA_ID="IDEXTRA";
}
Como ves, yo uso para el localhost la direccin 10.0.3.2 debido a que Genymotion (emulador alternativo) estableci este valor. Si vas a usar el
emulador de android usa la direccin 10.0.2.2 . Aqu el sitio oficial te habla un poco mas sobre ests convenciones de direcciones para
operaciones en la web.
/**
*Reflejodelatabla'meta'enlabasededatos
*/
publicclassMeta{
/*
Atributos
*/
privateStringidMeta;
privateStringtitulo;
privateStringdescripcion;
privateStringprioridad;
privateStringfechaLim;
privateStringcategoria;
publicMeta(StringidMeta,Stringtitulo,Stringdescripcion,Stringprioridad,StringfechaLim,Stringcategoria){
this.idMeta=idMeta;
this.titulo=titulo;
this.descripcion=descripcion;
this.prioridad=prioridad;
this.fechaLim=fechaLim;
this.categoria=categoria;
}
publicStringgetIdMeta(){
returnidMeta;
}
publicStringgetTitulo(){
returntitulo;
}
publicStringgetDescripcion(){
returndescripcion;
}
publicStringgetPrioridad(){
returnprioridad;
}
publicStringgetFechaLim(){
returnfechaLim;
}
publicStringgetCategoria(){
returncategoria;
}
/**
*Comparalosatributosdedosmetas
*@parammetaMetaexterna
*@returntruesisoniguales,falsesihaycambios
*/
publicbooleancompararCon(Metameta){
returnthis.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.
importandroid.app.Activity;
importandroid.content.Context;
importandroid.support.v7.widget.RecyclerView;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.TextView;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.modelo.Meta;
importcom.herprogramacion.iwish.ui.actividades.DetailActivity;
importjava.util.List;
/**
*Adaptadordelrecyclerview
*/
publicclassMetaAdapterextendsRecyclerView.Adapter<MetaAdapter.MetaViewHolder>
implementsItemClickListener{
/**
*Listadeobjetos{@linkMeta}querepresentanlafuentededatos
*deinflado
*/
privateList<Meta>items;
/*
Contextodondeactuaelrecyclerview
*/
privateContextcontext;
publicMetaAdapter(List<Meta>items,Contextcontext){
this.context=context;
this.items=items;
}
@Override
publicintgetItemCount(){
returnitems.size();
}
@Override
publicMetaViewHolderonCreateViewHolder(ViewGroupviewGroup,inti){
Viewv=LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_list,viewGroup,false);
returnnewMetaViewHolder(v,this);
}
@Override
publicvoidonBindViewHolder(MetaViewHolderviewHolder,inti){
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());
}
/**
*Sobrescrituradelmtododelainterfaz{@linkItemClickListener}
*
*@paramviewitemactual
*@parampositionposicindelitemactual
*/
@Override
publicvoidonItemClick(Viewview,intposition){
DetailActivity.launch(
(Activity)context,items.get(position).getIdMeta());
}
publicstaticclassMetaViewHolderextendsRecyclerView.ViewHolder
implementsView.OnClickListener{
//Camposrespectivosdeunitem
publicTextViewtitulo;
publicTextViewprioridad;
publicTextViewfechaLim;
publicTextViewcategoria;
publicItemClickListenerlistener;
publicMetaViewHolder(Viewv,ItemClickListenerlistener){
super(v);
titulo=(TextView)v.findViewById(R.id.titulo);
prioridad=(TextView)v.findViewById(R.id.prioridad);
fechaLim=(TextView)v.findViewById(R.id.fecha);
categoria=(TextView)v.findViewById(R.id.categoria);
this.listener=listener;
v.setOnClickListener(this);
}
@Override
publicvoidonClick(Viewv){
listener.onItemClick(v,getAdapterPosition());
}
}
}
interfaceItemClickListener{
voidonItemClick(Viewview,intposition);
}
ItemClickListener es la interfaz de comunicacin que nos ayudar a relacionar lo posicin del view con el evento onClick() . Como ves se
implementa en la clase MetaAdapter para iniciar la actividad detalle a travs de su mtodo de fabricacin launch() .
Es necesario que enviemos el identificador de la meta para tener una referencia de la meta que debemos detallar.
Esto significa que se debe realizar otra peticin para obtener los datos de la meta seleccionada. Lo que podra evitarsea travs de caching con
SQLite o enviando todos los datos de la meta. Sin embargo el fin de este tutorial es el uso al mximo de nuestro Web Service para que puedas
interiorizar el conocimiento y practicar esta metodologa. Por ahora no te preocupes en la arquitectura u optimizaciones.
importandroid.content.Intent;
importandroid.os.Bundle;
importandroid.support.v7.app.AppCompatActivity;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.ui.fragmentos.MainFragment;
/**
*Actividadprincipalquecontieneunfragmentoconunalista.
*Recuerdaquelanuevalibreradesoportereemplazlaclase
*{@linkandroid.support.v7.app.ActionBarActivity}por
*{@linkAppCompatActivity}paraelusodelaactionbar
*enversionesantiguas.
*/
publicclassMainActivityextendsAppCompatActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Creacindelfragmentoprincipal
if(savedInstanceState==null){
getSupportFragmentManager().beginTransaction()
.add(R.id.container,newMainFragment(),"MainFragment")
.commit();
}
}
}
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:
importandroid.content.Intent;
importandroid.os.Bundle;
importandroid.support.v4.app.Fragment;
importandroid.support.v7.widget.LinearLayoutManager;
importandroid.support.v7.widget.RecyclerView;
importandroid.util.Log;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.Toast;
importcom.android.volley.Request;
importcom.android.volley.Response;
importcom.android.volley.VolleyError;
importcom.android.volley.toolbox.JsonObjectRequest;
importcom.google.gson.Gson;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.modelo.Meta;
importcom.herprogramacion.iwish.tools.Constantes;
importcom.herprogramacion.iwish.ui.MetaAdapter;
importcom.herprogramacion.iwish.ui.actividades.InsertActivity;
importcom.herprogramacion.iwish.web.VolleySingleton;
importorg.json.JSONArray;
importorg.json.JSONException;
importorg.json.JSONObject;
importjava.util.Arrays;
/**
*Fragmentoprincipalquecontienelalistadelasmetas
*/
publicclassMainFragmentextendsFragment{
/*
Etiquetadedepuracion
*/
privatestaticfinalStringTAG=MainFragment.class.getSimpleName();
/*
Adaptadordelrecyclerview
*/
privateMetaAdapteradapter;
/*
Instanciaglobaldelrecyclerview
*/
privateRecyclerViewlista;
/*
instanciaglobaldeladministrador
*/
privateRecyclerView.LayoutManagerlManager;
/*
InstanciaglobaldelFAB
*/
com.melnykov.fab.FloatingActionButtonfab;
privateGsongson=newGson();
publicMainFragment(){
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState){
Viewv=inflater.inflate(R.layout.fragment_main,container,false);
lista=(RecyclerView)v.findViewById(R.id.reciclador);
lista.setHasFixedSize(true);
//UsarunadministradorparaLinearLayout
lManager=newLinearLayoutManager(getActivity());
lista.setLayoutManager(lManager);
//Cargardatoseneladaptador
cargarAdaptador();
//ObtenerinstanciadelFAB
fab=(com.melnykov.fab.FloatingActionButton)v.findViewById(R.id.fab);
//AsignarescuchaalFAB
fab.setOnClickListener(
newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
//Iniciaractividaddeinsercin
getActivity().startActivityForResult(
newIntent(getActivity(),InsertActivity.class),3);
}
}
);
returnv;
}
/**
*Cargaeladaptadorconlasmetasobtenidas
*enlarespuesta
*/
publicvoidcargarAdaptador(){
//PeticinGET
VolleySingleton.
getInstance(getActivity()).
addToRequestQueue(
newJsonObjectRequest(
Request.Method.GET,
Constantes.GET,
null,
newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
//ProcesarlarespuestaJson
procesarRespuesta(response);
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
Log.d(TAG,"ErrorVolley:"+error.getMessage());
}
}
)
);
}
/**
*Interpretalosresultadosdelarespuestayas
*realizarlasoperacionescorrespondientes
*
*@paramresponseObjetoJsonconlarespuesta
*/
privatevoidprocesarRespuesta(JSONObjectresponse){
try{
//Obteneratributo"estado"
Stringestado=response.getString("estado");
switch(estado){
case"1"://EXITO
//Obtenerarray"metas"Json
JSONArraymensaje=response.getJSONArray("metas");
//ParsearconGson
Meta[]metas=gson.fromJson(mensaje.toString(),Meta[].class);
//Inicializaradaptador
adapter=newMetaAdapter(Arrays.asList(metas),getActivity());
//Setearadaptadoralalista
lista.setAdapter(adapter);
break;
case"2"://FALLIDO
Stringmensaje2=response.getString("mensaje");
Toast.makeText(
getActivity(),
mensaje2,
Toast.LENGTH_LONG).show();
break;
}
}catch(JSONExceptione){
e.printStackTrace();
}
}
El cdigo anterior muestra el usode una constante llamada Constantes.GET, la cual contiene la direccin del servicio de obtencin
obtener_metas.php.
Con esa URL ya es posible realizar la peticin JsonObjectRequest con su respectivo mtodo GET a travs del mtodo cargarAdaptador() .
Para aislar un poco los procesos, he creado el mtodo procesarRespuesta() , el cual recibe un objeto JSONObject en bruto para comenzar el
parsing. Donde he divido los caminos a travs de una estructura switch basado en el valor del atributo "estado" .
Si el estado es exitoso inmediatamente obtendremos el array de metas que viene en el atributo "metas" . Este arreglo de objetos Json se parsea
directamente a un arreglo de objetos Meta a travs de la librera Gson.
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.
importandroid.app.Activity;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.os.Bundle;
importandroid.support.v7.app.AppCompatActivity;
importandroid.view.Menu;
importandroid.view.MenuItem;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.tools.Constantes;
importcom.herprogramacion.iwish.ui.fragmentos.DetailFragment;
/**
*Estaactividadcontieneunfragmentoquemuestraeldetalle
*delasmetas.
*/
publicclassDetailActivityextendsAppCompatActivity{
/*
Valorextraqueidentificaalametaadetallar
*/
privatestaticfinalStringEXTRA_ID="IDMETA";
/**
*Instanciaglobaldelametaadetallar
*/
privateStringidMeta;
/**
*Iniciaunanuevainstanciadelaactividad
*
*@paramactivityContextodesdedondeselanzar
*@paramidMetaIdentificadordelametaadetallar
*/
publicstaticvoidlaunch(Activityactivity,StringidMeta){
Intentintent=getLaunchIntent(activity,idMeta);
activity.startActivityForResult(intent,Constantes.CODIGO_DETALLE);
}
/**
*ConstruyeunIntentapartirdelcontextoylaactividad
*dedetalle.
*
*@paramcontextContextodondeseinicia
*@paramidMetaIdentificadordelameta
*@returnIntentlistoparausar
*/
publicstaticIntentgetLaunchIntent(Contextcontext,StringidMeta){
Intentintent=newIntent(context,DetailActivity.class);
intent.putExtra(EXTRA_ID,idMeta);
returnintent;
}
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(getSupportActionBar()!=null){
//Dehabilitartitulodelaactividad
getSupportActionBar().setDisplayShowTitleEnabled(false);
//Setearcono"X"comoUpbutton
getSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_close);
}
//Retenerinstancia
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
publicbooleanonCreateOptionsMenu(Menumenu){
getMenuInflater().inflate(R.menu.menu_detail,menu);
returntrue;
}
@Override
publicbooleanonOptionsItemSelected(MenuItemitem){
intid=item.getItemId();
switch(id){
caseandroid.R.id.home:
finish();
returntrue;
}
returnsuper.onOptionsItemSelected(item);
}
}
Este cdigo tiene varias cosas interesantes. En primera instancia el uso de un mtodo esttico llamado launch() , el cual construye una
instancia de la actividad de detalle y la inicia a travs de un Intent construido 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.
En el segundo caso de edicin es necesario consultar la base de datos para setear los datos en los views. Adems de ello asignar una escucha al
FAB para que inicie la actividad de actualizacin.
Realizar peticin HTTP: La realizacin delapeticin HTTP requiere consultar el detalle con el identificador que el adaptador envo a travs del
Intent.
Recuerda que la convencin createInstance() inicializa un nuevo fragmento con los extras necesarios para su funcionamiento. Por lo que en
onCreateView() es posible acceder al identificador y enviar una peticin GET hacia el Web Service:
DetailFragment.java
importandroid.content.Intent;
importandroid.os.Bundle;
importandroid.support.v4.app.Fragment;
importandroid.util.Log;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.ImageButton;
importandroid.widget.ImageView;
importandroid.widget.TextView;
importandroid.widget.Toast;
importcom.android.volley.Request;
importcom.android.volley.Response;
importcom.android.volley.VolleyError;
importcom.android.volley.toolbox.JsonObjectRequest;
importcom.google.gson.Gson;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.modelo.Meta;
importcom.herprogramacion.iwish.tools.Constantes;
importcom.herprogramacion.iwish.ui.actividades.UpdateActivity;
importcom.herprogramacion.iwish.web.VolleySingleton;
importorg.json.JSONException;
importorg.json.JSONObject;
/**
*Aplaceholderfragmentcontainingasimpleview.
*/
publicclassDetailFragmentextendsFragment{
/*
Etiquetadevalorextra
*/
privatestaticfinalStringEXTRA_ID="IDMETA";
/**
*Etiquetadedepuracin
*/
privatestaticfinalStringTAG=DetailFragment.class.getSimpleName();
/*
InstanciasdeViews
*/
privateImageViewcabecera;
privateTextViewtitulo;
privateTextViewdescripcion;
privateTextViewprioridad;
privateTextViewfechaLim;
privateTextViewcategoria;
privateImageButtoneditButton;
privateStringextra;
privateGsongson=newGson();
publicDetailFragment(){
}
publicstaticDetailFragmentcreateInstance(StringidMeta){
DetailFragmentdetailFragment=newDetailFragment();
Bundlebundle=newBundle();
bundle.putString(EXTRA_ID,idMeta);
detailFragment.setArguments(bundle);
returndetailFragment;
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState){
Viewv=inflater.inflate(R.layout.fragment_detail,container,false);
//Obtencindeviews
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);
editButton=(ImageButton)v.findViewById(R.id.fab);
//Setearescuchaparaelfab
editButton.setOnClickListener(
newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
//Iniciaractividaddeactualizacin
Intenti=newIntent(getActivity(),UpdateActivity.class);
i.putExtra(EXTRA_ID,extra);
getActivity().startActivityForResult(i,Constantes.CODIGO_ACTUALIZACION);
}
}
);
//Obtenerextradelintentdeenvo
extra=getArguments().getString(EXTRA_ID);
//Cargardatosdesdeelwebservice
cargarDatos();
returnv;
}
/**
*Obtienelosdatosdesdeelservidor
*/
publicvoidcargarDatos(){
//AadirparmetroalaURLdelwebservice
StringnewURL=Constantes.GET_BY_ID+"?idMeta="+extra;
//RealizarpeticinGET_BY_ID
VolleySingleton.getInstance(getActivity()).addToRequestQueue(
newJsonObjectRequest(
Request.Method.GET,
newURL,
null,
newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
//ProcesarrespuestaJson
procesarRespuesta(response);
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
Log.d(TAG,"ErrorVolley:"+error.getMessage());
}
}
)
);
}
/**
*Procesacadaunodelosestadosposiblesdela
*respuestaenviadadesdeelservidor
*
*@paramresponseObjetoJson
*/
privatevoidprocesarRespuesta(JSONObjectresponse){
try{
//Obteneratributo"mensaje"
Stringmensaje=response.getString("estado");
switch(mensaje){
case"1":
//Obtenerobjeto"meta"
JSONObjectobject=response.getJSONObject("meta");
//Parsearobjeto
Metameta=gson.fromJson(object.toString(),Meta.class);
//Asignarcolordelfondo
switch(meta.getCategoria()){
case"Salud":
cabecera.setBackgroundColor(getResources().getColor(R.color.saludColor));
break;
case"Finanzas":
cabecera.setBackgroundColor(getResources().getColor(R.color.finanzasColor));
break;
case"Espiritual":
cabecera.setBackgroundColor(getResources().getColor(R.color.espiritualColor));
break;
case"Profesional":
cabecera.setBackgroundColor(getResources().getColor(R.color.profesionalColor));
break;
case"Material":
cabecera.setBackgroundColor(getResources().getColor(R.color.materialColor));
break;
}
//Seteandovaloresenlosviews
titulo.setText(meta.getTitulo());
descripcion.setText(meta.getDescripcion());
prioridad.setText(meta.getPrioridad());
fechaLim.setText(meta.getFechaLim());
categoria.setText(meta.getCategoria());
break;
case"2":
Stringmensaje2=response.getString("mensaje");
Toast.makeText(
getActivity(),
mensaje2,
Toast.LENGTH_LONG).show();
break;
case"3":
Stringmensaje3=response.getString("mensaje");
Toast.makeText(
getActivity(),
mensaje3,
Toast.LENGTH_LONG).show();
break;
}
}catch(JSONExceptione){
e.printStackTrace();
}
}
}
Para la inclusin de parmetros en la peticin GET, adjunta a la URL el valor de idMeta con la convencin de formularios '?clave=valor' .
Al igual que en MainFragment , se cre un mtodo para procesar la respuesta dependiendo del estado que se obtuvo. Si hubo xito, entonces
seteamos los valores correspondientes de cada view.
<menuxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/resauto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.herprogramacion.iwish.ui.actividades.UpdateActivity">
<!DescartarCambios>
<itemandroid:id="@+id/action_discard"android:title="@string/action_discard"
android:orderInCategory="3"app:showAsAction="never"/>
<!Borrar>
<itemandroid:id="@+id/action_delete"android:title="@string/action_delete"
android:orderInCategory="4"app:showAsAction="never"/>
</menu>
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.support.v4.app.DialogFragment;
importandroid.support.v4.app.Fragment;
importandroid.util.Log;
importandroid.view.LayoutInflater;
importandroid.view.MenuItem;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.EditText;
importandroid.widget.Spinner;
importandroid.widget.TextView;
importandroid.widget.Toast;
importcom.android.volley.Request;
importcom.android.volley.Response;
importcom.android.volley.VolleyError;
importcom.android.volley.toolbox.JsonObjectRequest;
importcom.google.gson.Gson;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.modelo.Meta;
importcom.herprogramacion.iwish.tools.Constantes;
importcom.herprogramacion.iwish.web.VolleySingleton;
importorg.json.JSONException;
importorg.json.JSONObject;
importjava.util.HashMap;
importjava.util.Map;
/**
*Fragmentoconformularioparaactualizarlameta
*/
publicclassUpdateFragmentextendsFragment{
/*
Etiquetadedepuracin
*/
privatestaticfinalStringTAG=UpdateFragment.class.getSimpleName();
/*
Etiquetadevalorextraparamodoedicin
*/
privatestaticfinalStringEXTRA_ID="IDMETA";
/*
Controles
*/
privateEditTexttitulo_input;
privateEditTextdescripcion_input;
privateSpinnerprioridad_spinner;
privateTextViewfecha_text;
privateSpinnercategoria_spinner;
/*
Valordelargumentoextra
*/
privateStringidMeta;
/**
*EslametaobtenidacomorespuestadelapeticinHTTP
*/
privateMetametaOriginal;
/**
*InstanciaGsonparaelparsingJson
*/
privateGsongson=newGson();
publicUpdateFragment(){
}
/**
*Creaunnuevofragmentobasadoenunargumento
*
*@paramextraArgumentodeentrada
*@returnNuevofragmento
*/
publicstaticFragmentcreateInstance(Stringextra){
UpdateFragmentdetailFragment=newUpdateFragment();
Bundlebundle=newBundle();
bundle.putString(EXTRA_ID,extra);
detailFragment.setArguments(bundle);
returndetailFragment;
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState){
//Inflandolayoutdelfragmento
Viewv=inflater.inflate(R.layout.fragment_form,container,false);
//Obtencindeinstanciascontroles
titulo_input=(EditText)v.findViewById(R.id.titulo_input);
descripcion_input=(EditText)v.findViewById(R.id.descripcion_input);
fecha_text=(TextView)v.findViewById(R.id.fecha_ejemplo_text);
categoria_spinner=(Spinner)v.findViewById(R.id.categoria_spinner);
prioridad_spinner=(Spinner)v.findViewById(R.id.prioridad_spinner);
fecha_text.setOnClickListener(
newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
DialogFragmentpicker=newDatePickerFragment();
picker.show(getFragmentManager(),"datePicker");
}
}
);
//Obtenervalorextra
idMeta=getArguments().getString(EXTRA_ID);
if(idMeta!=null){
cargarDatos();
}
returnv;
}
/**
*Obtienelosdatosdesdeelservidor
*/
privatevoidcargarDatos(){
//AadiendoidMetacomoparmetroalaURL
StringnewURL=Constantes.GET_BY_ID+"?idMeta="+idMeta;
//Consultareldetalledelameta
VolleySingleton.getInstance(getActivity()).addToRequestQueue(
newJsonObjectRequest(
Request.Method.GET,
newURL,
null,
newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
//ProcesalarespuestaGET_BY_ID
procesarRespuestaGet(response);
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
Log.d(TAG,"ErrorVolley:"+error.getMessage());
}
}
)
);
}
/**
*Procesalarespuestadeobtencinobtenidadesdeelsevidor*
*/
privatevoidprocesarRespuestaGet(JSONObjectresponse){
try{
Stringestado=response.getString("estado");
switch(estado){
case"1":
JSONObjectmeta=response.getJSONObject("meta");
//Guardarinstancia
metaOriginal=gson.fromJson(meta.toString(),Meta.class);
//Setearvaloresdelameta
cargarViews(metaOriginal);
break;
case"2":
Stringmensaje=response.getString("mensaje");
//Mostrarmensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
//Enviarcdigodefalla
getActivity().setResult(Activity.RESULT_CANCELED);
//Terminaractividad
getActivity().finish();
break;
}
}catch(JSONExceptione){
e.printStackTrace();
}
}
/**
*Cargalosdatosinicialesdelformularioconlos
*atributosdeunobjeto{@linkMeta}
*
*@parammetaInstancia
*/
privatevoidcargarViews(Metameta){
//Seteandovaloresdelarespuesta
titulo_input.setText(meta.getTitulo());
descripcion_input.setText(meta.getDescripcion());
fecha_text.setText(meta.getFechaLim());
//Obteniendoaccesoalosarraydestringsparacategoriasyprioridades
String[]categorias=getResources().getStringArray(R.array.entradas_categoria);
String[]prioridades=getResources().getStringArray(R.array.entradas_prioridad);
//Obteniendolaposicindelspinnercategorias
intposicion_categoria=0;
for(inti=0;i<categorias.length;i++){
if(categorias[i].compareTo(meta.getCategoria())==0){
posicion_categoria=i;
break;
}
}
//SetearseleccindelSpinnerdecategoras
categoria_spinner.setSelection(posicion_categoria);
//Obteniendolaposicindelspinnerdeprioridades
intposicion_prioridad=0;
for(inti=0;i<prioridades.length;i++){
Log.d(TAG,"posicin:"+i);
if(prioridades[i].compareTo(meta.getPrioridad())==0){
posicion_prioridad=i;
break;
}
}
//SetearseleccindelSpinnerdeprioridades
prioridad_spinner.setSelection(posicion_prioridad);
}
/**
*Comparalosdatosactualesconaquellosqueseobtuvieron
*porprimeravezenlarespuestaHTTP
*
*@returntruesilosdatosnohancambiado,delocontrariofalse
*/
publicbooleanvalidarCambios(){
returnmetaOriginal.compararCon(obtenederDatos());
}
/**
*Retornaenunanuevametacreadaapartir
*delosdatosdelformularioactual
*
*@returnInstancia{@linkMeta}
*/
privateMetaobtenederDatos(){
Stringtitulo=titulo_input.getText().toString();
Stringdescripcion=descripcion_input.getText().toString();
Stringfecha=fecha_text.getText().toString();
Stringcategoria=(String)categoria_spinner.getSelectedItem();
Stringprioridad=(String)prioridad_spinner.getSelectedItem();
returnnewMeta("0",titulo,descripcion,fecha,categoria,prioridad);
}
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);//ContribucinalaAB
}
@Override
publicbooleanonOptionsItemSelected(MenuItemitem){
intid=item.getItemId();
switch(id){
caseandroid.R.id.home://CONFIRMAR
if(!validarCambios())
guardarMeta();
else
//Terminaractividad,yaquenohaycambios
getActivity().finish();
returntrue;
caseR.id.action_delete://ELIMINAR
mostrarDialogo(R.string.dialog_delete_msg);
break;
caseR.id.action_discard://DESCARTAR
if(!validarCambios()){
mostrarDialogo(R.string.dialog_discard_msg);
}else
//Terminaractividad,yaquenohaycambios
getActivity().finish();
break;
}
;
returnsuper.onOptionsItemSelected(item);
}
/**
*Guardaloscambiosdeunametaeditada.
*<p>
*Siestenmodoinsercin,entoncescreaunanueva
*metaenlabasededatos
*/
privatevoidguardarMeta(){
//Obtenervaloresactualesdeloscontroles
finalStringtitulo=titulo_input.getText().toString();
finalStringdescripcion=descripcion_input.getText().toString();
finalStringfecha=fecha_text.getText().toString();
finalStringcategoria=categoria_spinner.getSelectedItem().toString();
finalStringprioridad=prioridad_spinner.getSelectedItem().toString();
HashMap<String,String>map=newHashMap<>();//Mapeoprevio
map.put("idMeta",idMeta);
map.put("titulo",titulo);
map.put("descripcion",descripcion);
map.put("fechaLim",fecha);
map.put("categoria",categoria);
map.put("prioridad",prioridad);
//CrearnuevoobjetoJsonbasadoenelmapa
JSONObjectjobject=newJSONObject(map);
//DepurandoobjetoJson...
Log.d(TAG,jobject.toString());
//Actualizardatosenelservidor
VolleySingleton.getInstance(getActivity()).addToRequestQueue(
newJsonObjectRequest(
Request.Method.POST,
Constantes.UPDATE,
jobject,
newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
procesarRespuestaActualizar(response);
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
Log.d(TAG,"ErrorVolley:"+error.getMessage());
}
}
){
@Override
publicMap<String,String>getHeaders(){
Map<String,String>headers=newHashMap<String,String>();
headers.put("ContentType","application/json;charset=utf8");
headers.put("Accept","application/json");
returnheaders;
}
@Override
publicStringgetBodyContentType(){
return"application/json;charset=utf8"+getParamsEncoding();
}
}
);
}
/**
*Procesatodoslastareasparaeliminar
*unametaenlaaplicacin.Estemtodosoloseusa
*enlaedicin
*/
publicvoideliminarMeta(){
HashMap<String,String>map=newHashMap<>();//MAPEO
map.put("idMeta",idMeta);//Identificador
JSONObjectjobject=newJSONObject(map);//ObjetoJson
//Eliminardatosenelservidor
VolleySingleton.getInstance(getActivity()).addToRequestQueue(
newJsonObjectRequest(
Request.Method.POST,
Constantes.DELETE,
jobject,
newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
//Procesarlarespuesta
procesarRespuestaEliminar(response);
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
Log.d(TAG,"ErrorVolley:"+error.getMessage());
}
}
){
@Override
publicMap<String,String>getHeaders(){
Map<String,String>headers=newHashMap<String,String>();
headers.put("ContentType","application/json;charset=utf8");
headers.put("Accept","application/json");
returnheaders;
}
@Override
publicStringgetBodyContentType(){
return"application/json;charset=utf8"+getParamsEncoding();
}
}
);
}
/**
*Procesalarespuestadeeliminacinobtenidadesdeelsevidor
*/
privatevoidprocesarRespuestaEliminar(JSONObjectresponse){
try{
//Obtenerestado
Stringestado=response.getString("estado");
//Obtenermensaje
Stringmensaje=response.getString("mensaje");
switch(estado){
case"1":
//Mostrarmensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
//Enviarcdigodexito
getActivity().setResult(203);
//Terminaractividad
getActivity().finish();
break;
case"2":
//Mostrarmensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
//Enviarcdigodefalla
getActivity().setResult(Activity.RESULT_CANCELED);
//Terminaractividad
getActivity().finish();
break;
}
}catch(JSONExceptione){
e.printStackTrace();
}
}
/**
*Procesalarespuestadeactualizacinobtenidadesdeelsevidor
*/
privatevoidprocesarRespuestaActualizar(JSONObjectresponse){
try{
//Obtenerestado
Stringestado=response.getString("estado");
//Obtenermensaje
Stringmensaje=response.getString("mensaje");
switch(estado){
case"1":
//Mostrarmensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
//Enviarcdigodexito
getActivity().setResult(Activity.RESULT_OK);
//Terminaractividad
getActivity().finish();
break;
case"2":
//Mostrarmensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
//Enviarcdigodefalla
getActivity().setResult(Activity.RESULT_CANCELED);
//Terminaractividad
getActivity().finish();
break;
}
}catch(JSONExceptione){
e.printStackTrace();
}
}
/**
*Actualizalafechadelcampo{@linkfecha_text}
*
*@paramanoAo
*@parammesMes
*@paramdiaDa
*/
publicvoidactualizarFecha(intano,intmes,intdia){
//Seteareneltextviewlafecha
fecha_text.setText(ano+""+(mes+1)+""+dia);
}
/**
*Muestraundilogodeconfirmacin,cuyomensajeesta
*basadoenelparmetroidentificadordeStrings
*
*@paramidParmetro
*/
privatevoidmostrarDialogo(intid){
DialogFragmentdialogo=ConfirmDialogFragment.
createInstance(
getResources().
getString(id));
dialogo.show(getFragmentManager(),"ConfirmDialog");
}
}
Este cdigo es un poco largo debido a que tenemos la implementacin de dilogos y comunicaciones de datos. Por lo que acontinuacin te
explico la esencia de las peticiones de informacin.
Cargar los datos de la meta en los componentes del formulario:En el mtodo onCreateView() obtenemos el valor extra con que fue creado el
fragmento. Si existe un valor extra, lanzamos la misma peticin que hemos usado para conseguir el detalle de la meta con el mtodo
cargarDatos() .
Inmediatamente los datos conseguidos en la peticin, los seteamos en cada view del formulario.
Manejar los eventos en cada action button: Para lograr esta tarea se implement el mtodo onOptionsItemSelected() , donde se cre una
estructura switch que permitiera la ejecucin del mtodo correspondiente a la accin. Recuerda usar onHasOptionMenu() en onCreate() para
que el fragmento pueda escuchar los eventos de la action bar.
Implementar la insercin, eliminacin y borrado de las metas: Cada operacin en la base de datos tiene un mtodo asignado para su
realizacin. Estos son: guardarMeta() y borrarMeta() .El primer mtodo realiza una peticin POST con la respectiva URL del servicio de
actualizacin, usando los valores actuales del formulario.
Similarmente borrarMeta() enva el id de la meta que se desea eliminar hacia la direccin correspondiente.
En cuanto a los dilogos, simplemente usamos el formato clsico de ACEPTAR|CANCELAR para permitir o no el efecto de los mtodos en la base
de datos. Puedes encontrar la implementacin completa descargando el cdigo en la parte superior del artculo.
importandroid.os.Bundle;
importandroid.support.v4.app.DialogFragment;
importandroid.support.v7.app.AppCompatActivity;
importandroid.view.Menu;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.ui.fragmentos.ConfirmDialogFragment;
importcom.herprogramacion.iwish.ui.fragmentos.DatePickerFragment;
importcom.herprogramacion.iwish.ui.fragmentos.InsertFragment;
publicclassInsertActivityextendsAppCompatActivity
implementsDatePickerFragment.OnDateSelectedListener,
ConfirmDialogFragment.ConfirmDialogListener{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(getSupportActionBar()!=null)
getSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_done);
//Creacindelfragmentodeinsercin
if(savedInstanceState==null){
getSupportFragmentManager().beginTransaction()
.add(R.id.container,newInsertFragment(),"InsertFragment")
.commit();
}
}
@Override
publicbooleanonCreateOptionsMenu(Menumenu){
getMenuInflater().inflate(R.menu.menu_form,menu);
returntrue;
}
@Override
publicvoidonDateSelected(intyear,intmonth,intday){
InsertFragmentinsertFragment=(InsertFragment)
getSupportFragmentManager().findFragmentByTag("InsertFragment");
if(insertFragment!=null){
insertFragment.actualizarFecha(year,month,day);
}
}
@Override
publicvoidonDialogPositiveClick(DialogFragmentdialog){
InsertFragmentinsertFragment=(InsertFragment)
getSupportFragmentManager().findFragmentByTag("InsertFragment");
if(insertFragment!=null){
finish();//Finalizaractividaddescartandocambios
}
}
@Override
publicvoidonDialogNegativeClick(DialogFragmentdialog){
InsertFragmentinsertFragment=(InsertFragment)
getSupportFragmentManager().findFragmentByTag("InsertFragment");
if(insertFragment!=null){
//Nadaporelmomento
}
}
}
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.support.v4.app.DialogFragment;
importandroid.support.v4.app.Fragment;
importandroid.util.Log;
importandroid.view.LayoutInflater;
importandroid.view.Menu;
importandroid.view.MenuInflater;
importandroid.view.MenuItem;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.EditText;
importandroid.widget.Spinner;
importandroid.widget.TextView;
importandroid.widget.Toast;
importcom.android.volley.Request;
importcom.android.volley.Response;
importcom.android.volley.VolleyError;
importcom.android.volley.toolbox.JsonObjectRequest;
importcom.herprogramacion.iwish.R;
importcom.herprogramacion.iwish.tools.Constantes;
importcom.herprogramacion.iwish.web.VolleySingleton;
importorg.json.JSONException;
importorg.json.JSONObject;
importjava.util.HashMap;
importjava.util.Map;
/**
*Fragmentoquepermitealusuarioinsertarunnuevameta
*/
publicclassInsertFragmentextendsFragment{
/**
*Etiquetaparadepuracin
*/
privatestaticfinalStringTAG=InsertFragment.class.getSimpleName();
/*
Controles
*/
EditTexttitulo_input;
EditTextdescripcion_input;
Spinnerprioridad_spinner;
TextViewfecha_text;
Spinnercategoria_spinner;
publicInsertFragment(){
}
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
//Habilitaralfragmentoparacontribuirenlaactionbar
setHasOptionsMenu(true);
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState){
//Inflandolayoutdelfragmento
Viewv=inflater.inflate(R.layout.fragment_form,container,false);
//Obtencindeinstanciascontroles
titulo_input=(EditText)v.findViewById(R.id.titulo_input);
descripcion_input=(EditText)v.findViewById(R.id.descripcion_input);
fecha_text=(TextView)v.findViewById(R.id.fecha_ejemplo_text);
categoria_spinner=(Spinner)v.findViewById(R.id.categoria_spinner);
prioridad_spinner=(Spinner)v.findViewById(R.id.prioridad_spinner);
fecha_text.setOnClickListener(
newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
DialogFragmentpicker=newDatePickerFragment();
picker.show(getFragmentManager(),"datePicker");
}
}
);
returnv;
}
@Override
publicvoidonCreateOptionsMenu(Menumenu,MenuInflaterinflater){
super.onCreateOptionsMenu(menu,inflater);
//Removerelactionbuttondeborrar
menu.removeItem(R.id.action_delete);
}
@Override
publicbooleanonOptionsItemSelected(MenuItemitem){
intid=item.getItemId();
switch(id){
caseandroid.R.id.home://CONFIRMAR
if(!camposVacios())
guardarMeta();
else
Toast.makeText(
getActivity(),
"Completaloscampos",
Toast.LENGTH_LONG).show();
returntrue;
caseR.id.action_discard://DESCARTAR
if(!camposVacios())
mostrarDialogo();
else
getActivity().finish();
break;
}
returnsuper.onOptionsItemSelected(item);
}
/**
*Guardaloscambiosdeunametaeditada.
*<p>
*Siestenmodoinsercin,entoncescreaunanueva
*metaenlabasededatos
*/
publicvoidguardarMeta(){
//Obtenervaloresactualesdeloscontroles
finalStringtitulo=titulo_input.getText().toString();
finalStringdescripcion=descripcion_input.getText().toString();
finalStringfecha=fecha_text.getText().toString();
finalStringcategoria=categoria_spinner.getSelectedItem().toString();
finalStringprioridad=prioridad_spinner.getSelectedItem().toString();
HashMap<String,String>map=newHashMap<>();//Mapeoprevio
map.put("titulo",titulo);
map.put("descripcion",descripcion);
map.put("fechaLim",fecha);
map.put("categoria",categoria);
map.put("prioridad",prioridad);
//CrearnuevoobjetoJsonbasadoenelmapa
JSONObjectjobject=newJSONObject(map);
//DepurandoobjetoJson...
Log.d(TAG,jobject.toString());
//Actualizardatosenelservidor
VolleySingleton.getInstance(getActivity()).addToRequestQueue(
newJsonObjectRequest(
Request.Method.POST,
Constantes.INSERT,
jobject,
newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
//Procesarlarespuestadelservidor
procesarRespuesta(response);
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
Log.d(TAG,"ErrorVolley:"+error.getMessage());
}
}
){
@Override
publicMap<String,String>getHeaders(){
Map<String,String>headers=newHashMap<String,String>();
headers.put("ContentType","application/json;charset=utf8");
headers.put("Accept","application/json");
returnheaders;
}
@Override
publicStringgetBodyContentType(){
return"application/json;charset=utf8"+getParamsEncoding();
}
}
);
}
/**
*Procesalarespuestaobtenidadesdeelsevidor
*
*@paramresponseObjetoJson
*/
privatevoidprocesarRespuesta(JSONObjectresponse){
try{
//Obtenerestado
Stringestado=response.getString("estado");
//Obtenermensaje
Stringmensaje=response.getString("mensaje");
switch(estado){
case"1":
//Mostrarmensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
//Enviarcdigodexito
getActivity().setResult(Activity.RESULT_OK);
//Terminaractividad
getActivity().finish();
break;
case"2":
//Mostrarmensaje
Toast.makeText(
getActivity(),
mensaje,
Toast.LENGTH_LONG).show();
//Enviarcdigodefalla
getActivity().setResult(Activity.RESULT_CANCELED);
//Terminaractividad
getActivity().finish();
break;
}
}catch(JSONExceptione){
e.printStackTrace();
}
}
/**
*Validasiloscampos{@linktitulo_input}y{@linkdescripcion_input}
*sehanrellenado
*
*@returntruesialgunoodosdeloscamposestnvacios,falsesiambos
*estncompletos
*/
publicbooleancamposVacios(){
Stringtitulo=titulo_input.getText().toString();
Stringdescripcion=descripcion_input.getText().toString();
return(titulo.isEmpty()||descripcion.isEmpty());
}
/**
*Actualizalafechadelcampo{@linkfecha_text}
*
*@paramanoAo
*@parammesMes
*@paramdiaDa
*/
publicvoidactualizarFecha(intano,intmes,intdia){
//Seteareneltextviewlafecha
fecha_text.setText(ano+""+(mes+1)+""+dia);
}
/**
*Muestraundilogodeconfirmacin
*/
publicvoidmostrarDialogo(){
DialogFragmentdialogo=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.
Json es un formato muy flexible y cmodo a la vista. Esto lo hace un excelente complemento para implementaruna API entre Android, Mysql y
Php.
Aade caching de informacin a travs de SQLite para evitar realizar gran cantidad de operaciones de red.
Usa la clase ContentProvider para completar tu patrn MVC de Red y aadir restricciones RESTful en tu aplicacin. Esto te permitir
independizar tus clases y evitar problemas en tu hilo principal de forma sencilla.