-
Crear Un Web Service Para Android Con Mysql, Php y Json
Deseas conectaruna aplicacin Android a Mysql?
Has intentado crear un web service con Php para la comunicacin
de datos de tu aplicativo web con tu aplicativo mvil android, pero
an nocomprendes bien cmo hacerlo?
Pues bien, en este artculo te mostrar algunas ideas sobre la
creacin de una aplicacin android que consuma los datos de un
servidorexterno a travs de Php, Mysql y Json.
Para ello he creado una aplicacin llamada I Wish, la cual
permite a nuestros usuarios guardar una lista de deseos y metas que
tienen en suvida. Con este ejemplo podrs ver cmo implementar la
insercin, edicin, eliminacin y consulta de datos a travs de un Web
Service.
El cdigo de la aplicacin puedes obtenerlo presionando el
siguiente botn:
Descargar Cdigo
1. Qu Es Un Web Service?Un Web Service o Servicio Web es un
aplicativoque facilita lainteroperabilidad entre varios sistemas
independientemente del del lenguaje deprogramacin o plataforma en
que fueron desarrollados. Este debe tener una interfaz basada en un
formato estndar entendible por lasmaquinas 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. Sinembargo 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 deFacebook. 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 dinmicoincorpora el uso de un lenguaje web para la gestin HTTP
que en este caso ser Php.
2. Requerimientos De La AplicacinComo leste al inicio, la
aplicacin I Wish gestiona las metas y sueos de los usuarios
permitindoles tener un registro completo. Bsicamente elalcance 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 artculospasados 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
nousaremos caching para el soporte de los datos locales como lo
hicimos al crear el lector Rss. Nos enfocaremos en como usar Volley
pararealizar las peticiones en el localhost.
3. Wireframing De La AplicacinA 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
quenecesitas. 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
fragmentoscon actividades y viceversa.
4. Crear UI Para La Aplicacin Android4.1 Disear Actividad
Principal Con Fragmento Tipo ListaDespus de haber creado t proyecto
en Android Studio vas a crear una actividad principal que contengan
un fragmento con una lista. Debidoa que vamos a aadir los
fragmentos programticamente no es necesario enfocarnos tanto en los
layouts de las actividades. Incluso puedesusar un solo layout para
todas las actividades.
activity_main.xml
-
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 elrecycler para recubrir toda la actividad y adems aadir un
Floating Action Button en la parte inferior derecha con el fin de
que el usuarioaada 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 AndroidFloatingActionButton
de makovkastarFuture Simple
Incluso podras basarte en el ejemplo del sitio de android
devepers llamado FloatingActionButtonBasic. Todo depende de ti.
Cada librera traela 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
siguientedependencia deGradle:
compile'com.melnykov:floatingactionbutton:1.3.0'
Veamos como queda el layout del fragmento principal:
fragment_main.xml
Se usa una etiqueta para implementar el FAB. Simplemente se
ubica en la parte inferior derecha y leaadimos 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 ycolorRipple 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
unabuena experiencia de usuario:
-
El patrn anterior muestra un FAB grande para representar la
insercin con unas dimensiones reglamentarias de 56dpx56dp. El icono
que llevadebe mantenerse en 24dpx24dp.
Tambin podemos tener un FAB mini con dimensiones de 40dpx40dp,
donde el icono se mantiene sobre 24dpx24dp.
4.2 Disear Actividad De Detalle Con Fragmento
PersonalizadoAcudiendo a los estilos de layouts en Material Design,
dividiremos el fragmento de detalle en dos pasos. El primero ser
una ImageView alusivoa la categora de la meta y el segundo ser una
hoja para sus datos completos. Adicionalmente aadiremos un Float
Button Action para laedicin de la meta.
fragment_detail.xml
-
El FAB debe usar el atributofab:fab_type=mini para usar el botn
mini.
En este caso se us como nodo un LinearLayout con dos
RelativeLayout dentro. Esto nos permite dividir por pesos (weight)
la ocupacin deespacio entre ambos layouts y as mantener una
proporcin.
-
4.3 Disear Actividad Con FormularioLa insercin y edicin requiere
de la proyeccin de un formulario que contenga los controles
necesarios para que el usuario especifique lainformacin
personalizada que desea almacenar en la base de datos. Para ello
debes crear un layout con los datos que viste en losrequerimientos
de la aplicacin con las respectivos views para obtener la
informacin.
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. Ladescripcin es igual, necesita un campo de texto. La
fecha limite puede ser obtenida a travs de un DatePicker y para la
categora quetiene undominio de varias opciones, puedes usar un
Spinner .
Veamos:
-
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"/>
-
4.4 Disear Layout Personalizado De Los ItemsLa organizacin de
los atributos de cada meta dentro de los tems de la lista debe ser
un resumen de sus caractersticas principales. Puedesdejar la
descripcin solo para la actividad del detalle y eliminarlo de la
presentacin en la lista.
item_list.xml
-
El anterior diseo se vera de la siguiente forma:
5. Codificacin Del Web Service En PhpAntes de crear la aplicacin
Android debes desarrollar primero tu Web Service con cualquiera de
los estndares que te interesen. El alcance deeste tutorial no
abarca el uso de restricciones REST, SOAP, RPC o sus parecidos.
Simplemente vers cmo crear las implementaciones Phpnecesarias para
realizar operaciones sobre una base de datos en Mysql a travs de
peticiones GET y POST.
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
deunservidorApache 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
einterpretar scripts de Php.
5.1 Diseo E Implementacin De La Base De DatosDisear base de
datos: Siya lo has notado, la base de datos de la aplicacin I Wish
solo tiene una entidad que representa a los registros de lasmetas.
Esto reduce ampliamente el diseo de bases de datos en el problema.
No obstante, si tu proyecto es mas complicado, asegrate detener una
buena metodologa de diseo de bases de datos antes de crear el web
service,
Meta debe tener los atributos que hemos venido viendo ms una
llave primaria que mantenga la integridad de los datos. Observa el
siguienteminidiagrama entidad-relacin:
Crear base de datos: Para implementar la base de datos lo
primero que debes hacer es crear una nueva base de datos en la
aplicacinphpMyAdmin 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.
Puedeshacerlo 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');
5.2 Crear Cdigo Php Para Consumir DatosEn primera instancia crea
una conexin a la base de datos Mysql con la interfaz que mas se
acomode a tus necesidades. En mi caso voy a crearuna conexin con
PDO, la cual me permite proteger los datos de inyecciones sql.
Luego de eso crea una clase que mapee la estructura de la tabla
meta . El objetivo de ello es proveerla de comportamientos de
insercin,actualizacin, eliminacin y consulta a travs de la conexin
a la base de datos.
Finalmente implementar scripts Php para gestionar las peticiones
que lanzan los clientes. La idea es parsear los datos en formato
Json paraque nuestra aplicacin Android interprete los resultados de
forma legible.
Paso #1: Crear conexin a la base de datos con PDOEl uso de PDO
depende del enfoque que tengan tus proyectos, puedes crear una
clase que represente la conexin hacia la base de datos osimplemente
crear una nueva conexin en cada script de Php que tengas.
Para este caso te compartir un patrn singleton de PDO para
limitar el nmero de aperturas a la base de datos en una sola. Con
ello podremosdisponer de un solo objeto a travs de todo el
proyecto.
No obstante hay patrones de diseo muy interesantes que puedes
consultar en la web. Por ejemplo el repositorio delusuario indieteq
engithub. l se enfoca en la implementacin del CRUD de una forma muy
sencilla y orientada a objetos.
Veamosel resultado del patrn singleton:
Database.php
-
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. Conello 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 pordefecto "root" y sin contrasea alguna.
mysql_login.php
Adicionalmente debes aadir al cuarto parmetro del constructor de
PDO la indicacin SETNAMESUTF8 para el servidor. Esto permite que
losdatos de la base de datos vengan codificados en este formato
para evitar problemas de compatibilidad.
Paso #4: Crear clase para las metasEn este paso vas a modelar en
una clase a la tabla "meta" de tal forma que aplique el CRUD sobre
los datos a travs de la clase Database . Enesencia necesitas un
mtodo para obtener todos los registros, uno para la insercin, otro
para eliminacin, tambin para la actualizacin y unmtodo que permita
obtener del detalle de un solo registro.
Meta.php
-
returnfalse;}}
/***Obtieneloscamposdeunametaconunidentificador*determinado**@param$idMetaIdentificadordelameta*@returnmixed*/publicstaticfunctiongetById($idMeta){//Consultadelameta$consulta="SELECTidMeta,titulo,descripcion,prioridad,fechaLim,categoriaFROMmetaWHEREidMeta=?";
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//parapresentarloenlarespuestaJsonreturn1;}}
/***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 deinyecciones que puedan atentar contra la seguridad de
los datos.
Paso #5: Crear un scriptpara obtener todas las metasPara
retornar todos los registros que existen en la tabla "meta"
usaremos el mtodo getAll() de la clase Meta . La trata de la
peticin seguirala siguiente lgica:
1. Comprobar que la peticin se realiz con el mtodo GET.2.
Obtener todos los registros.3. La obtencin tuvo xito?
A. SI -> Retornar objeto Json con los datosB. NO ->
Retornar objeto Json con mensaje de error
Tenemos un flujo que se asegura de satisfacer el debido
resultado y aquellos resultados adversos. La trata de errores debe
comprender todos
-
aquellos posibles caminos que puedan generarse como una peticin
fallida, la falla de autenticacin, la no existencia del recurso, la
nodisponibilidad del servidor, etc. En resumen, contempla todas las
fallas tanto del lado del servidor (cdigos5xx) como las del cliente
(cdigos4xx).
No obstante este ejemplo se basa en el comportamiento ideal de
nuestro servidor local. Donde solo reportaremos aquellas anomalas
quesucedan en la base de datos, asumiendo que la respuesta siempre
tendr un cdigo de estado 200. Esto permitir trackear si nuestro
webservice 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
trabajote 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 puedanser interpretados del lado del cliente. Esto te ser
posible usando las funciones json_encode() y json_decode() . La
primera parsea un tipo dedatoa un string en formato json y la
segunda es el procedimiento contrario.
Veamos nuestro servicio de obtencin:
obtener_metas.php
-
"prioridad":"Alta","fechaLim":"20150525","categoria":"Espiritual"}]}
Por el otro lado, la respuesta de error simplemente sera:
{"estado":"2","mensaje":"Haocurridounerror"}
Cambiando de temaQu pasa si quieres filtrar los registros?
Por ejemplo
Puede que requieras en orden ascendente o descendente de los
registros con respecto a un campo. O simplemente obtener las metas
que vande una fecha a otra.
Para tener en cuenta estos casos, puedes consultar los datos de
acuerdo a una serie de parmetros establecidos en la API. Esto
quiere decirque podras incluir en el cuerpo de la peticin variables
que acten como filtros enla seleccin.Sin embargo dicho tema est
fuera del alcancede nuestro artculo.
El diseo RESTful para Web Services provee reglas supremamente
estilizadas para filtrar y consultar datos de forma ms sencilla
queestableciendo filtros manuales.
Paso #6: Crear un script php para consultar el detalle de una
metaEl 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 elmtodo getById() de Meta para conseguir el array
necesario.
Veamos:
-
);}
}else{//Enviarrespuestadeerrorprintjson_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 quela 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 enun 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\u00e1ximoparadesarrollarmitesisdegrado""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. Conello 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.
Paraeste caso envas tu cdigo 3 indicando este mensaje.
{"estado":"3","mensaje":"Senecesitaunidentificador"}
Paso #7: Crear un script php para la insercin de metasLa
insercin requiere el uso del mtodo POST para la recepcin de los
datos de la meta. Por lo quedebemos leer los datos en formato
Json:
-
$body['fechaLim'],$body['categoria'],$body['prioridad']);
if($retorno){//Cdigodexitoprintjson_encode(array('estado'=>'1','mensaje'=>'Creacinexitosa'));}else{//Cdigodefallaprintjson_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 esposible consultando el flujo con
file_get_contents() , que convierte un archivo a string.
Obviamentees necesario queuses la convencinphp://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
arregloasociativo 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
1contiene un mensaje de xito.
{"estado":"1","mensaje":"Creacinxitosa"}
De lo contrario usa un mensaje general de error.
{"estado":"2","mensaje":"Creacinfallida"}
Paso #8: Crear un scritp Php para la actualizacin de metasLa
actualizacin es casi idntica a la insercin, solo que esa vez
debemos obtener el identificador de la meta para saber que registro
actualizar.De resto procedemos con el mtodo update() de Meta sin
problemas:
-
//Cdigodexitoprintjson_encode(array('estado'=>'1','mensaje'=>'Actualizacinexitosa'));}else{//Cdigodefallaprintjson_encode(array('estado'=>'2','mensaje'=>'Actualizacinfallida'));}}
Es resultado de xito es similar y repetitivo para la
actualizacin:
{"estado":"1","mensaje":"Actualizacinxitosa"}
Al igual que el objeto Json de error:
{"estado":"2","mensaje":"Actualizacinfallida"}
Paso #9: Crear un script Php para la eliminacin de metasLa
eliminacin se basa enel mtodo POST para enviar el identificador de
la meta que se necesita eliminar de la base de datos. Esta
vezusaremos el mtodo delete() de Meta .
-
Y los errores se mostraran as:
{"estado":"2","mensaje":"Eliminacinfallida"}
6. Codificacin De La Aplicacin AndroidUna vez creado el Web
Service, es hora de construir nuestra aplicacin gestora de metas.
Recuerda que es necesario que crees los siguienteselementos e
interacciones de la arquitectura:
Unpatrn singleton Volley para las peticiones (o un cliente
HttpURLConnection si lo deseas).Crear la peticin personalizada para
tratar respuestas Json (el cdigo ya fue tratado en el artculo de
Volley).Crear un adaptador que procese los elementos del recycler
view.Tratar los eventos para la comunicacin de datos a travs de los
controles.
La idea es enfocarnos en el uso del servicio web y aprovechar al
mximo las peticiones Json que nos provee Volley.
Paso #1:Crear Patrn Singleton VolleyEste paso ya hace parte de
nuestra rutina para gestionar peticiones HTTP. As que reutilizars
el singleton de artculos pasados para simplificarprocesos. La nica
diferencia que tendrs ser la ausencia del ImageLoader 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
importandroid.content.Context;
importcom.android.volley.Request;importcom.android.volley.RequestQueue;importcom.android.volley.toolbox.Volley;
/***CreadoporHermosaProgramacin.**ClasequerepresentaunclienteHTTPVolley*/
publicfinalclassVolleySingleton{
//AtributosprivatestaticVolleySingletonsingleton;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*@paramResultadofinaldetipoT*/publicvoidaddToRequestQueue(Requestreq){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 lasdirecciones 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 elemulador de android usa la direccin 10.0.2.2 . Aqu el
sitio oficial te habla un poco mas sobre ests convenciones de
direcciones paraoperaciones en la web.
Paso #2:Crear fuente de datos para las metasNuestro adaptador
necesita alimentarse de una lista de elementos que le proporcionen
la informacin necesaria para proyectar el layout. Espor eso que
tienes que crearuna clase que represente la existencia de una meta
en la aplicacin Android.
Crea una nueva clase en Android Studio y llmala Meta . Pon todos
aquellos atributos puestos en la base de datos:
/***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
almomento de validar si hay cambios en los datos de los formularios
cuando el usuario interacta con ellos. Lo que permitir determinar
si hayque lanzar dilogos de confirmacin antes de aplicar
acciones.
Paso #3:Crear adaptador personalizado para el Recycler ViewEn
este paso debes relacionar el layout item_list.xml con los datos
que tenga cada objeto Meta de la fuente de datos.
No olvides usar le patrn ViewHolder para reducir la cantidad de
llamadas del mtodo findViewById().
Adems de ello tenemos que implementar sobre cada view holderuna
escucha OnClickListener para recibir los eventos del usuario en la
lista.Para ello se crear una interfaz intermediaria entre el
ViewHolder y el adaptador, de tal forma que cuando se active el
evento onClick() esteinicie la actividad de detalle.
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.AdapterimplementsItemClickListener{
/***Listadeobjetos{@linkMeta}querepresentanlafuentededatos*deinflado*/privateListitems;
/*Contextodondeactuaelrecyclerview*/privateContextcontext;
publicMetaAdapter(Listitems,Contextcontext){this.context=context;this.items=items;}
@OverridepublicintgetItemCount(){returnitems.size();}
@OverridepublicMetaViewHolderonCreateViewHolder(ViewGroupviewGroup,inti){Viewv=LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_list,viewGroup,false);returnnewMetaViewHolder(v,this);}
@OverridepublicvoidonBindViewHolder(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*/@OverridepublicvoidonItemClick(Viewview,intposition){DetailActivity.launch((Activity)context,items.get(position).getIdMeta());}
publicstaticclassMetaViewHolderextendsRecyclerView.ViewHolderimplementsView.OnClickListener{//CamposrespectivosdeunitempublicTextViewtitulo;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);}
@OverridepublicvoidonClick(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
seimplementa 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 conSQLite 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 puedasinteriorizar el conocimiento y practicar
esta metodologa. Por ahora no te preocupes en la arquitectura u
optimizaciones.
Paso #4: Realizar Peticin Para Poblar La ListaYa has construido
un Web Service en Php con todas lascaractersticas necesariasy has
desarrollado los componentes de software para que laaplicacin
Android comience a funcionar.
El fragmento de la lista lo iniciaremos dinmicamente a travs del
mtodo onCreate() de MainActivity :
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{
@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
//Creacindelfragmentoprincipalif(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 apenas 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(){}
@OverridepublicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){
Viewv=inflater.inflate(R.layout.fragment_main,container,false);
lista=(RecyclerView)v.findViewById(R.id.reciclador);lista.setHasFixedSize(true);
//UsarunadministradorparaLinearLayoutlManager=newLinearLayoutManager(getActivity());lista.setLayoutManager(lManager);
-
//CargardatoseneladaptadorcargarAdaptador();
//ObtenerinstanciadelFABfab=(com.melnykov.fab.FloatingActionButton)v.findViewById(R.id.fab);
//AsignarescuchaalFABfab.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){//IniciaractividaddeinsercingetActivity().startActivityForResult(newIntent(getActivity(),InsertActivity.class),3);}});
returnv;}
/***Cargaeladaptadorconlasmetasobtenidas*enlarespuesta*/publicvoidcargarAdaptador(){//PeticinGETVolleySingleton.getInstance(getActivity()).addToRequestQueue(newJsonObjectRequest(Request.Method.GET,Constantes.GET,null,newResponse.Listener(){
@OverridepublicvoidonResponse(JSONObjectresponse){//ProcesarlarespuestaJsonprocesarRespuesta(response);}},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(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"JsonJSONArraymensaje=response.getJSONArray("metas");//ParsearconGsonMeta[]metas=gson.fromJson(mensaje.toString(),Meta[].class);//Inicializaradaptadoradapter=newMetaAdapter(Arrays.asList(metas),getActivity());//Setearadaptadoralalistalista.setAdapter(adapter);break;case"2"://FALLIDOStringmensaje2=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
obtencinobtener_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 elparsing. 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 parseadirectamente a un arreglo de objetos Meta a travs de
la librera Gson.
Recuerda que el adaptador recibe una serie de metas en formato
List , por lo que usaremos la clase Arrays para convertir el
arreglo demetas a lista. Con eso listo ya es posible instanciar el
adaptador y asignarlo al recycler.
Paso #5: Ver Detalle De Items En Otra ActividadUna vez nuestro
adaptador poblado, ya podemos ver el detalle de la descripcin en
DetailActivity.java con un fragmento alojado.
Para ello inicia el fragmento dinmicamente en onCreate():
DetailActivity.java
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;}
@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
if(getSupportActionBar()!=null){//DehabilitartitulodelaactividadgetSupportActionBar().setDisplayShowTitleEnabled(false);//Setearcono"X"comoUpbuttongetSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_close);}
//Retenerinstanciaif(getIntent().getStringExtra(EXTRA_ID)!=null)idMeta=getIntent().getStringExtra(EXTRA_ID);
if(savedInstanceState==null){getSupportFragmentManager().beginTransaction().add(R.id.container,DetailFragment.createInstance(idMeta),"DetailFragment").commit();}}
@OverridepublicbooleanonCreateOptionsMenu(Menumenu){getMenuInflater().inflate(R.menu.menu_detail,menu);returntrue;}
@OverridepublicbooleanonOptionsItemSelected(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
unainstancia 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 seapedida con getIntent() .
A los fragmentos que hemos iniciado dinmicamente se les est
asignando una etiqueta que los diferencie de los otros. Esto es de
supremaimportancia, ya que necesitamos obtener sus instancias
cuando la actividad se comunique con ellos.
Paso #9: Consultar Detalles De Cada Item
-
Ahora pregntate que debe hacer el fragmento de detalle
Dependiendo del enfoque de experiencia de usuario que tengas,
puede que sean muchas cosas. Sin embargopara este ejemplo el
usuariotiene dos caminos evidentes:
Cerrarel detallecon el Up Button o Back ButtonEditar la meta a
travs del Floating Action Button.
La primera interaccin ya la tenemos cubierta en DetailActivity ,
ya que hemos sobrescrito el comportamiento del Up Button por el
cierre dela actividad.
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 alFAB 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 delIntent.
Recuerda que la convencin createInstance() inicializa un nuevo
fragmento con los extras necesarios para su funcionamiento. Por lo
que enonCreateView() 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;}
@OverridepublicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){Viewv=inflater.inflate(R.layout.fragment_detail,container,false);
//Obtencindeviewscabecera=(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);
//SetearescuchaparaelfabeditButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){//IniciaractividaddeactualizacinIntenti=newIntent(getActivity(),UpdateActivity.class);i.putExtra(EXTRA_ID,extra);getActivity().startActivityForResult(i,Constantes.CODIGO_ACTUALIZACION);}});
//Obtenerextradelintentdeenvoextra=getArguments().getString(EXTRA_ID);
//CargardatosdesdeelwebservicecargarDatos();
returnv;}
/***Obtienelosdatosdesdeelservidor*/publicvoidcargarDatos(){
//AadirparmetroalaURLdelwebserviceStringnewURL=Constantes.GET_BY_ID+"?idMeta="+extra;
//RealizarpeticinGET_BY_IDVolleySingleton.getInstance(getActivity()).addToRequestQueue(newJsonObjectRequest(Request.Method.GET,newURL,null,newResponse.Listener(){
@OverridepublicvoidonResponse(JSONObjectresponse){//ProcesarrespuestaJsonprocesarRespuesta(response);}},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(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");
//ParsearobjetoMetameta=gson.fromJson(object.toString(),Meta.class);
//Asignarcolordelfondoswitch(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;}
//Seteandovaloresenlosviewstitulo.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,
entoncesseteamos los valores correspondientes de cada view.
Paso #10: Realizar peticin POST para editar detalles de la
metaPara la edicin hemos creado la actividad UpdateActivity , la
cual mostrar el formulario de recoleccin con losdatos de la una
meta.
La lgica funciona as: Una vez el usuario haya modificado los
datos, entonces puede confirmar sus datos presionando el action
button de checkque usaremos para la confirmacin. Si no est de
acuerdo o se arrepiente de ello, entonces puede descartar los
cambios con el segundo actionbutton.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
No incluiremos el guardado de cambios, ya que lo implementaremos
en el Up Button.
Teniendo en cuenta esa apreciacin las tareas que tienes
porimplementar son:
Cargar los datos de la meta en los componentes del
formulario.Habilitar la contribucin a la action bar.Manejar los
eventos en cada action button.Implementar la insercin, eliminacin y
borrado de las metas.Lanzar dilogos para confirmar la eliminacin y
el descarte de cambios.Usar Toasts para afianzar la confirmacin de
las operaciones.
Veamos la solucin:
UpdateFragment.java
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;}
@OverridepublicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){
//InflandolayoutdelfragmentoViewv=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(){@OverridepublicvoidonClick(Viewv){DialogFragmentpicker=newDatePickerFragment();picker.show(getFragmentManager(),"datePicker");
}});
//ObtenervalorextraidMeta=getArguments().getString(EXTRA_ID);
if(idMeta!=null){cargarDatos();}
returnv;}
/***Obtienelosdatosdesdeelservidor*/privatevoidcargarDatos(){//AadiendoidMetacomoparmetroalaURLStringnewURL=Constantes.GET_BY_ID+"?idMeta="+idMeta;
//ConsultareldetalledelametaVolleySingleton.getInstance(getActivity()).addToRequestQueue(newJsonObjectRequest(Request.Method.GET,newURL,null,newResponse.Listener(){
@OverridepublicvoidonResponse(JSONObjectresponse){//ProcesalarespuestaGET_BY_IDprocesarRespuestaGet(response);}},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(VolleyErrorerror){Log.d(TAG,"ErrorVolley:"+error.getMessage());}}));}
/***Procesalarespuestadeobtencinobtenidadesdeelsevidor**/privatevoidprocesarRespuestaGet(JSONObjectresponse){
try{Stringestado=response.getString("estado");
switch(estado){case"1":JSONObjectmeta=response.getJSONObject("meta");//GuardarinstanciametaOriginal=gson.fromJson(meta.toString(),Meta.class);//SetearvaloresdelametacargarViews(metaOriginal);break;
case"2":Stringmensaje=response.getString("mensaje");
-
//MostrarmensajeToast.makeText(getActivity(),mensaje,Toast.LENGTH_LONG).show();//EnviarcdigodefallagetActivity().setResult(Activity.RESULT_CANCELED);//TerminaractividadgetActivity().finish();break;}}catch(JSONExceptione){e.printStackTrace();}}
/***Cargalosdatosinicialesdelformularioconlos*atributosdeunobjeto{@linkMeta}**@parammetaInstancia*/privatevoidcargarViews(Metameta){//Seteandovaloresdelarespuestatitulo_input.setText(meta.getTitulo());descripcion_input.setText(meta.getDescripcion());fecha_text.setText(meta.getFechaLim());
//ObteniendoaccesoalosarraydestringsparacategoriasyprioridadesString[]categorias=getResources().getStringArray(R.array.entradas_categoria);String[]prioridades=getResources().getStringArray(R.array.entradas_prioridad);
//Obteniendolaposicindelspinnercategoriasintposicion_categoria=0;for(inti=0;i
-
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);}
@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setHasOptionsMenu(true);//ContribucinalaAB}
@OverridepublicbooleanonOptionsItemSelected(MenuItemitem){intid=item.getItemId();
switch(id){caseandroid.R.id.home://CONFIRMARif(!validarCambios())guardarMeta();else//Terminaractividad,yaquenohaycambiosgetActivity().finish();returntrue;
caseR.id.action_delete://ELIMINARmostrarDialogo(R.string.dialog_delete_msg);break;
caseR.id.action_discard://DESCARTARif(!validarCambios()){mostrarDialogo(R.string.dialog_discard_msg);}else//Terminaractividad,yaquenohaycambiosgetActivity().finish();break;
};
returnsuper.onOptionsItemSelected(item);}
/***Guardaloscambiosdeunametaeditada.**Siestenmodoinsercin,entoncescreaunanueva*metaenlabasededatos*/privatevoidguardarMeta(){
//ObtenervaloresactualesdeloscontrolesfinalStringtitulo=titulo_input.getText().toString();finalStringdescripcion=descripcion_input.getText().toString();finalStringfecha=fecha_text.getText().toString();finalStringcategoria=categoria_spinner.getSelectedItem().toString();finalStringprioridad=prioridad_spinner.getSelectedItem().toString();
HashMapmap=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);
//CrearnuevoobjetoJsonbasadoenelmapaJSONObjectjobject=newJSONObject(map);
//DepurandoobjetoJson...Log.d(TAG,jobject.toString());
-
//ActualizardatosenelservidorVolleySingleton.getInstance(getActivity()).addToRequestQueue(newJsonObjectRequest(Request.Method.POST,Constantes.UPDATE,jobject,newResponse.Listener(){@OverridepublicvoidonResponse(JSONObjectresponse){procesarRespuestaActualizar(response);}},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(VolleyErrorerror){Log.d(TAG,"ErrorVolley:"+error.getMessage());}}
){@OverridepublicMapgetHeaders(){Mapheaders=newHashMap();headers.put("ContentType","application/json;charset=utf8");headers.put("Accept","application/json");returnheaders;}
@OverridepublicStringgetBodyContentType(){return"application/json;charset=utf8"+getParamsEncoding();}});
}
/***Procesatodoslastareasparaeliminar*unametaenlaaplicacin.Estemtodosoloseusa*enlaedicin*/publicvoideliminarMeta(){
HashMapmap=newHashMap();//MAPEO
map.put("idMeta",idMeta);//Identificador
JSONObjectjobject=newJSONObject(map);//ObjetoJson
//EliminardatosenelservidorVolleySingleton.getInstance(getActivity()).addToRequestQueue(newJsonObjectRequest(Request.Method.POST,Constantes.DELETE,jobject,newResponse.Listener(){@OverridepublicvoidonResponse(JSONObjectresponse){//ProcesarlarespuestaprocesarRespuestaEliminar(response);
}},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(VolleyErrorerror){Log.d(TAG,"ErrorVolley:"+error.getMessage());}}
){@OverridepublicMapgetHeaders(){Mapheaders=newHashMap();headers.put("ContentType","application/json;charset=utf8");headers.put("Accept","application/json");
-
returnheaders;}
@OverridepublicStringgetBodyContentType(){return"application/json;charset=utf8"+getParamsEncoding();}});}
/***Procesalarespuestadeeliminacinobtenidadesdeelsevidor*/privatevoidprocesarRespuestaEliminar(JSONObjectresponse){
try{//ObtenerestadoStringestado=response.getString("estado");//ObtenermensajeStringmensaje=response.getString("mensaje");
switch(estado){case"1"://MostrarmensajeToast.makeText(getActivity(),mensaje,Toast.LENGTH_LONG).show();//EnviarcdigodexitogetActivity().setResult(203);//TerminaractividadgetActivity().finish();break;
case"2"://MostrarmensajeToast.makeText(getActivity(),mensaje,Toast.LENGTH_LONG).show();//EnviarcdigodefallagetActivity().setResult(Activity.RESULT_CANCELED);//TerminaractividadgetActivity().finish();break;}}catch(JSONExceptione){e.printStackTrace();}
}
/***Procesalarespuestadeactualizacinobtenidadesdeelsevidor*/privatevoidprocesarRespuestaActualizar(JSONObjectresponse){
try{//ObtenerestadoStringestado=response.getString("estado");//ObtenermensajeStringmensaje=response.getString("mensaje");
switch(estado){case"1"://MostrarmensajeToast.makeText(getActivity(),mensaje,Toast.LENGTH_LONG).show();//EnviarcdigodexitogetActivity().setResult(Activity.RESULT_OK);//TerminaractividadgetActivity().finish();break;
-
case"2"://MostrarmensajeToast.makeText(getActivity(),mensaje,Toast.LENGTH_LONG).show();//EnviarcdigodefallagetActivity().setResult(Activity.RESULT_CANCELED);//TerminaractividadgetActivity().finish();break;}}catch(JSONExceptione){e.printStackTrace();}
}
/***Actualizalafechadelcampo{@linkfecha_text}**@paramanoAo*@parammesMes*@paramdiaDa*/publicvoidactualizarFecha(intano,intmes,intdia){//Seteareneltextviewlafechafecha_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 teexplico 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
elfragmento. Si existe un valor extra, lanzamos la misma peticin
que hemos usado para conseguir el detalle de la meta con el
mtodocargarDatos() .
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
unaestructura switch que permitiera la ejecucin del mtodo
correspondiente a la accin. Recuerda usar onHasOptionMenu() en
onCreate() paraque 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
surealizacin. Estos son: guardarMeta() y borrarMeta() .El primer
mtodo realiza una peticin POST con la respectiva URL del servicio
deactualizacin, 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
basede datos. Puedes encontrar la implementacin completa
descargando el cdigo en la parte superior del artculo.
Paso #11: Realizar peticin para insertar nuevos registrosLa
insercin de nuevas metas la crearemos en una nueva actividad
llamada InsertActivity junto a un fragmento InsertFragment .
Haremos
-
exactamente lo mismo que hemos venido haciendo.
Iniciaremos el fragmento y estaremos a la espera de que el
usuario guarde los datos o los descarte. Veamos:
InsertActivity.java
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;
publicclassInsertActivityextendsAppCompatActivityimplementsDatePickerFragment.OnDateSelectedListener,ConfirmDialogFragment.ConfirmDialogListener{
@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
if(getSupportActionBar()!=null)getSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_done);
//Creacindelfragmentodeinsercinif(savedInstanceState==null){getSupportFragmentManager().beginTransaction().add(R.id.container,newInsertFragment(),"InsertFragment").commit();}}
@OverridepublicbooleanonCreateOptionsMenu(Menumenu){getMenuInflater().inflate(R.menu.menu_form,menu);returntrue;}
@OverridepublicvoidonDateSelected(intyear,intmonth,intday){InsertFragmentinsertFragment=(InsertFragment)getSupportFragmentManager().findFragmentByTag("InsertFragment");
if(insertFragment!=null){insertFragment.actualizarFecha(year,month,day);}}
@OverridepublicvoidonDialogPositiveClick(DialogFragmentdialog){InsertFragmentinsertFragment=(InsertFragment)getSupportFragmentManager().findFragmentByTag("InsertFragment");
if(insertFragment!=null){finish();//Finalizaractividaddescartandocambios}}
@OverridepublicvoidonDialogNegativeClick(DialogFragmentdialog){InsertFragmentinsertFragment=(InsertFragment)getSupportFragmentManager().findFragmentByTag("InsertFragment");
if(insertFragment!=null){//Nadaporelmomento}}}
-
Ahora el fragmento de insercin tiene las siguientes
caractersticas:
InsertFragment.java
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(){}
@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);//HabilitaralfragmentoparacontribuirenlaactionbarsetHasOptionsMenu(true);}
@OverridepublicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){//InflandolayoutdelfragmentoViewv=inflater.inflate(R.layout.fragment_form,container,false);
//Obtencindeinstanciascontrolestitulo_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(){@OverridepublicvoidonClick(Viewv){DialogFragmentpicker=newDatePickerFragment();picker.show(getFragmentManager(),"datePicker");
}});
returnv;}
@OverridepublicvoidonCreateOptionsMenu(Menumenu,MenuInflaterinflater){super.onCreateOptionsMenu(menu,inflater);//Removerelactionbuttondeborrarmenu.removeItem(R.id.action_delete);}
@OverridepublicbooleanonOptionsItemSelected(MenuItemitem){intid=item.getItemId();
switch(id){caseandroid.R.id.home://CONFIRMARif(!camposVacios())guardarMeta();elseToast.makeText(getActivity(),"Completaloscampos",Toast.LENGTH_LONG).show();returntrue;
caseR.id.action_discard://DESCARTARif(!camposVacios())mostrarDialogo();elsegetActivity().finish();break;
}
returnsuper.onOptionsItemSelected(item);}
/***Guardaloscambiosdeunametaeditada.**Siestenmodoinsercin,entoncescreaunanueva*metaenlabasededatos*/publicvoidguardarMeta(){
//ObtenervaloresactualesdeloscontrolesfinalStringtitulo=titulo_input.getText().toString();finalStringdescripcion=descripcion_input.getText().toString();finalStringfecha=fecha_text.getText().toString();finalStringcategoria=categoria_spinner.getSelectedItem().toString();finalStringprioridad=prioridad_spinner.getSelectedItem().toString();
HashMapmap=newHashMap();//Mapeoprevio
map.put("titulo",titulo);map.put("descripcion",descripcion);map.put("fechaLim",fecha);map.put("categoria",categoria);map.put("prioridad",prioridad);
//CrearnuevoobjetoJsonbasadoenelmapaJSONObjectjobject=newJSONObject(map);
//DepurandoobjetoJson...Log.d(TAG,jobject.toString());
-
//ActualizardatosenelservidorVolleySingleton.getInstance(getActivity()).addToRequestQueue(newJsonObjectRequest(Request.Method.POST,Constantes.INSERT,jobject,newResponse.Listener(){@OverridepublicvoidonResponse(JSONObjectresponse){//ProcesarlarespuestadelservidorprocesarRespuesta(response);}},newResponse.ErrorListener(){@OverridepublicvoidonErrorResponse(VolleyErrorerror){Log.d(TAG,"ErrorVolley:"+error.getMessage());}}
){@OverridepublicMapgetHeaders(){Mapheaders=newHashMap();headers.put("ContentType","application/json;charset=utf8");headers.put("Accept","application/json");returnheaders;}
@OverridepublicStringgetBodyContentType(){return"application/json;charset=utf8"+getParamsEncoding();}});
}
/***Procesalarespuestaobtenidadesdeelsevidor**@paramresponseObjetoJson*/privatevoidprocesarRespuesta(JSONObjectresponse){
try{//ObtenerestadoStringestado=response.getString("estado");//ObtenermensajeStringmensaje=response.getString("mensaje");
switch(estado){case"1"://MostrarmensajeToast.makeText(getActivity(),mensaje,Toast.LENGTH_LONG).show();//EnviarcdigodexitogetActivity().setResult(Activity.RESULT_OK);//TerminaractividadgetActivity().finish();break;
case"2"://MostrarmensajeToast.makeText(getActivity(),mensaje,Toast.LENGTH_LONG).show();//EnviarcdigodefallagetActivity().setResult(Activity.RESULT_CANCELED);//TerminaractividadgetActivity().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){//Seteareneltextviewlafechafecha_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
hayacompletado. Si te fijas en el procesamiento de los eventos
sobre la action bar, puedes ver que existe la posibilidad de
guardar y descartar losdatos.
Ambos se basan en la validacin de los campos del formulario que
requieren texto escrito por parte del usuario. Para ello se cre el
mtodocamposVacios() . 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
descartarcambios sin ver un dilogo si ya ha escrito algn dato.
Ejecutar Proyecto Completo En Android StudioRecuerda que puedes
descargar el proyecto completo con el botn que tienes en el inicio
del articulo. Al final, luego de haber seguido todos lospasos la
aplicacin se ver as:
-
ConclusionesUsar un Web Service en Php permite compartir datos
entre tus aplicativos externos y tus aplicaciones android para
mantener un proyectointegral. 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 yPhp.
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
permitirindependizar tus clases y evitar problemas en tu hilo
principal de forma sencilla.