JQuery Mobile: Desarrollo de aplicaciones y portales web para dispositivos móviles
Índice de contenidos
- Introducción
- Objetivo: Usabilidad y velocidad
- Ejemplos de webs construidas con JQuery Mobile
- Aprendiendo lo básico de JQuery, haciendo ejemplos
- ¿Cómo detecto si acceden a través de un móvil o de un ordenador tradicional?
- Referencias y enlaces interesantes
- Otros frameworks o librerías alternativos para el desarrollo web para móviles
- Conclusiones
Introducción
Actualmente gracias a las tarifas planas es muy común navegar por Internet a través de nuestros teléfonos móviles, smartphones y tablets (y seguramente pronto será también la televisión).
Sin embargo, la gran mayoría de la información accesible en Internet está pensada teniendo en mente que van ser accedidas a través de un ordenador de escritorio, pero poco a poco, somos más los que accedemos a través de nuestros tablets, smartphones,.. dispositivos mucho más limitados en cuanto a ancho de banda, capacidad de procesamiento, resolución de pantalla y batería, lo cual hace que la navegación sea menos agradable de lo que el usuario espera.
De ahí que muchos negocios hayan decidido dar un valor añadido creando una versión web específica para dispositivos móviles que satisfaga las necesidades de sus usuarios/clientes.
JQuery Mobile, es una de las opciones (otras alternativas) que tenemos los desarrolladores a la hora de crear un web para
móvil, se trata de una librería javascript y un conjunto de estilos e imágenes con las que
podremos crear un website (html, css y js) para los clientes más extendidos: Android, Blackberry, iPhone, iPad, WebOS, etc. Listado de plataformas compatibles.
Objetivo: Usabilidad y velocidad
Aunque siempre debería de ser importante, en el caso de los webs para tablets, smartphones, etc. es de vital importancia debido a la escasez de recursos (Red, RAM, CPU, etc), usar técnicas de optimización así como generar
un código HTML de calidad que además permita al usuario con una pantalla de reducidas dimensiones trabajar de forma usable.
Por ejemplo, cualquier web se podrían mejorar muchísimo realizando cosas como:
- Usar imágenes optimizados para que ocupen el mínimo número de KB.
- Comprimir y optimizar CSS, HTML y Javascript.. quitar espacios, funciones no usadas, fusionar varios css en uno, etc.
- Reducir el número de imágenes externas para disminuir el número de peticiones HTTP. Por ejemplo, creando CSS image sprites.
- No meter librerías de JavaScript que no aportan apenas nada e incrementan el tiempo de carga. (Usa correctamente defer y async)
- Usar comprensión en la transferencia de datos cuando sea posible (gzip ó deflate).
- Usar correctamente las cabeceras del protocolo HTTP para cachear los recursos estáticos.
- Usar AppCache y LocalStorage de HTML5.
- etc.
Pues bien, casi todo esto hay que tenerlo en cuenta si deseas una experiencia agradable con tiempos de respuesta rápidos en clientes con conexiones lentas y recursos limitados
Además, Google tiene en cuenta para el SEO el tiempo de carga de una página, penalizando las webs que tardan más en cargarse.
Si quieres analizar una web y ver la calidad en cuanto tiempo de carga de la misma puedes hacerlo usando la herramienta online gratuita pagespeed
(por cortesía de Google).
En el siguiente enlace os dejo una charla sobre aspectos de optimización a tener en cuenta cuando desarrollamos aplicaciones web para móviles, Mobile Web Performance
El usuario debería de poder usar la aplicación sin tener que estar usando zooms o haciendo scrolls de pantalla, es decir, debería de ser lo más parecido posible a una
aplicación nativa.
Ejemplos de webs construidas con JQuery Mobile
- http://www.jqmgallery.com, un repertorio de bonitas webs construidas con JQuery Mobile
- Algunas secciones de la web de Bankia, http://m.bankia.es/.
- Mi web personal http://www.carlos-garcia.es, la cual tengo que maquillarla un poco aún.
Aprendiendo lo básico de JQuery, haciendo ejemplos
JQuery Mobile, está construido sobre JQuery core, por lo que es necesario tener conocimientos del mismo para poder usarlo y por lo tanto requiere tener habilitado el JavaScript.
Como una de las finalidades de JQM es construir aplicaciones rápidas, en donde el usuario no tenga esperas molestas (debido al ancho de banda reducido),
lo que se hace es almacenar varias pantallas lógicas en un único archivo HTML e ir mostrándolas y ocultándolas.
El desarrollador no tendrá que aprender un completo nuevo lenguaje para construir sus aplicaciones con JQM, tan sólo tendrá
que usar el HTML que ya conoce y algún que otro atributo y elemento nuevo, pero nada que asuste 🙂
Con lo anterior no quiero decir que todo el web deba estar en un único archivo HTML, sino que hay que usar el sentido común e ir agrupando las páginas relacionadas
por semántica, flujo de navegación o alguna otra forma de organización.
Se asimilan mejor las ideas poniendolas en práctica… veamos unos ejemplos:
Vea primero el ejemplo en acción y luego observe el código fuente del mismo expuesto a continuación:
Emulador del navegador de smartphones, móviles y tablets para probar nuestras aplicaciones webs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>jQuery Mobile Ejemplo 1</title> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" /> <script type="text/javascript" src="http://code.jquery.com/jquery-1.5.min.js"></script> <script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script> </head> <body> <section id="page1" data-role="page"> <header data-role="header"> <h1>Página 1</h1> </header> <div data-role="content"> <p>Inicio contenido de la Página 1</p> <ul> <li><a href="#page2" title="Ir a la página 2">Página 2</a></li> <li><a href="#page3" data-transition="flip">Página 3</a></li> <li><a href="#page3" data-transition="slidedown" data-role="button" data-icon="home" data-iconpos="right">Página 3 b</a></li> <li><a href="#page3" data-rel="dialog">Página 3 (Diálogo)</a></li> </ul> <p>Fin contenido de la Página 1</p> </div> <footer data-role="footer"> Pie de la página 1 </footer> </section> <!-- Página 2 --> <section id="page2" data-role="page"> <header data-role="header"> <h1>Página 2</h1> </header> <div data-role="content"> <p>Contenido de la Página 2</p> </div> <footer data-role="footer"> Pie de la página 2 </footer> </section> <!-- Página 3 --> <section id="page3" data-role="page"> <header data-role="header"> <h1>Página 3</h1> </header> <div data-role="content"> <p>Contenido de la Página 3</p> </div> <footer data-role="footer"> Pie de la página 3 </footer> </section> </body> </html> |
En el ejemplo anterior podemos ver lo siguiente:
- El DOCTYPE indica que es una página HTML versión 5.
- En la sección <head> de la página HTML importamos las librerías de JQuery y JQuery Mobile, así como la hoja de estilos de JQuery Mobile.
- La página HTML está compuesta por tres páginas lógicas, las cuales están delimitadas por las etiquetas
<section id="paginaID" data-role="page"> y </section>
-
Cada página (section) está compuesta por los elementos:
<header data-role="header">
: Cabecera de la página.<div data-role="content">
: Cuerpo de la página.<footer data-role="footer">
: Pie de la página.
En realidad, lo que le indica a JQuery Mobile lo que es una página, una cabecera, cuerpo o pie, es el atributo data-role=»page».
- Cuándo se carga el HTML, JQuery a través de JavaScript analizará el HTML, lo modificará internamente en base a los atributos que reconoce (data-role, etc) y mostrará la primera página que se encuentre.
- Para navegar de una página a otra, simplemente debemos crear un enlace y establecer el identificador de la página destino en su atributo
href
. - Si deseamos aplicar un efecto a la navegación entre páginas debemos especificar en el enlace el atributo data-transition=»xxxx»
- Si deseamos convertir un enlace en un botón debemos especificar en el enlace el atributo
data-role="button"
- Si deseamos decorar un botón con una imagen debemos especificar en el enlace el atributo data-icon=»home»
- Si deseamos que la página a la que deseamos navegar sea mostrada como un diálogo modal, debemos especificar en el enlace el atributo
data-rel="dialog"
Continuemos aprendiendo en base a ejemplos, como antes vea primero el ejemplo en acción y luego observe el código fuente del mismo expuesto a continuación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
<!DOCTYPE html> <html> <head> <title>jQuery Mobile Application</title> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" /> <script type="text/javascript" src="http://code.jquery.com/jquery-1.5.min.js"></script> <script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script> </head> <body> <section id="page1" data-role="page"> <header data-role="header"> <h1>Página 1</h1> </header> <div data-role="content"> <div class="ui-grid-b"> <div class="ui-block-a"><a href="#listas-anidadas">Listas anidadas</a></div> <div class="ui-block-b"><a href="#barra-herramientas">Barra de herramientas</a></div> <div class="ui-block-c"><a href="#formularios">Formularios</a></div> <!-- fila 2 --> <div class="ui-block-a"><a href="#listas-anidadas">Listas anidadas</a></div> <div class="ui-block-b"><a href="#barra-herramientas">Barra de herramientas</a></div> <div class="ui-block-c"><a href="#formularios">Formularios</a></div> </div> </div> <footer data-role="footer"> <h2>Pie Página 1</h2> </footer> </section> <section id="listas-anidadas" data-role="page"> <header data-role="header"> <h1>Listas anidadas</h1> </header> <div data-role="content"> <ul data-role="listview"> <li><a href="#">Colores <span class="ui-li-count">3</span></a> <ul> <li>Rojo</li> <li>Verde</li> <li>Azul</li> </ul> </li> <li><a href="#">Ciudades <span class="ui-li-count">2</span></a> <ul> <li>Madrid</li> <li>Barcelona</li> </ul> </li> </ul> </div> </section> <section id="barra-herramientas" data-role="page"> <header data-role="header"> <h1>Barra de herramientas</h1> <nav data-role="navbar"> <ul> <li><a href="#barra-herramientas" class="ui-btn-active">Inicio</a></li> <li><a href="#">Proyectos</a></li> <li><a href="#">Acerca de</a></li> </ul> <!-- Observe que si quitamos el UL li la apariencia es diferente --> </nav> </header> <div data-role="content"> <p>Contenido de la seccion de inicio</p> </div> <footer data-role="footer" data-position="fixed"> <h2>Pie Barra de herramientas</h2> </footer> </section> <section id="formularios" data-role="page"> <header data-role="header"> <h1>Formularios</h1> </header> <div class="content" data-role="content"> <form id="form1" action="#" method="post"> <div data-role="fieldcontain"> <label for="nombre">¿Como se llama?:</label> <input type="text" id="nombre" name="nombre" placeholder="Nombre completo..."/> <label for="email">Email:</label> <input type="email" id="email" name="email"/> <label for="movil">Móvil:</label> <input type="phone" id="movil" name="movil"/> <label for="numAnios">Num de años trabajados:</label> <input type="range" id="numAnios" name="numAnios" min="0" max="50"/> <label for="viajar">Disponibilidad para viajar?</label> <select id="viajar" name="viajar" data-role="slider"> <option value="1">Sí</option> <option value="0">No</option> </select> <fieldset data-role="controlgroup"> <legend>Idiomas que es capaz de leer:</legend> <label for="ingles">Inglés</label> <input type="checkbox" id="ingles" name="ingles"> <label for="portuges">Portugués</label> <input type="checkbox" id="portuges" name="portuges"> <label for="aleman">Alemán</label> <input type="checkbox" id="aleman" name="aleman"> </fieldset> <label for="estadocivil">Estado civil:</label> <select id="estadocivil" name="estadocivil" data-native-menu="false"> <option value="0">Soltero</option> <option value="1">Casado</option> <option value="2">Viudo</option> <option value="3">Divorciado</option> </select> </div> </form> </div> <footer data-role="footer" data-position="fixed"> <h2>Pie Formularios</h2> </footer> </section> </body> </html> |
En el ejemplo anterior podemos ver lo siguiente:
- Podemos crear estructuras de tablas (celdas y columnas) usando clases css en donde ui-grid-b es una tabla de 3 columnas, ui-grid-c es una tabla de 4 columnas, etc. Ver documentación.
- Podemos simular N pantallas usando listas html (<ul>) anidadas agregándoles el atributo
data-role="listview"
. Ver documentación. - Existen una gran variedad de controles de formulario con el que podemos recoger y/o mostrar información a los usuarios de una manera elegante y usable. Ver documentación.
¿Cómo detecto si acceden a través de un móvil o de un ordenador tradicional?
Seguramente sólo desees redirigir al usuario a una Web para móvil cuando accedan a través de su móvil y continuar mostrando la web clásica cuando se accedan desde un pc de sobremesa o portátil, pues bien, puedes
programartelo tu mismo y analizar las cabeceras User-Agent, etc. o bien usar una de las soluciones ya creadas, como por ejemplo:
- En Java puedes usar Spring Mobile.
- En PHP puedes usar http://detectmobilebrowsers.mobi/.
- etc.
Referencias y enlaces interesantes:
- Web oficial: Página principal
- Web oficial: documentación
- Otros tutoriales sobre JQuery Mobile (en Inglés)
- Emulador de dispositivos (smartphones, móviles y tablets) para probar nuestras aplicaciones webs
- Una charla de Google IO: Mobile Web Development
- Improving web performance with Apache and htaccess
- Apache Caching Guide
Otros frameworks o librerías alternativos para el desarrollo web para móviles:
- http://www.primefaces.org/showcase/touch (para Java Server Faces).
- http://jqtjs.com/
- http://phonegap.com
- http://the-m-project.net
- http://joapp.com
- http://www.appcelerator.com
- http://rhomobile.com
- http://jquerymobile.com
- http://www.sencha.com/products/touch/
Conclusiones
Como se ha podido ver en este artículo, JQuery Mobile es una excelente solución para crear una web específica para móviles de manera que permita a los usuarios de las plataformas más extendidas acceder
a la información de una forma más rápida y cómoda.
Si bien le he detectado alguna anomalia, es un proyecto estable en donde se están invirtiendo considerables esfuerzos para continuar madurándolo.
Bueno, espero que os sea de utilidad, recuerda que puedes profundizar más, esto es sólo una introducción. Si os gusta este tema, podeis continuar viendo las conferencias/charlas que publiqué en este tutorial, son bastante interesantes.
Un saludo, Carlos García
Carlos, soy hugo de mendoza argentina, queria hacerte una consulta, resulta que estuve probando tu codigo fuente, y compila absolutamente todo, incluso le doy clean-build y da todo ok, aunque hice unos cambios en el package y en la url donde puso el php, aun asi todo ok, pero cuando ejecuto desde el emulador me dice que esta aplicacion no la puede ejecutar. esta aplicacion tiene una operacion ilegal. la clase no es un midlet. Carlos no entiendo donde puede estar el error, te comento ademas que el php lo coloque en mi localhost, tengo server propio y se que funciona bien y sin problema ya que compilo y ejecuto codigo java, jsp y todo ok, server propio pero ip dinamica, cual piensas puede ser el error, te envio el codigo aunque toque solo dos lineas, la del package y la del path del prueba.php. uso netbeans 7.1 y win xp3
package phone;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
/**
* Formulario principal de la aplicación
*
* @author Carlos García. Autentia Real Business Solutions.
*/
public class PhoneTestForm extends Form implements CommandListener,
java.lang.Runnable {
private PhoneTest midlet;
private Command cmdExit;
private Command cmdTest;
private Display display;
private String nl;
/** Constructor */
public PhoneTestForm(PhoneTest midlet) {
super(\\\»MENU PRINCIPAL\\\»);
this.midlet = midlet;
this.display = Display.getDisplay(midlet);
this.createUI();
}
/**
* Crea el interfaz gráfico
*/
private void createUI(){
String nl = this.getLS();
this.cmdExit = new Command(\\\»Salir\\\», Command.EXIT, 0);
this.cmdTest = new Command(\\\»Probar\\\», Command.OK, 0);
this.append(\\\»Esta aplicación realiza una prueba de conectividad para ver si su teléfono es capaz de conectarse a Internet.\\\»);
this.append(nl);
this.append(\\\»Si la prueba es satisfactoria en unos 10 segundos aparecerá la palabra OK en la pantalla de su terminal.\\\»);
this.append(nl);
this.append(\\\»Autentia Real Business Solutions.\\\»);
this.addCommand(cmdExit);
this.addCommand(cmdTest);
this.setCommandListener(this);
}
/**
* Realiza las operaciones de finalización
*/
private void cmdExit_click() throws Exception {
midlet.destroyApp(false);
}
/**
* Realiza la prueba
*/
private void cmdTestClick() {
Thread testThread = new Thread(this);
testThread.start();
}
/**
* Devuelve el separador de linea
*/
private String getLS() {
String nl = System.getProperty(\\\»line.separator\\\»);
if (\\\»\\\».equals(nl) || (nl == null)) {
nl = \\\»\\\
\\\»;
}
return nl;
}
/**
* Realiza la conexión en Background (Es requerido lo dice la
* especificación)
*/
public void run() {
HttpConnection http = null;
DataInputStream is = null;
String p1 = null;
String p2 = null;
int code;
StringBuffer buffer;
byte[] bytes;
boolean testOK = false;
Exception exception = null;
try { // Configura la solicitud
p1 = \\\»Profile/\\\» + System.getProperty(\\\»microedition.profiles\\\») + \\\» Configuration/\\\» + System.getProperty(\\\»microedition.configuration\\\»);
p2 = System.getProperty(\\\»microedition.locale\\\»);
http = (javax.microedition.io.HttpConnection) Connector.open(\\\»http://localhost:3306/BaseDatos/data/mibasedato/prueba.php\\\», Connector.READ);
//o agregar BaseDatos/data/mibasedato/
http.setRequestMethod(HttpConnection.GET);
http.setRequestProperty(\\\»User-Agent\\\», p1);
if (p2 != null) {
http.setRequestProperty(\\\»Accept-Language\\\», p2);
}
// Leemos la respuesta
code = http.getResponseCode();
if (code != HttpConnection.HTTP_OK) {
exception = new Exception(http.getResponseMessage());
} else {
is = http.openDataInputStream();
bytes = this.readFully(is);
p1 = this.getHeaderField(http, \\\»CABECERA\\\»);
try {
p2 = new String(bytes);
p2 = p2.trim();
if (\\\»OK\\\».equals(p1)
&& \\\»Autentia. Real Business Solutions\\\».equals(p2)) {
testOK = true;
}
} catch (Exception ex) {
// NADA
}
}
} catch (java.lang.Exception ex) {
exception = ex;
}
// Cerramos la connexión
try {
http.close();
} catch (Exception ex) {
}
try {
this.clear();
} catch (Exception ex) {
// NADA
}
if (testOK) {
this.append(\\\»OK\\\»);
} else if (exception != null) {
this.append(exception.toString());
} else {
if (p1 == null) {
this.append(\\\»\\\
p1 null\\\»);
} else {
this.append(p1);
}
if (nl == null) {
this.append(\\\»\\\
\\\»);
} else {
this.append(this.nl);
}
if (p2 == null) {
this.append(\\\»\\\
p2 null\\\»);
} else {
this.append(p2);
}
}
}
/**
* @return Devuelve todos los bytes disponibles y cierra el stream
*/
public byte[] readFully(DataInputStream input) throws java.io.IOException {
ByteArrayOutputStream bo = null;
int value;
try {
bo = new ByteArrayOutputStream(1024); // 1Kb
while (true) {
bo.write(input.readByte());
}
} catch (java.io.EOFException ex) {
}
byte[] bytes = bo.toByteArray();
// Cerramos los stream y liberamos recursos
bo.close();
input.close();
bo = null;
input = null;
return bytes;
}
private void clear() {
this.removeCommand(cmdTest);
for (int i = this.size() – 1; i >= 0; i–) {
this.delete(i);
}
}
/**
* Lee una cabecera Http. Algunos terminales tienen un bug en el método
* getHeader(\\\»CabeceraDeseada\\\»)
*/
private String getHeaderField(HttpConnection http, String header)
throws java.io.IOException {
int index = 0;
String name = http.getHeaderFieldKey(index);
while (name != null) {
if (this.equalsIgnoreCase(name, header)) {
return http.getHeaderField(index);
}
index++;
name = http.getHeaderFieldKey(index);
}
return null;
}
/**
* Compara dos cadenas de caracteres de modo case-insentivive
*/
private boolean equalsIgnoreCase(String str1, String str2) {
str1 = str1.toLowerCase();
str2 = str2.toLowerCase();
return str1.equals(str2);
}
/**
* Receptor de eventos
*/
public void commandAction(Command cmd, Displayable d) {
try {
if (cmd == cmdExit) {
this.cmdExit_click();
return;
}
if (cmd == cmdTest) {
this.cmdTestClick();
return;
}
} catch (java.lang.Exception ex) {
display.setCurrent(new Alert(ex.toString()), this);
}
}
}
package phone;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.Display;
/**
* Esta aplicación sirve para ver si el dispositivo es capaz o no de conectarse
* a internet y leer y escribir correctamente
*
* @author Carlos García Pérez. Autentia Real Business Solutions
*/
public class PhoneTest extends MIDlet {
private boolean started;
/** Constructor */
public PhoneTest() {
started = false;
}
/**
* Punto de inicio del MIDLET. Muestra el formulario principal.
*/
public void startApp() throws MIDletStateChangeException {
if (started) {
return;
}
this.started = true;
Display display = Display.getDisplay(this);
display.setCurrent(new PhoneTestForm(this));
}
/**
* Liberamos recursos
*/
public void destroyApp(boolean unconditional) throws MIDletStateChangeException {
this.notifyDestroyed();
}
/**
* Pause, discontinue ….
*/
public void pauseApp() {
}
}
muchas gracias