Author Archives: admin

Dojo: Un ejemplo simple de una gráfica con Dojo Charting

La implementación de Dojo charting es muy versatil. Esto , sin embargo, tiene su coste en una estructura compleja de componentes. En la siguiente imagen vemos la estructura de componentes gráficos que se usa:
Dojo Charting Diargam
Como vemos de las relaciones existentes en el diagrama ellos permiten la habilidad de dojo de pintar multiples tipos distintos de gráficos, utilizando distintos plotters en una sola area de plot. Tambien tienen diferentes implementaciones  para VML y para SVG tanto en los objetos Axis, PlotArea y Plotter.

El ejemplo

Empecemos referenciando dojo y las librerias requeridas:

<script type="text/javascript" src="dojo/dojo.js"></script>
<script type="text/javascript">
//Include the required dojo libraries/namespaces
dojo.require("dojo.collections.Store");
dojo.require("dojo.charting.Chart");
dojo.require('dojo.json');
</script>

Ahora definamos o recuperemos los datos con los que alimentaremos nuestra gráfica en una collection store. Se añaden a la gráfica como series, definidos asi:

var exampleData =
	[
	{ time: 10, count: 7382 },
	{ time: 20, count: 1852 },
	{ time: 35, count: 2397 },
	{ time: 50, count: 1442 },
	{ time: 55, count: 1854 }
	];

var store = new dojo.collections.Store();
store.setData(exampleData);

var timeSeries = new dojo.charting.Series({
	dataSource: store,
	bindings: { x: "time", y: "count" },
	label: "Example Series"
});

Ahora vayamos con la definiciones de los ejes donde podemos especificar el rango de display, el origen de datos y las etiquetas de los ticks de los ejes:

//Define the x-axis
var xAxis = new dojo.charting.Axis();

//Set the upper and lower data range values
xAxis.range = { lower: exampleData[0].time, upper: exampleData[exampleData.length-1].time };

xAxis.origin = "max";
xAxis.showTicks = true;
xAxis.label = "Example chart";

//Setup the x tick marks on the chart
xAxis.labels = [
	{ label: 'First', value: 20 },
	{ label: 'Second', value: 25 },
	{ label: 'Third', value: 35 },
	{ label: 'Fourth', value: 50 },
	{ label: 'Fifth', value: 55 }
];

//Define the y-axis
var yAxis = new dojo.charting.Axis();
yAxis.range = { lower: 0, upper: 5000 };
yAxis.showLines = true;
yAxis.showTicks = true;
yAxis.label = "Time Taken";

//Setup the y tick marks on the chart
yAxis.labels = [
	{ label: "0s", value: 0 },
	{ label: "1s", value: 1000 },
	{ label: "2s", value: 2000 },
	{ label: "3s", value: 3000 },
	{ label: "4s", value: 4000 },
	{ label: "5s", value: 5000 }
];

Entonces definimos comos los datos serán mostrados definiendo el objeto Plot y asignandole las series con un pletter para pintarlas:

var chartPlot = new dojo.charting.Plot(xAxis, yAxis);

	chartPlot.addSeries({
	data: timeSeries,
	plotter: dojo.charting.Plotters.CurvedArea
});
Esto entonces necesita ser pintado dentro de un area especifica , asi que definimos el PlotArea para añadirle el Plot:
var chartPlotArea = new dojo.charting.PlotArea();
chartPlotArea.size = { width: 380, height: 170 };
chartPlotArea.padding = { top: 20, right: 20, bottom: 30, left: 50 };

//Add the plot to the area
chartPlotArea.plots.push(chartPlot);
Finalmente creamos la chart y le añadimos la PlotArea. La chart tambien necesita un elememto contenedor al cual se asigna el chart.node.
var chart = new dojo.charting.Chart(null, "Example chart", "This is the example chart description");
//Add the plot area at an offset of 10 pixels from the top left
chart.addPlotArea({ x: 10, y: 10, plotArea: chartPlotArea });

//Setup the chart to be added to the DOM on load
dojo.addOnLoad(function()
{
	chart.node = dojo.byId("GraphContainerArea");
	chart.render();
});
And voila, you have a graph that looks like this:

Dojo Graph Example

Css display property

[fuente: http://www.librosweb.es/referencia/css/display.html]

display

Definición Establece el tipo de caja generada por un elemento
Valores

permitidos

Uno y sólo uno de los siguientes valores:

  • inline
  • block
  • list-item
  • run-in
  • inline-block
  • table
  • inline-table
  • table-row-group
  • table-header-group
  • table-footer-group
  • table-row
  • table-column-group
  • table-column
  • table-cell
  • table-caption
  • none
  • inherit
Valor inicial inline
Se aplica a Todos los elementos
Válida en all
Se hereda no
Definición en

el estándar

http://www.w3.org/TR/CSS21/visuren.html#pr

Ejemplos de uso

La propiedad display es una de las propiedades CSS más infrautilizadas. Aunque todos los diseñadores conocen esta propiedad y utilizan sus valores inline, block y none, las posibilidades de display son mucho más avanzadas.De hecho, la propiedad display es una de las más complejas de CSS 2.1, ya que establece el tipo de la caja que genera cada elemento. La propiedad display es tan compleja que casi ningún navegador es capaz de mostrar correctamente todos sus valores.

El valor más sencillo de display es none que hace que el elemento no genere ninguna caja. El resultado es que el elemento desaparece por completo de la página y no ocupa sitio, por lo que los elementos adyacentes ocupan su lugar. Si se utiliza la propiedad display: none sobre un elemento, todos sus descendientes también desaparecen por completo de la página.

Si se quiere hacer un elemento invisible, es decir, que no se vea pero que siga ocupando el mismo sitio, se debe utilizar la propiedad visibility. La propiedad display: none se utiliza habitualmente en aplicaciones web dinámicas creadas con JavaScript y que muestran/ocultan contenidos cuando el usuario realiza alguna acción como pulsar un botón o un enlace.

Los otros dos valores más utilizados son block e inline que hacen que la caja de un elemento sea de bloque o en línea respectivamente. El siguiente ejemplo muestra un párrafo y varios enlaces a los que se les ha añadido un borde para mostrar el espacio ocupado por cada caja:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Lorem ipsum Donec mollis nunc in leo Vivamus fermentum

Como el párrafo es por defecto un elemento de bloque (“block element”), ocupa todo el espacio disponible hasta el final de su línea, aunque sus contenidos no ocupen todo el sitio. Por su parte, los enlaces por defecto son elementos en línea (“inline element”), por lo que su caja sólo ocupa el espacio necesario para mostrar sus contenidos.
Si se aplica la propiedad display: inline al párrafo del ejemplo anterior, su caja se convierte en un elemento en línea y por tanto sólo ocupa el espacio necesario para mostrar sus contenidos:
[display: inline] Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum Donec mollis nunc in leo Vivamus fermentumPara visualizar más claramente el cambio en el tipo de caja, el siguiente ejemplo muestra un mismo párrafo largo con display: block y display: inline:
[display: block] Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed non sem quis tellus vulputate lobortis. Vivamus fermentum, tortor id ornare ultrices, ligula ipsum tincidunt pede, et blandit sem pede suscipit pede. Nulla cursus porta sem. Donec mollis nunc in leo.
[display: inline] Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed non sem quis tellus vulputate lobortis. Vivamus fermentum, tortor id ornare ultrices, ligula ipsum tincidunt pede, et blandit sem pede suscipit pede. Nulla cursus porta sem. Donec mollis nunc in leo.

De la misma forma, si en los enlaces del ejemplo anterior se emplea la propiedad display: block se transforman en elementos de bloque, por lo que siempre empiezan en una nueva línea y siempre ocupan todo el espacio disponible en la línea, aunque sus contenidos no ocupen todo el sitio:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
[display: block] Lorem ipsum[display: block] Donec mollis nunc in leo[display: block] Vivamus fermentum

Uno de los valores más curiosos de display es inline-block, que crea cajas que son de bloque y en línea de forma simultánea. Una caja de tipo inline-block se comporta como si fuera de bloque, pero respecto a los elementos que la rodean es una caja en línea.
El enlace del siguiente ejemplo es de tipo inline-block, lo que permite por ejemplo establecerle un tamaño mediante la propiedad width:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed non sem quis tellus vulputate lobortis. Vivamus fermentum, tortor id ornare ultrices, ligula ipsum tincidunt pede, et blandit sem pede suscipit pede. Nulla cursus porta sem. Donec mollis nunc in leo. [display: inline-block, width: 25%] Quisque semper, magna sed pharetra tincidunt, quam urna dapibus dolor, a dignissim sem neque id purus. Etiam luctus viverra nisi. Integer lobortis accumsan felis. Cras venenatis. Morbi cursus, tellus vitae iaculis pulvinar, turpis nibh posuere nisl, sed vehicula massa orci at dui. Morbi pede ipsum, porta quis, venenatis et, ullamcorper in, metus. Nulla facilisi. Quisque laoreet molestie mi. Ut mollis elit eget urna.

Si tu navegador soporta el valor inline-block, el ejemplo anterior se debe visualizar tal y como muestra la siguiente imagen:

Otro de los valores definidos por la propiedad display es list-item, que hace que cualquier elemento de cualquier tipo se muestre como si fuera un elemento de una lista (elemento <li>). El siguiente ejemplo muestra tres párrafos que utilizan la propiedad display: list-item para simular que son una lista de elementos de tipo <ul>:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Sed non sem quis tellus vulputate lobortis.
Vivamus fermentum, tortor id ornare ultrices, ligula ipsum tincidunt pede, et blandit sem pede suscipit pede.

A continuación se muestra el código HTML del ejemplo anterior:
<p style=”display: list-item; margin-left: 2em”>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</p><p style=”display: list-item; margin-left: 2em”>Sed non sem quis tellus vulputate lobortis.</p><p style=”display: list-item; margin-left: 2em”>Vivamus fermentum, tortor id ornare ultrices, ligula ipsum tincidunt pede, et blandit sem pede suscipit pede.</p>

Los elementos con la propiedad display: list-item son exactamente iguales que los elementos <li> a efectos de su visualización, por lo que se pueden utilizar las propiedades de listas como list-style-type, list-style-image, list-style-position y list-style.
Uno de los valores más curiosos de la propiedad display es run-in, que genera una caja de bloque o una caja en línea dependiendo del contexto, es decir, dependiendo de sus elementos adyacentes. El comportamiento de las cajas de tipo run-in se rige por las siguientes reglas:
Si la caja run-in contiene una caja de bloque, la caja run-in se convierte en una caja de bloque.Si después de la caja run-in se encuentra una caja de bloque (que no esté posicionada de forma absoluta y tampoco esté posicionada de forma flotante), la caja run-in se convierte en una caja en línea en el interior de la caja de bloque.En cualquier otro caso, la caja run-in se convierte en una caja de bloque.El siguiente ejemplo muestra una misma caja de tipo run-in que se visualiza de forma muy diferente en función del tipo de caja que existe a continuación:
[display: run-in] Lorem ipsum[display: block] dolor sit amet, consectetuer adipiscing elit.
[display: run-in] Lorem ipsum
[display: inline] dolor sit amet, consectetuer adipiscing elit.El código HTML y CSS del ejemplo anterior se muestra a continuación:
<p style=”display: run-in; border: 2px dashed #C00;”><strong>[display: run-in]</strong> Lorem ipsum</p><p style=”display: block; border: 2px solid #000;”><strong>[display: block]</strong> dolor sit amet, consectetuer adipiscing elit.</p> <p style=”display: run-in; border: 2px dashed #C00;”><strong>[display: run-in]</strong> Lorem ipsum</p><p style=”display: inline; border: 2px solid #000;”><strong>[display: inline]</strong> dolor sit amet, consectetuer adipiscing elit.</p>

En la actualidad sólo la última versión del navegador Opera es capaz de mostrar correctamente el ejemplo anterior, tal y como muestra la siguiente imagen:

El estándar CSS 2.1 incluye un ejemplo del posible uso del valor run-in. En este ejemplo, un título de sección <h3> crea una caja run-in, de forma que cuando va seguido de un párrafo, el titular se mete dentro del párrafo:
<h3 style=”display: run-in”>Lorem ipsum dolor sit amet</h3><p>Sed non sem quis tellus vulputate lobortis. Vivamus fermentum, tortor id ornare ultrices, ligula ipsum tincidunt pede, et blandit sem pede suscipit pede. Nulla cursus porta sem. Donec mollis nunc in leo. Integer lobortis accumsan felis.</p>

El resto de valores de la propiedad display están relacionados con las tablas y hacen que un elemento se muestre como si fuera una parte de una tabla: fila, columna, celda o grupos de filas/columnas. Los valores definidos por la propiedad display son inline-table, table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, table-caption.
Aunque los valores relacionados con las tablas son los más avanzados, también son los que peor soportan los navegadores. A continuación se muestra un ejemplo con tres párrafos de texto que establecen la propiedad display: table-cell:
[display: table-cell] Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas non tortor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed fermentum lorem a velit. [display: table-cell] In molestie suscipit libero. Cras sem. Nunc non tellus et urna mattis tempor. Nulla nec tellus a quam hendrerit venenatis. Suspendisse pellentesque odio et est. Morbi sed nisl sed dui consequat sodales. [display: table-cell] Morbi sed nisl sed dui consequat sodales. Vivamus ornare felis nec est. Phasellus massa justo, ornare sed, malesuada a, dignissim a, nibh. Vestibulum vitae nunc at lectus euismod feugiat. Nullam eleifend. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In ut ipsum.

La propiedad display: table-cell hace que cualquier elemento se muestre como si fuera una celda de una tabla. Como en el ejemplo anterior los tres elementos <p> utilizan la propiedad display: table-cell, el resultado es visualmente idéntico a utilizar una tabla y tres elementos <td>.
Si utilizas un navegador con soporte completo de CSS 2.1, el ejemplo anterior se visualiza tal y como muestra la siguiente imagen:

Como los valores relacionados con las tablas hacen que cualquier elemento que no sea una tabla se muestre y comporte como si lo fuera, se pueden utilizar para crear los layouts de las páginas. Hace años, la estructura de las páginas se definía mediante tablas, filas y columnas. Esta solución tiene innumerables desventajas y por eso todos los diseñadores web profesionales crean la estructura de sus páginas mediante CSS y elementos <div>.No obstante, las tablas tienen algunas ventajas en su comportamiento respecto a los elementos <div> posicionados de forma absoluta o flotante. La principal ventaja es que todas las celdas de una fila siempre tienen la misma altura, por lo que si se utilizan tablas no se sufre el problema de las columnas de página con diferente altura.Además, la estructura creada con una tabla nunca se rompe, ya que las celdas de datos nunca se visualizan una debajo de otra cuando la ventana del navegador se hace muy pequeña. Sin embargo, cuando se define la estructura mediante elementos <div> posicionados es posible que la página se rompa y alguna columna se muestre debajo de otros contenidos.
Utilizando la propiedad display de forma avanzada es posible crear una estructura de página que sea semánticamente correcta, esté diseñada exclusivamente con CSS y que se comporte exactamente igual que como lo hacen las tablas.
El siguiente código HTML corresponde a la estructura de una página con tres columnas:
…<div id=”contenedor”>  <div id=”contenidos”>    <div id=”secundario”>      Curabitur rutrum…    </div>    <div id=”principal”>      Lorem ipsum dolor sit amet…     </div>    <div id=”lateral”>      Nam erat massa…    </div>  </div></div>…Utilizando las siguientes reglas CSS y la propiedad display es posible hacer que los elementos <div> anteriores se comporten como si fueran elementos <tr> y <td>:
#contenedor {  display: table;  border-spacing: 5px;}#contenidos {  display: table-row;}#principal, #secundario, #lateral {  display: table-cell;}#principal {  width: 60%;}#secundario, #lateral {  width: 20%;}El elemento #contenedor se visualiza como una tabla porque se le aplica la propiedad display: table. De esta forma, se pueden aplicar al elemento #contenedor propiedades exclusivas de las tablas como border-spacing. El elemento #contenidos se visualiza como si fuese una fila de tabla (etiqueta <tr>). En su interior se encuentran las tres columnas de la página que se visualizan como si fueran tres elementos <td> gracias a la propiedad display: table-cell.
A continuación se muestra el resultado obtenido al aplicar estas reglas CSS al código HTML anterior:
Curabitur rutrum eros a risus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In molestie suscipit libero. Cras sem. Nunc non tellus et urna mattis tempor. Nulla nec tellus a quam hendrerit venenatis. Suspendisse pellentesque odio et est. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas non tortor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed fermentum lorem a velit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin sodales, enim in volutpat vehicula, leo turpis vehicula magna, ut rutrum arcu lorem ac pede. Curabitur rutrum eros a risus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In molestie suscipit libero. Cras sem. Nunc non tellus et urna mattis tempor. Nulla nec tellus a quam hendrerit venenatis. Suspendisse pellentesque odio et est. Morbi sed nisl sed dui consequat sodales. Donec porta porta ligula. Nam erat massa, blandit in, dapibus sit amet, vestibulum et, augue. Suspendisse ac risus. Duis semper fringilla sem. Praesent augue arcu, scelerisque nec, ornare malesuada, posuere a, neque. Nullam nulla nisi, ultrices quis, adipiscing non, varius ut, dui. Nulla viverra pellentesque sem. Nam erat massa, blandit in, dapibus sit amet, vestibulum et, augue. Suspendisse ac risus.La estructura de la página del ejemplo anterior está diseñada exclusivamente con CSS pero se comporta como si fuera una tabla. Todas las columnas de la página tienen la misma altura sin necesidad de recurrir a ningún truco y la página nunca se rompe por muy pequeña que se haga la ventana del navegador.
Si visualizas esta página con un navegador que soporte correctamente la propiedad display de CSS 2.1, el ejemplo anterior se ve tal y como muestra la siguiente imagen:

Por último, aunque el estándar CSS 2.1 establece que el valor por defecto de la propiedad display es inline, todos los navegadores obvian esta recomendación y asignan por defecto el valor block a los elementos de bloque y el valor inline a los elementos en línea.

Simplifica XML a objeto Java utilizando Jakarta Digester

En muchas ocasiones un programador necesita rellenar objetos Java desde datos en XML. En este tuto vamos a explicar como se puede hacer esto con Jakarta Digester , que originalmente fue configurado para procesar el archivo de configuración de Struts “struts-config.xml”. Pronto fue movido a un proyecto de Jakarta-commons.

Cuando una aplicación software trata con datos formateados en XML, es útil ser capaz de procesar un documento XML a la manera de “conducida por eventos”, donde objetos java en particular son creados cuando son reconocidos patrones de elementos XML anidados especificos.

When software application deals with the XML-formatted data, it is useful to be able to process an XML document in “event driven” manner, where particular java objects are created when particular patters of nested XML elements have been recognized. Digester provides a high level and more developer-friendly interface to SAX events. Since digester uses SAX APIs for parsing it becomes choice for the developer in opposed to Document Object Model (DOM) parser. Also because most of the details of shoving the XML elements are hidden, digester allows the developer to focus on the processing to be performed. Digester package allows you to configure an XML to Java object mapping, which sets off defined actions called rules whenever a particular pattern of XML elements is recognized. Full sets of predefined rules are available from Jakarta digester package, or you can create your own rules. Among the other XML parsing options available, the Digester package offers greater simplicity with very simple classes and a collection of “predefined rules”.

Understanding Jakarta Digester

Digester depends on following Jakarta common components in order to compile and run.

  • commons-digester.jar
  • commons-collections.jar
  • commons-logging.jar
  • commons-beanutils.jar

Main feature of the org.apache.commons.digester.Digester parser is that the Digester automatically walks through the element hierarchy of the XML document you are parsing, without entailing any attention to this process. This process is called as element matching patterns. Assume that you have registered processing rules that match patterns “x” “x/y” and “x/y/z”. For specified XML document with the below contents, the designated patters will be matched when the corresponding element is parsed. You can also use “*” wildcard character in your matching pattern rule.

<x>         Matches pattern “x”
<y>       Matches pattern “x/y”
<z/>    Matches pattern “x/y/z”

 

Using Jakarta Digester
In order to use a Digester, the following essential steps are necessary:

  • Create a new instance of the org.apache.commons.digester.Digester class.
  • Register all the element matching patterns for which you wish to have processing rules fired when this pattern is recognized in an inputted XML document. If there is more than one rule for a given pattern, then the rules will be executed in the order that they were listed.
  • Call the digester.parse() method, passing the reference to the XML document to be parsed.

Developing sample application

Following code example will make you understand Digester unambiguously.

address.xml

<addresses>
<address>
<addressLine1>101 blvd</addressLine1>
<addressLine2>Little park</addressLine2>
</address>
<address>
<addressLine1>Author 2</addressLine1>
<addressLine2>His One Book</addressLine2>
</address>

<person>
<name>Megha</name>
<detail age=”22″>
<education>PT</education>
</detail>
</person>
<person>
<name>person 2</name>
<detail age=”32″>
<education>engineer</education>
</detail>
</person>
</addresses>

Following are the Java beans you need to populate.

Addresses.java
package address;

import java.util.Vector;

public class Addresses {
private Vector addresses;
private Vector persons;

public Addresses() {
addresses = new Vector();
persons = new Vector();
}

public void addAddress(Address address ) {
addresses.addElement(address );
}
public void addPerson(Person person ) {
persons.addElement( person );
}

}

Address.java
package address;

public class Address {
private String addressLine1;
private String addressLine2;

public Address() {}

public void setAddressLine1( String addressLine1 ) {
this.addressLine1 = addressLine1;
System.out.println(“addressLine 1 is “+this.addressLine1);
}
public void setAddressLine2( String addressLine2 ) {
this.addressLine2 = addressLine2;
}

}

Person.java
package address;

import java.util.Vector;

public class Person {
private String name;
private Vector details;

public Person() {
details = new Vector();
}

public void setName( String name ) {
this.name = name;
System.out.println(“name ” +this.name);
}

public void addDetail(Detail detail ) {
details.addElement(detail);
}

}

Detail.java
package address;

public class Detail {
private String age;
private String education;

public Detail() {

}

public void setAge(String age){
this.age = age;
System.out.println(“Age is “+this.age);
}
public void setEducation(String education) {
this.education = education;
System.out.println(“education is “+this.education);
}

}

Following is the AddressDigester class in which you specify the rules in digester with this class.

AddressDigester.java
package address;

import org.apache.commons.digester.Digester;

import java.io.File;

public class AddressDigester {

public static void main( String[] args ) {

try {
Digester digester = new Digester();
digester.setValidating( false );

digester.addObjectCreate( “addresses”, Addresses.class );

digester.addObjectCreate( “addresses/address”, Address.class );
digester.addBeanPropertySetter( “addresses/address/addressLine1”, “addressLine1” );
digester.addBeanPropertySetter( “addresses/address/addressLine2”, “addressLine2” );
digester.addSetNext( “addresses/address”, “addAddress” );

digester.addObjectCreate( “addresses/person”, Person.class );
digester.addBeanPropertySetter( “addresses/person/name”, “name” );

digester.addObjectCreate( “addresses/person/detail”, Detail.class );
digester.addSetProperties( “addresses/person/detail”, “age”, “age” );
digester.addBeanPropertySetter( “addresses/person/detail/education” );
digester.addSetNext( “addresses/person/Detail”, “addDetail” );

digester.addSetNext( “addresses/person”, “addPerson” );

File inputFile = new File( args[0] );
Addresses address = (Addresses)digester.parse( inputFile );

} catch( Exception exc ) {
exc.printStackTrace();
}

}

As I discussed in the “Using Jakarta Digester” section, The Digester class processes the input XML document based on patterns and rules. The patterns must match XML elements, based on their name and location in the document tree. The pattern address matches the top level <address> element and likewise for the other elements.

  • addObjectCreate adds an “object create” rule for the specified parameters.
  • addBeanPropertySetter adds a bean property setter rule for the specified parameters.
  • addsetNext adds a set next rule for the specified parameters.

Although Digester is a simple solution, it’s not always ideal in the real programming world. When you have to parse XML whose elements keep on changing based on some input. In this situation, you need to write custom Digester Rules.

Now you know the simplicity and creativity of Jakarta Digester for XML Java parsing.

 

Dojo: hacer un custom theme

[Fuente: http://www.sitepen.com/blog/2010/07/26/dojo-chart-theming/h

Dojo Chart Themes

 

Setting Up Your Namespace

All of Dojo’s charting themes live with in the dojox.charting.themes namespace. As is the best practice with custom class creation with Dojo, we’ll create our own namespace for our custom chart themes. Let’s give our custom theme the davidwalsh.charting.themes namespace. davidwalshserves as my namespace for all custom Dojo classes and I’ve chosen to mimic the path dojox uses to themes.

/* declaring charts within my namespace */
dojo.provide("davidwalsh.charting.themes.SitePen");
dojo.provide("davidwalsh.charting.themes.SitePenFTW");

Quick Peek at a Simple Theme

The levels of complexity within Dojo themes can vary greatly depending on your desire to use simply define colors or implement advanced features like gradations, markers, scrolling, panning, and chart events. Let’s start by looking at a basic theme called Dollar:

dojo.provide("dojox.charting.themes.Dollar");
dojo.require("dojox.charting.Theme");
(function(){
	dojox.charting.themes.Dollar = new dojox.charting.Theme({
		colors: [
			"#A4CE67",
			"#739363",
			"#6B824A",
			"#343434",
			"#636563"
		]
	});
})();

Dollar illustrates how simple creating a theme can be; provide a list of colors and you’ve created a custom theme!

Basic Custom Theme: SitePen

Now that we’ve seen how simple it is to make a basic theme, we can easily create a custom SitePen theme base on the SitePen logo colors:

dojo.provide("davidwalsh.charting.themes.SitePen");
dojo.require("dojox.charting.Theme");
(function(){
	davidwalsh.charting.themes.SitePen = new dojox.charting.Theme({
		colors: [
			"#f2f2f2",
			"#bed3d9",
			"#7fc25d",
			"#60b32b",
			"#277085",
			"#333"
		]
	});
})();

Your colors array can have any number of colors. Colors are repeated in sequence within the chart, if necessary. Check out a few different charts using the new SitePen theme:

Basic Custom Chart Theme

Advanced Chart Theming

With the basic SitePen chart theme, we simply defined a series of colors we’d like used in the chart. With advanced chart theming, you can customize everything from default plotareas, axis, series, and marker colors, fonts, and strokes. The amount of control you can have over your charts by creating your own theme is truly incredible. Using Tom Trenka’s “Tom” theme as a template, let’s further customize and enhance the SitePen theme.

/* requires and provides */
dojo.provide("dojox.charting.themes.SitePenFTW");
dojo.require("dojox.gfx.gradutils");
dojo.require("dojox.charting.Theme");

/* define the theme */
(function() {

/* create shortcut references to dojox classes */
var dc = dojox.charting, themes = dc.themes, Theme = dc.Theme, g = Theme.generateGradient,

	/* fill settings for gradation */
	defaultFill = {type: "linear", space: "shape", x1: 0, y1: 0, x2: 0, y2: 100};

/* create theme */
davidwalsh.charting.themes.SitePenFTW = new dc.Theme({

	/* customize the chart wrapper */
	chart: {
		fill: "#333",
		stroke: { color: "#333" },
		pageStyle: {
			backgroundColor: "#000",
			color: "#fff"
		}
	},

	/* plotarea definition */
	plotarea: { fill: "#000" },

	/* axis definition */
	axis:{
		stroke:	{ // the axis itself
			color: "#fff",
			width: 1
		},
		tick: {	// used as a foundation for all ticks
			color: "#fff",
			position: "center",
			font: "normal normal normal 7pt Helvetica, Arial, sans-serif",	// labels on axis
			fontColor: "#fff" // color of labels
		}
	},

	/* series definition */
	series: {
		stroke: { width: 2.5, color: "#fff" },
		outline: null,
		font: "normal normal normal 8pt Helvetica, Arial, sans-serif",
		fontColor: "#fff"
	},

	/* marker definition */
	marker: {
		stroke: { width: 1.25, color: "#fff" },
		outline: { width: 1.25, color: "#fff" },
		font: "normal normal normal 8pt Helvetica, Arial, sans-serif",
		fontColor: "#fff"
	},

	/* series theme with gradations! */
	//light => dark
	//from above: 	g = dojox.charting.Theme.generateGradient
	//defaultFill object holds all of our gradation settings
	seriesThemes: [
		{ fill: g(defaultFill, "#fff", "#f2f2f2") },
		{ fill: g(defaultFill, "#d5ecf3", "#bed3d9") },
		{ fill: g(defaultFill, "#9ff275", "#7fc25d") },
		{ fill: g(defaultFill, "#81ee3b", "#60b32b") },
		{ fill: g(defaultFill, "#4dcff4", "#277085") },
		{ fill: g(defaultFill, "#666", "#333") }
	],	

	/* marker theme */
	markerThemes: [
		{fill: "#bf9e0a", stroke: {color: "#ecc20c"}},
		{fill: "#73b086", stroke: {color: "#95e5af"}},
		{fill: "#216071", stroke: {color: "#277084"}},
		{fill: "#c7212d", stroke: {color: "#ed2835"}},
		{fill: "#87ab41", stroke: {color: "#b6e557"}}
	]
});
})();

You wont need to define every property created above; if a given property isn’t defined, a default value will be used. On the flip side, you may also override each and any of these settings when creating chart instances. This custom theme acts as a middle-ground between chart defaults and chart instance settings.

Take a look at some of our enhanced theme examples:

Advanced Custom Chart Theme

Using Gradients

The advanced SitePen theme we created above made use of gradients thanks to thedojox.gfx.gradutils class and dojox.charting.Theme.generateGradient method introduced in Dojo 1.5. Gradation instances can be passed to any property that desires a color (usually the fillproperty.) The signature of the new generateGradient method is:

generateGradient: function(
	fillPattern,
	colorFrom,
	colorTo
}

The fill pattern holds the gradient settings:

{
	type: "linear",		//or "radial"
	space: "shape",
	x1: 0, 			//gradation direction
	y1: 0, 			//gradation direction
	x2: 0, 			//gradation direction
	y2: 100			//gradation direction
}

Feel free to experiment with the gradation settings to find just the right gradient for you.

New to Dojo 1.5: Gradients, Themes, and Extended Support!

Dojo 1.5 introduces great new features to the charting library, including:

 

Great Charting Resources

 

Create Your Theme!

While Dojo’s charting library comes with a variety of stylish themes, don’t feel as though you need to choose an existing theme; take a few minutes to create an eye-catching custom theme to match your branding!

Bookmark and Share

 

Android Aplicación completa IV: Realizando la request asíncrona desde el main activity

Porqué una llamada asincrona

En esta parte integraremos juntos los servicios del  HTTP retriever con el XML parser para realizar una API search request desde la main activity de nuestra aplicación. La request será ejecutada asíncronamente en un hilo en background para evitar bloquear el main UI thread.En desarrollo de aplicaciones para móviles, un aspecto muy importante del comportamiento de la aplicación es una ejecución suave. La respuesta de la aplicación a la entrada del usuario debe ser rápida y la experiencia de usuario debe ser suave y fácil. La responsividad es muy significante específicamente en la plataforma Android y por eso Google ha publicado algunas guias de diseño para ello. Esto es la razón por la que la búsqueda de operaciones la iremos haciendo en background ejecutándose en threads distintos al del main UI thread.

Independientemente del hecho que la velocidad de la conexión a internet en móviles ha mejorado mucho en los últimos tiempos, permanece el hecho que descargar datos desde internet es aún una operación costosa en tiempo. Asi, no queremos pausar el main thread mientras el HTTP client espera para que los datos terminen de ser descargados. También observa que pausar el main thread UI por más de 5 segundos causará que salga un diálogo  “Application Not Responding” (ANR)  y al usuario se le da la oportunidad de matar la aplicación. Por tanto no queremos esto.

Para este propósito, vamos a tirar del API de Android y vamos a utilizar una clase built-in llamada AsyncTask. Esta clase nos permite usar fácilmente el UI thread. Según la documentación oficial: “AsyncTask habilita un uso apropiado y fácil del UI thread. Esta clase te permite realizar operaciones en background y publicar resultados en el UI thread sin tener que manipular  thread y/o handlers”.

Servicio GenericSeeker

Antes de empezar el código asíncrono, primero introducimos algunas clases de servicio que serán responsables para realizar las peticiones HTTP, parsear las respuestas XML, crear los objetos de modelo correspondientes y retornar estos al Activity que los invoca. Estas clases extenderán la clase base abstracta GenericSeeker que tiene el siguiente código:

package com.javacodegeeks.android.apps.moviesearchapp.services;
import java.net.URLEncoder;
import java.util.ArrayList;
public abstract class GenericSeeker<E> {

	protected static final String BASE_URL = "http://api.themoviedb.org/2.1/";
	protected static final String LANGUAGE_PATH = "en/";
	protected static final String XML_FORMAT = "xml/";
	protected static final String API_KEY = "<YOUR_API_KEY_HERE>";
	protected static final String SLASH = "/";
	protected HttpRetriever httpRetriever = new HttpRetriever();
	protected XmlParser xmlParser = new XmlParser();
	public abstract ArrayList<E> find(String query);
	public abstract ArrayList<E> find(String query, int maxResults);
	public abstract String retrieveSearchMethodPath();

	protected String constructSearchUrl(String query) {
		StringBuffer sb = new StringBuffer();
		sb.append(BASE_URL);
		sb.append(retrieveSearchMethodPath());
		sb.append(LANGUAGE_PATH);
		sb.append(XML_FORMAT);
		sb.append(API_KEY);
		sb.append(SLASH);
		sb.append(URLEncoder.encode(query));
		return sb.toString();
	}

	public ArrayList<E> retrieveFirstResults(ArrayList<E> list, int maxResults) {
		ArrayList<E> newList = new ArrayList<E>();
		int count = Math.min(list.size(), maxResults);
		for (int i=0; i<count; i++) {
		newList.add(list.get(i));
		}
		return newList;
	}
}

La clase GenericSeeker denota que es hábil para encontrar resultados de una clase en particular y las clases que la extiendan tendrán que proporcionar implementaciones concretas para las clases apropiadas.Utilizaremos los objetos HttpRetriever y XmlParser creados en la parte 3 de esta serie. Recordemos que el API TMDb usa URLs similares tanto para la búsqueda de movies como para la de personas:

  • Movie.search (http://api.themoviedb.org/2.1/methods/Movie.search) Para la búsqueda por una película
  • Person.search (http://api.themoviedb.org/2.1/methods/Person.search) Para la búsqueda por un actor, actriz o miembro de producción

Asi , estamos utilizando una URL base común y las extending clases tiene que proporcionar un path adicional implementando el método “retrieveSearchMethodPath”. Hay dos métodos más que deben ser implementados, find(String) y find(String,int), ambos retornan un ArrayList de objetos con las clase apropiada. La segunda puede ser utilizada para estrechar el número total de resultados. Esto puede ser útil porque el API típicamente retorna resultados que no son muy relevantes para la búsqueda que se ha hechoy y pueden ser descartados para tener un mejor rendimiento. Finalmente, no olvidemos reemplazar el valor de la variable API_KEY con un valid key desde el TMDb site.

Servicios MovieSeeker y PersonSeeker

Lo siguiente es presentar el código para las dos clases hijas: MovieSeekerPersonSeeker. Las clases son muy similares, así que mostraremos sólo una de ellas por razones de brevedad:

package com.javacodegeeks.android.apps.moviesearchapp.services;

import java.util.ArrayList;
import android.util.Log;
import com.javacodegeeks.android.apps.moviesearchapp.model.Movie;

public class MovieSeeker extends GenericSeeker<Movie> {
	private static final String MOVIE_SEARCH_PATH = "Movie.search/";
	public ArrayList<Movie> find(String query) {
		ArrayList<Movie> moviesList = retrieveMoviesList(query);
		return moviesList;
	}

	public ArrayList<Movie> find(String query, int maxResults) {
		ArrayList<Movie> moviesList = retrieveMoviesList(query);
		return retrieveFirstResults(moviesList, maxResults);
	}

	private ArrayList<Movie> retrieveMoviesList(String query) {
		String url = constructSearchUrl(query);
		String response = httpRetriever.retrieve(url);
		Log.d(getClass().getSimpleName(), response);
		return xmlParser.parseMoviesResponse(response);
	}

	@Override
	public String retrieveSearchMethodPath() {
		return MOVIE_SEARCH_PATH;
	}
}

El método private “retrieveMoviesList” es el importante de la clase. Primero construye la URL para hacer la llamada al TMDb API y entonces ejecuta la petición HTTP utilizando una instancia de la case HttpRetriever. Si la request tiene éxito, la respuesta XML es pasada al servicio XMLParser que es el responsable de mapear la respuesta a un objeto de modelo Movie. Los métodos find(String) y find(String, int) son de hecho envolturas para este método privado

Utilizar los servicios desde el main Activity

Ahora estamos preparados para usar los servicios de búsqueda desde nuestra main Activity. Primero creamos una instancia de esos servicios como sigue:

...
private GenericSeeker<Movie> movieSeeker = new MovieSeeker();
private  GenericSeeker<Person> personSeeker = new PersonSeeker();
...

Como mencionamos al comienzo de este artículo, la invocación de los métodos find lo haremos en un thread aparte del UI thread. Es ahora momento de crear nuestra implementación de AsyncTask, que para la búsqueda de pelis es lo siguiente:

...
private class PerformMovieSearchTask extends AsyncTask<String, Void, List<Movie>> {
	@Override
	protected List<Movie> doInBackground(String... params) {
		String query = params[0];
		return movieSeeker.find(query);
	}

	@Override
	protected void onPostExecute(final List<Movie> result) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (progressDialog!=null) {
					progressDialog.dismiss();
					progressDialog = null;
					}
					if (result!=null) {
						for (Movie movie : result) {
						longToast(movie.name + " - " + movie.rating);
					}
				}
			}
		});
	}
}

...

Primero declaramos que nuestra implementación extiende de la clase Asynctask. Con este encabezado estamos diciendo que el tipo de los parámetros enviados a la tarea en ejecución son del tipo String, que no se mostrarán unidades de progreso durante lo que tarde la ejecución en background (lo denotamos con Void) y que los resultados de este ejecución en segundo plano es del tipo List que contiene objetos de tipo Movie.

En el método doInbackground, realizamos la recuperación de datos por HTTP y entonces en el método onPostExecute  presentamos los resultados en la forma de notificaciones Toast. Notese que hay que crear otra clase tarea llamada “PerformPersonSearchTask” para realizar el otro tipo de búsqueda.

Notese que un widget ProgressDialog es usado para permitir al usuario conocer ir sabiendo que la recuperación de datos está en curso y que debe ser paciente. EL progress dialog está declarado cancelable en su método de Factory asi que el usuario puede cancelar la tarea de la request. El código es el siguiente:

private void performSearch(String query) {
	progressDialog = ProgressDialog.show(MovieSearchAppActivity.this, "Please wait...", "Retrieving data...", true, true);
	if (moviesSearchRadioButton.isChecked()) {
		PerformMovieSearchTask task = new PerformMovieSearchTask();
		task.execute(query);
		progressDialog.setOnCancelListener(new CancelTaskOnCancelListener(task));
	}  else if (peopleSearchRadioButton.isChecked()) {
		PerformPersonSearchTask task = new PerformPersonSearchTask();
		task.execute(query);
		progressDialog.setOnCancelListener(new CancelTaskOnCancelListener(task));
	}
}

...

También proporcionamos una implementación del OnCancelListener del progress dialog con el único propósito de cancelar la tarea relevante. El código es el siguiente:

...
private class CancelTaskOnCancelListener implements OnCancelListener {
	private AsyncTask<?, ?, ?> task;

	public CancelTaskOnCancelListener(AsyncTask<?, ?, ?> task) {
		this.task = task;
	}

	@Override
	public void onCancel(DialogInterface dialog) {
		if (task!=null) {
			task.cancel(true);
		}
	}
}

...

Veamos los resultados de nuestro código. Ejecutemos la aplicación , lancemos una búsqueda. El dialogo de progreso debe aparecer notificando de que se está haciendo una operación de request como en la siguiente imagen:

El usuario puede cancelar en cualquier momento la tarea dándole al botón “Back”. Si la operación se hace bien, entonces se invoca al método callback y se van mostrando un toast para cada uno de los resultados retornados, algo como esto:

Android Aplicación Completa III: Parseando la respuesta del XML

En esta parte vamos a parsear la respuesta XML utilizando las capacidades built-in del XML para parseo de XML.

1.- Analizamos las respuestas XML

El TMDb API soporta tanto XML como formatos JSON para las respuestas HTTP. Vamos a utilizar XML para nuestra aplicación. Veamos como algunas respuestas de ejemplo para queries de búsqueda como estas:

Movies search for “Transformers” and (year) “2007” à archivo “Transformes+2007.xml

Person search for “Brad Pitt” à archivo Bradd+Pitt.xml

Lass respuestas son documentos XML típicos que pueden ser parseados utilizando los procedimientos estándar SAX o DOM. La especificación SAX define una aproximación basada en eventos donde los parsers implementados escanean a través de los datos XML y utilizan manejadores call-back cuando quiera que ciertas partes del documento han sido encontradas. Por otro lado , la especificación DOM define una aproximación basada en árbol para navegar por el documento XML.

En general, el uso de SAX es más trabajoso de implementar porque hay que desarrollar todos los método callback que manejan los eventos, mientras la aproximación DOM requiere de más memoria. Por esta razón, vamos a elegir SAX para la implementación de nuestros parsers XML, ya que nuestra aplicación funcionará en un entorno hardware con restricciones como es un dispositivo móvil.

2.- Creación de clases de modelo

Antes de proceder con el parseo de XML, vamos a crear algunas clases modelo con las cuales mapearemos los elementos XML a clases Java. Simplemente mirando las respuestas XML, se pueden derivar las siguientes clases modelo:

package com.javacodegeeks.android.apps.moviesearchapp.model;
import java.util.ArrayList;
public class Person {
	public String score;
	public String popularity;
	public String name;
	public String id;
	public String biography;
	public String url;
	public String version;
	public String lastModifiedAt;
	public ArrayList<Image> imagesList;
}

package com.javacodegeeks.android.apps.moviesearchapp.model;
import java.util.ArrayList;
public class Movie {
	public String score;
	public String popularity;
	public boolean translated;
	public boolean adult;
	public String language;
	public String originalName;
	public String name;
	public String type;
	public String id;
	public String imdbId;
	public String url;
	public String votes;
	public String rating;
	public String certification;
	public String overview;
	public String released;
	public String version;
	public String lastModifiedAt;
	public ArrayList<Image> imagesList;

	public String retrieveThumbnail() {
		if (imagesList!=null && !imagesList.isEmpty()) {
			for (Image movieImage : imagesList) {
				if (movieImage.size.equalsIgnoreCase(Image.SIZE_THUMB) &&	movieImage.type.equalsIgnoreCase(Image.TYPE_POSTER)) {
					return movieImage.url;
				}
			}
		}
		return null;
	}
}

package com.javacodegeeks.android.apps.moviesearchapp.model;

public class Image {
	public static final String SIZE_ORIGINAL = "original";
	public static final String SIZE_MID = "mid";
	public static final String SIZE_COVER = "cover";
	public static final String SIZE_THUMB = "thumb";
	public static final String TYPE_PROFILE = "profile";
	public static final String TYPE_POSTER = "poster";
	public String type;
	public String url;
	public String size;
	public int width;
	public int height;
}

No hay nada raro en estas clases. En la clase Movie proporcionamos el método retrieveThumbnail el cual hace loop por las Images disponibles y retorna una de tamaño “thumb” y tipo “poster”.

3.- Haciendo el parser

Procederemos con la creación de una clase llamada XmlParser la cual utiliza SAX para parsear las respuestas XML. La clase utiliza dos handlers de código nuestro (PersonalHandler y MovieHandler) para realizar el parseo. El código para la clase es el siguiente:

package com.javacodegeeks.android.apps.moviesearchapp.services;
import java.io.StringReader;
import java.util.ArrayList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import com.javacodegeeks.android.apps.moviesearchapp.handlers.MovieHandler;
import com.javacodegeeks.android.apps.moviesearchapp.handlers.PersonHandler;
import com.javacodegeeks.android.apps.moviesearchapp.model.Movie;
import com.javacodegeeks.android.apps.moviesearchapp.model.Person;

public class XmlParser {
	private XMLReader initializeReader() throws ParserConfigurationException, SAXException {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		// create a parser
		SAXParser parser = factory.newSAXParser();
		// create the reader (scanner)
		XMLReader xmlreader = parser.getXMLReader();
		return xmlreader;
	}

	public ArrayList<Person> parsePeopleResponse(String xml) {
		try {
			XMLReader xmlreader = initializeReader();
			PersonHandler personHandler = new PersonHandler();
			// assign our handler
			xmlreader.setContentHandler(personHandler);
			// perform the synchronous parse
			xmlreader.parse(new InputSource(new StringReader(xml)));
			return personHandler.retrievePersonList();
		}
		catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public ArrayList<Movie> parseMoviesResponse(String xml) {
		try {
			XMLReader xmlreader = initializeReader();
			MovieHandler movieHandler = new MovieHandler();
			// assign our handler
			xmlreader.setContentHandler(movieHandler);
			// perform the synchronous parse
			xmlreader.parse(new InputSource(new StringReader(xml)));
			return movieHandler.retrieveMoviesList();
		}
		catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}

En cada método , tenemos que recuperar una referencia de la clase factory parser SAX utilizando el método estático de la SAXParserFactory. Ese método retorna la implementación apropiada para Android. Entonces, el objeto SAXParser se crea utilizando el método new SAXParser, que crea una nueva instancia de una SAXParser utilizando los parámetros de Factory configurados actualmente.

La clase SAXParser define el API que envuelve una implementación de la clase XMLReader. XMLReader es un interfaz para leer un documento XML utilizando callbacks. Los callbacks están definidos generalmente via clases que extienden la clase DefaultHandler, que es la clase por defecto base para los event handlers de SAX2. Creamos dos handlers, uno para parseo de las respuestas de búsqueda de personas (PersonHandler) y uno para parsear las respuestas de búsqueda de películas (MovieHandler). El código para la clase PersonHandler es este (el código para la clase MovieHandler es casi el mismo):

package com.javacodegeeks.android.apps.moviesearchapp.handlers;
import java.util.ArrayList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.javacodegeeks.android.apps.moviesearchapp.model.Image;
import com.javacodegeeks.android.apps.moviesearchapp.model.Person;

public class PersonHandler extends DefaultHandler {
	private StringBuffer buffer = new StringBuffer();
	private ArrayList<Person> personList;
	private Person person;
	private ArrayList<Image> personImagesList;
	private Image personImage;

	@Override
	public void startElement(String namespaceURI, String localName,String qName, Attributes atts) throws SAXException {
		buffer.setLength(0);
		if (localName.equals("people")) {
			personList = new ArrayList<Person>();
		} else if (localName.equals("person")) {
			person = new Person();
		} else if (localName.equals("images")) {
			personImagesList = new ArrayList<Image>();
		} else if (localName.equals("image")) {
			personImage = new Image();
			personImage.type = atts.getValue("type");
			personImage.url = atts.getValue("url");
			personImage.size = atts.getValue("size");
			personImage.width = Integer.parseInt(atts.getValue("width"));
			personImage.height = Integer.parseInt(atts.getValue("height"));
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName)throws SAXException {
		if (localName.equals("person")) {
			personList.add(person);
		} else if (localName.equals("score")) {
			person.score = buffer.toString();
		} else if (localName.equals("popularity")) {
			person.popularity = buffer.toString();
		} else if (localName.equals("name")) {
			person.name = buffer.toString();
		} else if (localName.equals("id")) {
			person.id = buffer.toString();
		} else if (localName.equals("biography")) {
			person.biography = buffer.toString();
		} else if (localName.equals("url")) {
			person.url = buffer.toString();
		} else if (localName.equals("version")) {
			person.version = buffer.toString();
		} else if (localName.equals("last_modified_at")) {
			person.lastModifiedAt = buffer.toString();
		} else if (localName.equals("image")) {
			personImagesList.add(personImage);
		} else if (localName.equals("images")) {
			person.imagesList = personImagesList;
		}
	}

	@Override
	public void characters(char[] ch, int start, int length) {
		buffer.append(ch, start, length);
	}

	public ArrayList<Person> retrievePersonList() {
		return personList;
	}

}

Aquí se utiliza el tipo de parseado SAX , luego el código de arriba te debe ser familiar si has utilizado alguna vez este parser. Nota que en vez de parámetros qName , es la variable localName la que guarda los datos del elemento.

En nuestra clase, definimos las funciones callback necesarias:

  • startElement: Invocado cuando se encuentra una etiqueta XML de comienzo de nuevo elemento. Inicializamos el campo apropiado ahí.
  • endElement: Invocado cuando se encuentra una etiqueta XML de final del elemento. Entonces los campos correspondientes son rellenados con los datos leidos hasta ese momento.
  • characters: Invocado cuando se encuentra un nuevo texto dentro de un elemento. Un buffer interno es rellenado con el contenido del elemento.

Nota que dentro de la respuesta, puede haber varios elementos Person y dentro de cada uno de ellos, varias Images. Particularmente para las imágenes, la información relevante reside dentro de los atributos del elemento y no dentro del nodo de texto. Asi que se usa el método apropiado getValue para extraer la información.

En este punto hemos preparado la infraestructura para realizar el parseo del XML de las respuestas XML utilizando la aproximación SAX.

El MovieHandler es :

package com.jes.android.smfinder;

import java.util.ArrayList;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class MovieHandler extends DefaultHandler {

	private StringBuffer buffer = new StringBuffer();

	private ArrayList<Movie> moviesList;
	private Movie movie;
	private ArrayList<Image> movieImagesList;
	private Image movieImage;

	@Override
	public void startElement(String namespaceURI, String localName,
			String qName, Attributes atts) throws SAXException {

		buffer.setLength(0);

		if (localName.equals("movies")) {
			moviesList = new ArrayList<Movie>();
		}
		else if (localName.equals("movie")) {
			movie = new Movie();
		}
		else if (localName.equals("images")) {
			movieImagesList = new ArrayList<Image>();
		}
		else if (localName.equals("image")) {
			movieImage = new Image();
			movieImage.type = atts.getValue("type");
			movieImage.url = atts.getValue("url");
			movieImage.size = atts.getValue("size");
			movieImage.width = Integer.parseInt(atts.getValue("width"));
			movieImage.height = Integer.parseInt(atts.getValue("height"));
		}

	}

	@Override
	public void endElement(String uri, String localName, String qName)throws SAXException {

		if (localName.equals("movie")) {
			moviesList.add(movie);
		}
		else if (localName.equals("score")) {
			movie.score = buffer.toString();
		}
		else if (localName.equals("popularity")) {
			movie.popularity = buffer.toString();
		}
		else if (localName.equals("translated")) {
			movie.translated = Boolean.valueOf(buffer.toString());
		}
		else if (localName.equals("adult")) {
			movie.adult = Boolean.valueOf(buffer.toString());
		}
		else if (localName.equals("language")) {
			movie.language = buffer.toString();
		}
		else if (localName.equals("original_name")) {
			movie.originalName = buffer.toString();
		}
		else if (localName.equals("name")) {
			movie.name = buffer.toString();
		}
		else if (localName.equals("type")) {
			movie.type = buffer.toString();
		}
		else if (localName.equals("id")) {
			movie.id = buffer.toString();
		}
		else if (localName.equals("imdb_id")) {
			movie.imdbId = buffer.toString();
		}
		else if (localName.equals("url")) {
			movie.url = buffer.toString();
		}
		else if (localName.equals("votes")) {
			movie.votes = buffer.toString();
		}
		else if (localName.equals("rating")) {
			movie.rating = buffer.toString();
		}
		else if (localName.equals("certification")) {
			movie.certification = buffer.toString();
		}
		else if (localName.equals("overview")) {
			movie.overview = buffer.toString();
		}
		else if (localName.equals("released")) {
			movie.released = buffer.toString();
		}
		else if (localName.equals("version")) {
			movie.version = buffer.toString();
		}
		else if (localName.equals("last_modified_at")) {
			movie.lastModifiedAt = buffer.toString();
		}
		else if (localName.equals("image")) {
			movieImagesList.add(movieImage);
		}
		else if (localName.equals("images")) {
			movie.imagesList = movieImagesList;
		}

	}

	@Override
	public void characters(char[] ch, int start, int length) {
		buffer.append(ch, start, length);
	}

	public ArrayList<Movie> retrieveMoviesList() {
		return moviesList;
	}

}

Eventos en jQuery Mobile

jQuery Mobile ofrece varios eventos personalizados que se han implementado sobre los eventos nativos para asi aumentar las posibilidades de los UI en las plataformas móviles. Nótese que estos eventos emplean varios eventos de tipo touch, mouse o de ventana, dependiendo de la existencia del evento, asi que puedes hacer bind a eventos tanto en plataforma móviles como en entornos desktop. Puedes engancharte a estos eventos como cualquier otro evento jQuery, utilizando los métodos live() o bind().

Eventos touch

tap

Toque de la pantalla rápido

taphold

Toque de la pantalla por al menos un segundo

swipe

Este evento ocurre cuando se realiza un arrastrado horizontal por la pantalla de 30px o más (y menos de 20px verticalmente) durante al menos un segundo.

swipeleft

El mismo evento que el anterior pero cuando la dirección es hacia la izquierda.

swiperight

El mismo evento que el anterior pero cuando la dirección es hacia la derecha

Evento de cambio de orientación

orientationchange

Este evento se dispara cuando la orientación del dispositivo cambia (girándolo verticalmente u horizontalmente). Cuando te enganchas a este eventos, la función de callback puede recibir un segundo argumento, que contiene la property ‘orientation’ cuyos valores posibles son “portrait” o “landscape”.Estos valores son también añadidos como clases al elemento HTML, permitiendo que se pueda seleccionar desde selectores CSS. Nótese que cuando este evento no está soportado por el dispositivo , la implementación actual de jQuery Mobile invoca el evento resize.

Eventos de Scroll

scrollstart

Se dispara cuando comienza el scroll. Nótese que los dispositivos con iOS congelan la manipulación del DOM mientras se hace el scroll, encolando eventos hasta que acaba el scroll. Actualmente se está investigando formas para permitir manipulaciones DOM que se apliquen antes de que comienze el scroll.

scrollstop

Cuando el scroll termina

Eventos de esconder/mostrar Page

Cuandoquiera que una página es mostrada o escondida con jQuery Mobile, se disparan dos eventos sobre esa página. Los eventos disparados dependen de si esa página está siendo mostrada o siendo escondida, asi que cuando una tiene lugar una transición de Page, se disparan 4 eventos: 2 por página

pagebeforeshow

Antes de que la página vaya a ser mostrada

pagebeforehide

Antes de que la página vaya a ser escondida

pageshow

Después de que la página ha sido mostrada

pagehide

Después de que la página ha sido escondida

Nota que estos 4 eventos pueden hacer referencia tanto a la siguiente pagina (nextPage) como a la página previa (prevPage),  dependiendo de si la página esta siendo mostrada o escondida, y de si existe o no la pagina siguiente o previa (la primera página de todas no tiene una página previa a referenciar, pero de todas formas hay un objeto jQuery vació para el caso). Puedes acceder a esta referencia a través del segundo argumento de la función callback. Por ejemplo:

$('div').live('pageshow',function(event, ui){

alert('This page was just hidden: '+ ui.prevPage);

});

$('div').live('pagehide',function(event, ui){

alert('This page was just shown: '+ ui.nextPage);

});

También , para que estos handlers sean invocados durante la carga de la página inicial, debes engancharlos antes de que se ejecute eljQuery Mobile. Esto por ejemplo, se puede hacer en el mobileInit handler, como se describió en el artículo sobre la autoinicializacion de configuraciones.

Eventos de inicialización de Page

Internamente, jQuery Mobile autoinicializa plugins basándose en las convenciones de maquetado encontradas en la “page” que esté. Por ejemplo, un elemento input de tipo rango automáticamente generará un slider control personalizado.

Esta auto-inicializacion se controla por el plugin “page”, que despacha eventos antes y después de que se ejecute, permitiendo manipular un page en la pre/post inicialización, o incluso proporcionarle un comportamiento de inicialización personalizado hasta prevenir de que se hagan autoinicializaciones. Nótese que estos eventos sólo se dispararán una vez por página, a diferencia de los eventos “show/hide”, que se disparan cada vez que una página es mostrada o escondida.

pagebeforecreate

Antes de que se haga la inicializacion

pagecreate

Después de que se haga la inicializacion

$('#aboutPage').live('pagebeforecreate',function(event){

alert('This page was just inserted into the dom!');

});

$('#aboutPage').live('pagecreate',function(event){

alert('This page was just enhanced by jQuery Mobile!');

});

Nótese que si hacemos un binding del eventos pagebeforecreate y hacemos retornar false , prevenimos que el plugin de la page haga sus manipulaciones:

$('#aboutPage').live('pagebeforecreate',function(event){

//run your own enhancement scripting here...

return false;

});

Nota sobre los Page IDs de la versión Alpha 2: En esta versión y anteriores, los elementos page utilizaban el atributo ID para almacenar la localización desde donde venían. Luego cuando colocamos un atributo ID en una page que es mostrada como una página simple dejQuery mobile a través de Ajax, jQuery Mobile envuelve esa página en otro elemento div “page”, conservando asi todas las referencias CSS al ID. Sin embargo , esto significa que el atributo ID ya no está en el elemento “page”, asi que debes acordarte de esto cuando se hagan binding de eventos page (pagebeforecreate, pagecreate, etc). Para evitar problemas , intenta utilizar una clase CSS si es posible.

Eventos de Animación

jQuery Mobile tiene un plugin llamada “animationComplete” , que se ejecuta después de añadir o borrar un class que aplica una transición CSS.

 

Anatomía de una página

[Fuente: web de jquery mobile]

La estructura “Page” de jQuery Mobile se optimiza para soportar tanto páginas sencillas, o también para enlaces internos locales “pages” dentro de una página.

El objetivo de seguir este modelo es permitir a los programadores crear webs utilizando las mejores prácticas – donde los enlaces normales funcionan sin ninguna configuración especial – ya que hacer interfaces de usuario ricos , como si fueran nativos no puede ser conseguido utilizando peticiones HTTP estandars.

La estructura Mobile page

Una web en jQuery Mobile debe comenzar con un ‘doctype’ de HTML5 para poder asi aprovechar todas las ventajas de todas las características del framework (Los dispositivos más antiguos con navegadores que no entienden HTML5 tan solo ignorarán el ‘doctype’ y algunos atributos de personalización). En el ‘head’ es donde ponemos las referencias a la librería de jQuery , jQuery Mobile y el mobiletheme CSS. Recomendamos referenciarlos CDN-Hosted para aumentar el rendimiento:

<!DOCTYPE html>
<html>
<head>
	<title>Page Title</title>
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css" />
	<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.3.min.js"></script>
	<script type="text/javascript" src="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.js"></script>
</head>
<body>
	...content goes here...
</body>
</html>

 

La meta tag Viewport

Nótese que en el código anterior hay un metatag viewport, esto es para especificar como el browser debe pintar el nivel de zoom de la página y las dimensiones. Si no está configurada, muchos navegadores de móviles utilizarán una página “virtual” con una anchura de 900 pixeles para hacerla funcionar bien con los sites de los desktop y las pantallas pueden parecer con un zoom muy cerca y demasiado ancho. Configurando este metatag con content=”width=device-width, initial-scale=1”, la anchura será ajustada a la anchura de la pantalla del dispositivo.

<meta name="viewport" content="width=device-width, initial-scale=1">

Estos settings no deshabilitan la posibilidad que tiene el usuario para hacer zoom de las páginas lo cual es importante desde un punto de la accesibilidad. Hay un bug que ocurre en los iOS y es que a pesar de poner esta metatag cuando se cambia la orientación estas configuraciones no se respetan, esperamos que en futuras versiones sea corregido. Se pueden poner valores a este metatag para deshabilitar el zooming si es necesario.

Dentro del body: Pages

Dentro de la tag <body>, cada vista o “page” dentro de un dispositivo móvil se identifica con un elemento (normalmente un div) con el atributo “data-role” con el valor “page”.

<div  data-role="page">
...
</div>

Dentro de un contenedor “page”, se puede poner cualquier maquetación HTML, pero en las páginas típicas donde se utiliza jQueryMobile, el hijo inmediatamente seguido de un “page” son divs con los data-roles de “header”, “content” y “footer”:

<div data-role="page">
	<div data-role="header">...</div>
	<div data-role="content">...</div>
	<div data-role="footer">...</div>
</div>

 

Poniendolo todo junto: Template de una página básica simple

Poniendolo todo junto , este es un template que puede ser bueno para empezar un proyecto:

<!DOCTYPE html>
<html>
<head>
	<title>Page Title</title>
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css" />
	<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.3.min.js"></script>
	<script type="text/javascript" src="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.js"></script>
</head>

<body>
	<div data-role="page">
	
		<div data-role="header">
		<h1>Page Title</h1>
		</div><!-- /header -->
	
		<div data-role="content">
			<p>Page content goes here.</p>
		</div><!-- /content -->

		<div data-role="footer">
			<h4>Page Footer</h4>
		</div><!-- /footer -->

	</div><!-- /page -->
</body>
</html>

Queda algo como esto:

Template de estructura multi página

Un solo documento HTML puede contener varios ‘pages’ que son cargados juntos , se hace apilando varios divs con el data-role de “page”. Cada bloque ‘page’ necesita un ID único (id=”foo”) que será utilizado para enlazar internamente entre ‘pages’ (href=”#foo”). Cuanod un link se pincha, el framework buscará un ‘page’ interno con el ID especificado y hará una transición hacia ella.

Veamos un ejemplo de un site de 2 ‘page’ programado con dos jQuery mobile divs. Observese que sólo hay que poner un atributo ID en los divs pages cuando queremos que sean enlazados internamente, si son páginas separadas en documentos HTML distintos entonces no es necesario:

<body>
	<!-- Start of first page -->
	<div data-role="page" id="foo">
		<div data-role="header">
			<h1>Foo</h1>
		</div><!-- /header -->
		
		<div data-role="content">
			<p>I'm first in the source order so I'm shown as the page.</p>
			<p>View internal page called <a href="#bar">bar</a></p>
		</div><!-- /content -->
		
		<div data-role="footer">
			<h4>Page Footer</h4>
		</div><!-- /footer -->
		
	</div><!-- /page -->
	
	<!-- Start of second page -->
	<div data-role="page" id="bar">
	
		<div data-role="header">
			<h1>Bar</h1>
		</div><!-- /header -->
	
		<div data-role="content">
			<p>I'm first in the source order so I'm shown as the page.</p>
			<p><a href="#foo">Back to foo</a></p>
		</div><!-- /content -->
	
		<div data-role="footer">
			<h4>Page Footer</h4>
		</div><!-- /footer -->
	
	</div><!-- /page -->
</body>

NOTA: Debido a qué estamos utilizando la hash para guardar el historial de navegación de todas las ‘pages’ Ajax, no es posible poner enlaces a un anchor del tipo index.html#foo para llegar a una page de jQuery Mobile, porque el framework buscará por una page con el ID de #foo en vez de hacer el comportamiento nativo de hacer scrolling al contenido con el ID.

Convenciones , no requerimientos

Aunque la estructura de ‘page’ subrayada arriba es una aproximación recomendada para una web app estándar desarrollada con jQueryMobile, el framework es muy flexible con la estructura del documento. Los elementos page, header, content y footer son opcionales y son útiles en el sentido de proporcionar una estructura básica de formato.

El envoltorio ‘page’ suele requerirse para que funcione la auto-inicializacion pero ahora también es opcional en documentos de una sola página.

Para una página con un custom layout , todos estos elementos pueden ser omitidos pero la navegación Ajax y todos los widgetsfuncionarán como hacen en el template que pusimos arriba. Por detrás, el framework inyectará el envoltorio ‘page’ si no está incluido en el maquetado HTML porque se necesita para manejar pages, pero el maquetado HTML de inicio puede ser extremadamente simple.

Observese que en un setup de múltiples páginas, se requiere tener page wrappers en el maquetado HTML para agrupar el contenido enmultiples pages.

 

Mejora el jQuery con 25 trucos

[Fuente: http://www.tvidesign.co.uk/blog/improve-your-jquery-25-excellent-tips.aspx]

Introduction

jQuery is awesome. I’ve been using it for about a year now and although I was impressed to begin with I’m liking it more and more the longer I use it and the more I find out about it’s inner workings.

I’m no jQuery expert. I don’t claim to be, so if there are mistakes in this article then feel free to correct me or make suggestions for improvements.

I’d call myself an “intermediate” jQuery user and I thought some others out there could benefit from all the little tips, tricks and techniques I’ve learned over the past year. The article also ended up being a lot longer than I thought it was going to be so I’ll start with a table of contents so you can skip to the bits you’re interested in.

1. Carga el framework jQuery desde el Google Code

Google have been hosting several JavaScript libraries for a while now on Google Code and there are several advantages to loading it from them instead of from your server. It saves on bandwidth, it’ll load very quickly from Google’s CDN and most importantly it’ll already be cached if the user has visited a site which delivers it from Google Code.

This makes a lot of sense. How many sites out there are serving up identical copies of jQuery that aren’t getting cached? It’s easy to do too…

<script src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
	// Load jQuery
	google.load("jquery", "1.2.6");
	google.setOnLoadCallback(function() {
		// Your code goes here.
	});
</script>

Or, you can just include a direct reference like this…

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
2. Utiliza una hoja de estilos

Not just a jQuery tip, there are some great cheat sheets out there for most languages. It’s handy having every function on a printable A4 sheet for reference and luckily these guys have produced a couple of nice ones..

http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/
http://colorcharge.com/jquery/

3. Combina todos tus scripts y minimizalos (empaquetándolos para que ocupen menos)

OK, a general JavaScript tip here. But any big project that uses lots of jQuery probably uses lots of plugins (this site uses easing, localScroll, lightbox and preload) so it’s usually applicable.

Browsers can’t load scripts concurrently (well, most can’t, yet), which means that if you’ve got several scripts downloading one at a time then you’re really slowing down the loading of your page. So, assuming the scrips are being loaded on every page then you should consider combining them into one long script before deploying.

Some of the plugins will already be minified, but you should consider packing your scripts and any that aren’t already. It only takes a few seconds. I’m personally a fan of Packer by Dean Edwards

4. Utiliza Firebug para mostrar mensajes de consola

If you haven’t already installed Firebug then you really should. Aside from many other useful features such as allowing you to inspect http traffic and find problems with your CSS it has excellent logging commands that allow you to easily debug your scripts.

Here’s a full explanation of all of it’s features

My favourite features are “console.info”, which you can use to just dump messages and variables to the screen without having to use alert boxes and “console.time” which allows you to easily set up a timer to wrap a bunch of code and see how long it takes. They’re all really easy to use too…

console.time('create list');
for (i = 0; i < 1000; i++) {
	var myList = $('.myList');
	myList.append('This is list item ' + i);
}
console.timeEnd('create list');

In this instance I’ve deliberately written some very inefficient code! In the next few tips I’ll show you how we can use the timer to show some improvements which can be made.

5. Manten las operaciones de selección al mínimo haciendo caché

jQuery selectors are awesome. They make selecting any element on the page incredibly simple, but internally they have to do a fair amount of work and if you go mad with them you might find things starting to get pretty slow.

If you’re selecting the same element time and time again (in a loop for example) then you can just select it once and keep it in memory while you manipulate it to your heart’s content. Take the following example where we add items to an unordered list using a loop.

for (i = 0; i < 1000; i++) {
	var myList = $('.myList');
	myList.append('This is list item ' + i);
}

That takes 1066 milliseconds on my PC in Firefox 3 (imagine how long it would IE6!), which is pretty slow in JavaScript terms. Now take a look at the following code where we use the selector just once.

var myList = $('.myList');
for (i = 0; i < 1000; i++) {
	myList.append('This is list item ' + i);
}

That only takes 224 milliseconds, more than 4x faster, just by moving one line of code.

6. Manten la manipulación del DOM al mínimo

We can make the code from the previous tip even faster by cutting down on the number of times we insert into the DOM. DOM insertion operations like .append() .prepend() .after() and .wrap() are relatively costly and performing lots of them can really slow things down.

All we need to do is use string concatenation to build the list and then use a single function to add them to your unordered list like .html() is much quicker. Take the following example…

var myList = $('#myList');
for (i=0; i<1000; i++){
	myList.append('This is list item ' + i);
}
On my PC that takes 216 milliseconds , just over a 1/5th of a second, but if we build the list items as a string first and use the HTML method to do the insert, like this….
var myList = $('.myList');
var myListItems = '';
for (i = 0; i < 1000; i++) {
	myListItems += '<li>This is list item ' + i + '</li>';
}
myList.html(myListItems);

That takes 185 milliseconds, not much quicker but that’s another 31 milliseconds off the time.

7. Envuelve todo en un solo elemento cuando se haga cualquier tipo de inserción en el DOM

OK, don’t ask me why this one works (I’m sure a more experienced coder will explain).

In our last example we inserted 1000 list items into an unordered list using the .html() method. If we had have wrapped them in the UL tag before doing the insert and inserted the completed UL into another tag (a DIV) then we’re effectively only inserting 1 tag, not 1000, which seems to be much quicker. Like this…

var myList = $('.myList');
var myListItems = '<ul>';
for (i = 0; i < 1000; i++) {
	myListItems += '<li>This is list item ' + i + '</li>';
}
myListItems += '</ul>';
myList.html(myListItems);

The time is now only 19 milliseconds, a massive improvement, 50x faster than our first example.

8. Utiliza IDs en vez de classes cuando sea posible

jQuery makes selecting DOM elements using classes as easy as selecting elements by ID used to be, so it’s tempting to use classes much more liberally than before. It’s still much better to select by ID though because jQuery uses the browser’s native method (getElementByID) to do this and doesn’t have to do any of it’s own DOM traversal, which is much faster. How much faster? Let’s find out.

I’ll use the previous example and adapt it so each LI we create has a unique class added to it. Then I’ll loop through and select each one once.

// Create our list
var myList = $('.myList');
var myListItems = '<ul>';
for (i = 0; i < 1000; i++) {
	myListItems += '<li class="listItem' + i + '">This is a list item</li>';
}
myListItems += '</ul>';
myList.html(myListItems);
// Select each item once
for (i = 0; i < 1000; i++) {
	var selectedItem = $('.listItem' + i);
}

Just as I thought my browser had hung, it finished, in 5066 milliseconds (over 5 seconds). So i modified the code to give each item an ID instead of a class and then selected them using the ID.

// Create our list
var myList = $('.myList');
var myListItems = '<ul>';
for (i = 0; i < 1000; i++) {
	myListItems += '<li id="listItem' + i + '">This is a list item</li>';
}
myListItems += '</ul>';
myList.html(myListItems);
// Select each item once
for (i = 0; i < 1000; i++) {
	var selectedItem = $('#listItem' + i);
}

This time it only took 61 milliseconds. Nearly 100x faster.

9. Dale a tus selectores un contexto

By default, when you use a selector such as $(‘.myDiv’) the whole of the DOM will be traversed, which depending on the page could be expensive.

The jQuery function takes a second parameter when performing a selection.

jQuery( expression, context )

By providing a context to the selector, you give it an element to start searching within so that it doesn’t have to traverse the whole of the DOM.

To demonstrate this, let’s take the first block of code from the tip above. It creates an unordered list with 1000 items, each with an individual class. It then loops through and selects each item once. You’ll remember that when selecting by class it took just over 5 seconds to select all 1000 of them using this selector.

  1. var selectedItem = $(‘#listItem’ + i);

I then added a context so that it was only running the selector inside the unordered list, like this…

  1. var selectedItem = $(‘#listItem’ + i, $(‘.myList’));

It still took 3818 milliseconds because it’s still horribly inefficient, but that’s more than a 25% speed increase by making a small modification to a selector.

10. Utiliza el encadenado de forma correcta

One of the coolest things about jQuery is it’s ability to chain method calls together. So, for example, if you want to switch the class on an element.

  1. $(‘myDiv’).removeClass(‘off’).addClass(‘on’);

If you’re anything like me then you probably learned that in your first 5 minutes of reading about jQuery but it goes further than that. Firstly, it still works across line breaks (because jQuery = JavaScript), which means you can write neat code like this…

  1. $(‘#mypanel’)
  2. .find(‘TABLE .firstCol’)
  3. .removeClass(‘.firstCol’)
  4. .css(‘background’ : ‘red’)
  5. .append(‘<span>This cell is now red</span>’);

Making a habit of using chaining automatically helps you to cut down on your selector use too.

But it goes further than that. Let’s say that you want to perform several functions on an element but one of the first functions changes the element in some way, like this…

  1. $(‘#myTable’).find(‘.firstColumn’).css(‘background’,’red’);

We’ve selected a table, drilled down to find cells with a class of “firstColumn” and coloured them in red.

Let’s say we now want to colour all the cells with a class of “lastColumn” blue. Because we’ve used the find() funciton we’ve filtered out all the cells that don’t have a class of “firstColumn” so we need to use the selector again to get the table element and we can’t continue chaining, right? Luckily jQuery has an end() function which actually reverts back to the previous unaltered selection so you can carry on chaining, like this…

  1. $(‘#myTable’)
  2. .find(‘.firstColumn’)
  3. .css(‘background’,’red’)
  4. .end()
  5. .find(‘.lastColumn’)
  6. .css(‘background’,’blue’);

It’s also easier than you might think to write your own jQuery function which can chain. All you have to do is write a function which modifies an element and returns it.

  1. $.fn.makeRed = function() {
  2. return $(this).css(‘background’, ‘red’);
  3. }
  4. $(‘#myTable’).find(‘.firstColumn’).makeRed().append(‘hello’);

How easy was that?

11. Aprende a utilizar “animate” de forma correcta

When I first started using jQuery I loved the fact that it was easy to use the pre-defined animations like slideDown() and fadeIn() to get some really cool effects incredibly easy. It’s easy to take things further though because jQuery’s animate() method is very easy to use and very powerful. In fact, is you look at the jQuery source code you’ll see that internally those methods are just shortcuts which use the animate() function.

  1. slideDown: function(speed,callback){
  2. return this.animate({height: “show”}, speed, callback);
  3. },
  4. fadeIn: function(speed, callback){
  5. return this.animate({opacity: “show”}, speed, callback);
  6. }

The animate() method simply takes any CSS style and smoothly transitions it from one value to another. So, you can change the width, height, opacity, background-color, top, left, margin, color, font-size, anything you want.

This is how easy it is to animate all your menu items grow to 100 pixels high when you roll over them.

  1. $(‘#myList li’).mouseover(function() {
  2. $(this).animate({“height”: 100}, “slow”);
  3. });

Unlike other jQuery functions, animations are automatically queued, so if you want to run a second animation once the first is finished then just call the animate method twice, no callback necessary.

  1. $(‘#myBox’).mouseover(function() {
  2. $(this).animate({ “width”: 200 }, “slow”);
  3. $(this).animate({“height”: 200}, “slow”);
  4. });

If you want the animations to happen concurrently then just put both styles in the params object of a single call, like this…

  1. $(‘#myBox’).mouseover(function() {
  2. $(this).animate({ “width”: 200, “height”: 200 }, “slow”);
  3. });

You can animate any property that’s numeric. You can also download plugins to help you animate properties that aren’t, likecolors and background colors

12. Aprende sobre delegación de eventos

jQuery makes it easier than ever to attach events to elements in the DOM unobtrusively, which is great, but adding too many events is inefficient. Event delegation allows you to add less events to achieve the same result in many situations. The best way to illustrate this is with an example…

  1. $(‘#myTable TD’).click(function(){
  2. $(this).css(‘background’, ‘red’);
  3. });

A simple function which turns cells in a table red when you click on them. Let’s say that you’ve got a grid with 10 columns and 50 rows though, that’s 500 events bound. Wouldn’t it be neater if we could just attach a single event to the table and when the table is clicked have the event handler work out which cell was clicked before turning it red?

Well that’s exactly what event delegation is and it’s easy to implement…

  1. $(‘#myTable’).click(function(e) {
  2. var clicked = $(e.target);
  3. clicked.css(‘background’, ‘red’);
  4. });

‘e’ contains information about the event, including the target element that actually received the click. All we have to do is inspect it to see which cell was actually clicked. Much neater.

Event delegation has another benefit. Normally, When you bind a handler to a collection of elements it gets attached to those elements and those elements only. If you add new elements to the DOM which would have been matched by the selector then they don’t have the event handler bound to them (are you following me?) then nothing will happen.

When using event delegation you can add as many matching elements to the DOM as you like after the event is bound and they work too.

13. Utiliza clases para almacenar el estado

This is the most basic way of storing information about a block of html. jQuery is great at manipulating elements based upon their classes, so if you need to store information about the state of an element then why not add an extra class to store it?

Here’s an example. We want to create an expanding menu. When you click the button we want the panel to slideDown() if it’s currently closed, or slideUp() if it’s currently open. We’ll start with the HTML

  1. <div class=”menuItem expanded”>
  2. <div class=”button”>
  3. click me
  4. </div>
  5. <div class=”panel”>
  6. <ul>
  7. <li>Menu item 1</li>
  8. <li>Menu item 2</li>
  9. <li>Menu item 3</li>
  10. </ul>
  11. </div>
  12. </div>

Very simple! We’ve just added an extra class to the wrapper div which serves no other purpose other than to tell us the state of the item. So all we need is a click event handler which performs slideUp() or slideDown() on the corresponding panel when the button is clicked.

  1. $(‘.button’).click(function() {
  2. var menuItem = $(this).parent();
  3. var panel = menuItem.find(‘.panel’);
  4. if (menuItem.hasClass(“expanded”)) {
  5. menuItem.removeClass(‘expanded’).addClass(‘collapsed’);
  6. panel.slideUp();
  7. }
  8. else if (menuItem.hasClass(“collapsed”)) {
  9. menuItem.removeClass(‘collapsed’).addClass(‘expanded’);
  10. panel.slideDown();
  11. }
  12. });

That’s a very simple example, but you can add extra classes for storing all sorts of information about an element or HTML fragment.

However, in all but simple cases it’s probably better to use the next tip.

14. Incluso mejor, utilizar el método interno data() de jQuery para almacenar el estado

It’s not very well documented for some reason but jQuery has an internal data() method which can be used to store information in key/value pairs against any DOM element. Storing a piece of data is as simple as this…

  1. $(‘#myDiv’).data(‘currentState’, ‘off’);

We can amend the example from the previous tip. We’ll use the same HTML (with the “expanded” class removed) and use the data() function instead.

  1. $(‘.button’).click(function() {
  2. var menuItem = $(this).parent();
  3. var panel = menuItem.find(‘.panel’);
  4. if (menuItem.data(‘collapsed’)) {
  5. menuItem.data(‘collapsed’, false);
  6. panel.slideDown();
  7. }
  8. else {
  9. menuItem.data(‘collapsed’, true);
  10. panel.slideUp();
  11. }
  12. });

I’m sure you’ll agree this is much neater. For more information about data() and removeData(), see this page…

jQuery internals

15. Escribe tus propios selectores

jQuery has loads of built-in selectors for selecting elements by ID, class, tag, attribute and many more. But what do you do when you need to select elements based upon something else and jQuery doesn’t have a selector?

Well, one answer would be to add classes to the elements from the start and use those to select them, but it turns out that it’s not hard to extend jQuery to add new selectors.

The best way to demonstrate is with an example.

  1. $.extend($.expr[‘:’], {
  2. over100pixels: function(a) {
  3. return $(a).height() > 100;
  4. }
  5. });
  6. $(‘.box:over100pixels’).click(function() {
  7. alert(‘The element you clicked is over 100 pixels high’);
  8. });

The first block of code creates a custom selector which finds any element that is more than 100 pixels tall. The second block just uses it to add a click handler to all those elements.

I won’t go into any more detail here but you can imagine how powerful this is and if you search google for “custom jquery selector” you’ll find loads of great examples.

16. Streamline tu HTML y modificalo una vez que la pagina ha cargado

The title might not make a lot of sense but this tip can potentially neaten up your code, reduce the weight and download time of your page and help your SEO. Take the following HTML for example…

  1. <div class=”fieldOuter”>
  2. <div class=”inner”>
  3. <div class=”field”>This is field number 1</div>
  4. </div>
  5. <div class=”errorBar”>
  6. <div class=”icon”><img src=”icon.png” alt=”icon” /></div>
  7. <div class=”message”><span>This is an error message</span></div>
  8. </div>
  9. </div>
  10. <div class=”fieldOuter”>
  11. <div class=”inner”>
  12. <div class=”field”>This is field number 2</div>
  13. </div>
  14. <div class=”errorBar”>
  15. <div class=”icon”><img src=”icon.png” alt=”icon” /></div>
  16. <div class=”message”><span>This is an error message</span></div>
  17. </div>
  18. </div>

That’s an example of how a form might be marked up, modified slightly for illustrative purposes. I’m sure you’ll agree it’s pretty ugly and if you had a long form you’d end up with a fairly long ugly page. It’s be nicer if you could just put this in your HTML.

  1. <div class=”field”>This is field 1</div>
  2. <div class=”field”>This is field 2</div>
  3. <div class=”field”>This is field 3</div>
  4. <div class=”field”>This is field 4</div>
  5. <div class=”field”>This is field 5</div>

All you have to do is a bit of jQuery manipulation to add all the ugly HTML back in. Like this…

  1. $(document).ready(function() {
  2. $(‘.field’).before(‘<div class=”fieldOuter”><div class=”inner”>’);
  3. $(‘.field’).after(‘</div><div class=”errorBar”><div class=”icon”>
  4. <img src=”icon.png” alt=”icon” /></div><div class=”message”>
  5. <span>This is an error message</span></div></div></div>’);
  6. });

It’s not always advisable to do this, you’ll get a bit of a flash as the page loads, but in certain situations where you’ve got a lot of repeated HTML it can really reduce your page weight and the SEO benefits of reducing all your repeated extraneous markup should be obvious.

17. Lazy load para el contenido para beneficios de velocidad y beneficios SEO

Another way to speed up your page loads and neaten up the HTML that search spiders see is to lazy load whole chunks of it using an AJAX request after the rest of the page has loaded. The user can get browsing right away and spiders only see the content you want them to index.

We’ve used this technique on our own site. Those purple buttons at the top of the page drop down 3 forms, directions and a google map, which was doubling the size of our pages. So, we just put all that HTML in a static page and use the load() function to load it in once the DOM was ready. Like this…

  1. $(‘#forms’).load(‘content/headerForms.html’, function() {
  2. // Code here runs once the content has loaded
  3. // Put all your event handlers etc. here.
  4. });

I wouldn’t use this everywhere. You have to consider the trade offs here. You’re making extra requests to the server and portions of your page might not be available to the user right away, but used correctly it can be a great optimization technique.

18. Utiliza funciones de utility de jQuery

jQuery isn’t just about flash effects. The creator has exposed some really useful methods which fill a few gaps in JavaScript’s repertoire.

http://docs.jquery.com/Utilities

In particular, browser support for certain common array functions is patchy (IE7 doesn’t even have an indexOf() method!). Jquery has methods for iterating, filtering, cloning, merging and removing duplicates from Arrays.

Other common functions that are difficult in Javascript include getting the selected item in a drop down list. In plain old JavaScript you’d have to get the <select> element using getElementByID, get the child elements as an array and iterate through them checking whether each one was selected or not. jQuery makes it easy…

  1. $(‘#selectList’).val();

It’s worth spending some time looking through the jQuery documentation on the main site and having a nose around some of the lesser known functions.

19. Utiliza noconflict para renombrar el objeto jquery cuando se usan otros frameworks

Most javascript frameworks make use of the $ symbol as a shorthand and this can cause clashes when trying to use more than one framework on the same page. Luckily there’s a simple solution. The .noconflict() function gives control of the $ back and allows you to set your own variable name, like this…

var $j = jQuery.noConflict();
$j('#myDiv').hide();

20. Como decir cuando las imágenes han terminado de cargarse

This is another one of those problems that doesn’t seem to be as well documented as it should be (not when I went looking anyway) and it’s a fairly common requirement when building photo galleries, carousels etc, but it’s fairly easy.

All you have to do is use the .load() method on an IMG element and put a callback function in it. The following example changes the “src” attribute of an image tag to load a new image and attaches a simple load function.

$('#myImage').attr('src', 'image.jpg').load(function() {
	alert('Image Loaded');
});

You should find that the alert is called as soon as the image is loaded.

21. Utiliza siempre la ultima version

jQuery is constantly improving and John Resig, it’s creator, always seems to be in search of ways to improve performance.

jQuery is currently on version 1.2.6 but John has already revealed that he’s working on a new selector engine called Sizzle, which may apparently improve selector speeds in Firefox by up to 4x. So, it pays to keep up to date.

22. Como comprobar si un elemento existe

You don’t need to check if an element exists on the page before you manipulate it because jQuery will will simply do nothing if you try to select something and it isn’t in the DOM. But when you do need to check if anything has been selected, or how many items have been selected you can use the length property.

if ($('#myDiv).length) {
	// your code
}

Simple, but not obvious.

23. Añade un clase JS a tu atributo HTML

I learned this tip from Karl Swedberg whose excellent books I used to learn jQuery.

He recently left a comment on one of my previous articles about this technique and the basics are as follows…

Firstly, as soon as jQuery has loaded you use it to add a “JS” class to your HTML tag.

  1. $(‘HTML’).addClass(‘JS’);

Because that only happens when javascript is enabled you can use it to add CSS styles which only work if the user has JavaScript switched on, like this…

  1. .JS #myDiv{display:none;}

So, what this means is that we can hide content when JavaScript is switched on and then use jQuery to show it when necessary (e.g. by collapsing some panels and expanding them when the user clicks on them), while those with JavaScript off (and search engine spiders) see all of the content as it’s not hidden. I’ll be using this one a lot in the future.

To read his full article click here.

24. Retorna ‘false’ para prevenir el comportamiento por defecto

This should be an obvious one but maybe not. if you have a habit of doing this…

  1. <a href=”#” class=”popup”>Click me!</a>

… and then attaching an event handler like this…

  1. $(‘popup’).click(function(){
  2. // Launch popup code
  3. });

… it’ll probably work fine until you use it on a long page, at which point you’ll notice that the # is causing it to jump to the top of the page when your click event is triggered.

All you have to do to prevent this default behaviour, or indeed any default behaviour on any event handler is to add “return false;” to your handler, like this…

  1. $(‘popup’).click(function(){
  2. // Launch popup code
  3. return false;
  4. });

25. Abreviatura para el evento ready

A small tip this one but you can save a few characters by using shorthand for the $(document).ready function.

Instead of this…

  1. $(document).ready(function (){
  2. // your code
  3. });

You can do this…

  1. $(function (){
  2. // your code
  3. });

Aplicación completa II : Utilizando el API de HTTP

(Fuente: http://www.javacodegeeks.com/2010/10/android-full-app-part-2-using-http-api.html)

En la primera parte de esta serie hemos creado el interfaz básico para la actividad principal de la aplicación. En esta parte vamos a ver cómo utilizar un API HTTP externo y cómo integrar las posibilidades de búsqueda dentro de nuestra aplicación.

El TMDb API

Para la búsqueda de actores y películas vamos a utilizar el TMDb API (http://api.themoviedb.org/2.1). Según la definición oficial:“El TMDbAPI es un recurso potente para todos los programadores que quieren integrar datos de películas junto con posters o chismes de películas. Todos los métodos API están disponibles en XML, YAML y JSON”.

Como la mayoría de los APIs disponibles, necesitas un valid key para poder utilizar el API. El primer paso para eso es crearse un cuenta gratuito en la página http://www.themoviedb.org/account/signup. Después de registrarte, haz logging en tu cuenta y encuentra el link para generar un API key. En mi caso me registro ,y después de rellenar un formulario me dicen que me la enviaran en 3 días laborables.

La lista de los métodos disponibles en el API los puedes encontrar en la documentación del TMDb aPI y los más importantes son las siguientes:

  • Movie.search: proporciona la forma más fácil y rápida de buscar una película.Para buscar info sobre la peli “Transformers” ejemplo de URL es el siguiente:

http://api.themoviedb.org/2.1/Movie.search/en/xml/APIKEY/Transformers

  • Person.search: se utiliza para buscar por un actor, actriz o miembro de producción, un ejemplo de búsqueda de actores:

http://api.themoviedb.org/2.1/Person.search/en/xml/APIKEY/Brad+Pitt

(donde APIKEY debe ser reemplazado por un API key válida)

Como puedes ver, el API es fácil de utilizar. Sólo hay que hacer la request HTTP con las URL especificadas y entonces recuperar las respuestas en un formato predefinido. Proximamente, vamos a ver las posibilidades de networking del Android para utilizar el API y recoger y presentar los datos proporcionados. Observese que utilizaremos el formato XML para las respuestas, aunque esto lo veremos en el siguiente artículo de la serie.

Haciendo peticiones HTTP

Para manipular las HTTP requests/responses en un entorno Android, las clases estándar del paquete java.net pueden ser utilizadas. Asi, clases como URL, URLConnection, HttpURLConnection etc todas pueden ser utilizadas de la forma ya conocida. Sin embargo, si queremos despreocuparnos por los detalles de bajo nivel podemos utilizar las Apache HTTP Client libraries. Estas librerías están basadas en el ya conocido Apache Commons HTTP Client frameworkhttp://hc.apache.org/

Empezemos con el código. Crearemos una clase llamada “HttpRetriever” que será responsable de realizar todas las HTTP requests y retornará las respuestas tanto en formato texto como en stream (para manipulación de imágenes). El código para esta clase es el siguiente:

package com.jes;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

import com.jes.io.FlushedInputStream;
import com.jes.util.Utils;

public class HttpRetriever {

	private DefaultHttpClient client = new DefaultHttpClient();

	public String retrieve(String url) {
		HttpGet getRequest = new HttpGet(url);

		try {
			HttpResponse getResponse = client.execute(getRequest);
			final int statusCode = getResponse.getStatusLine().getStatusCode();

			if (statusCode != HttpStatus.SC_OK) {
				Log.w(getClass().getSimpleName(), "Error " + statusCode + " for URL " + url);
				return null;
			}

			HttpEntity getResponseEntity = getResponse.getEntity();
			if (getResponseEntity != null) {
				return EntityUtils.toString(getResponseEntity);
			}
		} catch (IOException e) {
			getRequest.abort();
			Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
		}

		return null;

	}

	public InputStream retrieveStream(String url) {
		HttpGet getRequest = new HttpGet(url);
		try {
			HttpResponse getResponse = client.execute(getRequest);
			final int statusCode = getResponse.getStatusLine().getStatusCode();
			if (statusCode != HttpStatus.SC_OK) {
				Log.w(getClass().getSimpleName(), "Error " + statusCode + " for URL " + url);
				return null;
			}

			HttpEntity getResponseEntity = getResponse.getEntity();
			return getResponseEntity.getContent();
		}catch (IOException e) {
			getRequest.abort();
			Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
		}

		return null;
	}

	public Bitmap retrieveBitmap(String url) throws Exception {
		InputStream inputStream = null;
		try {
			inputStream = this.retrieveStream(url);
			final Bitmap bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
			return bitmap;
		}finally {
			Utils.closeStreamQuietly(inputStream);
		}	

	}

}

Para la ejecución de las peticiones HTTP, estamos utilizando una instancia de la clase DefaultHttpClient, la cual , como su nombre indica, es la implementación por defecto de un cliente HTTP, es decir, la implementación por defecto del interface HttpClient. El HTTP client ejecuta la request y proporciona un objeto HttpResponse que contiene la respuesta del servidor además de cualquier otra información.

Por ejemplo, podemos recuperar el response status code y compararlo el código con el código HttpStatus.SC_OK para ver si la petición ha ido bien. Cuando la petición ha ido bien, tomamos la referencia del objeto HttpEntity que está anidado desde el cual podemos tener acceso a los datos de la respuesta.

Para respuestas de texto convertimos la entity a String utilizando un método de la EntityUtils. Si deseamos recuperar los datos como un byte stream (por ejemplo para cuando nos bajemos un binario), utilizaremos el método getContent de la clase HttpEntity, que crea un nuevo objeto InputStream de la entity.

Observese que hay también un tercer método para retornar directamente objetos Bitmap. Esto puede ser útil en las últimos artículos de esta serie, donde descargaremos imágenes de internet. En ese método, ejecutaremos una GET request y recuperaremos un InputStream como siempre. Entonces, utilizaremos un método decodeStream de la clase BitmapFactory para crear un nuevo objeto Bitmap. Noteseque no proporcionamos directamente el InputStream descargado, sino que primero lo envolvemos en una clase FlushedInputStream. (En los foros de Android developers , se comenta que hay un bug en versiones previas del método decodeStream que puede causar problemas a la hora de descargar una imagen en una conexión lenta. Con la clase FlushedInputStream, que extiende FilterInputStream, se utiliza para saltarnos ese bug). El código de FlushedInputStream es el siguiente:

package com.javacodegeeks.android.apps.moviesearchapp.io;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FlushedInputStream extends FilterInputStream {
	public FlushedInputStream(InputStream inputStream) {
		super(inputStream);
	}

	@Override
	public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;
			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);
				if (bytesSkipped == 0L) {
				int b = read();
				if (b < 0) {
					break;  // we reached EOF
				} else {
					bytesSkipped = 1; // we read one byte
				}
			}
			totalBytesSkipped += bytesSkipped;
		}
		return totalBytesSkipped;
	}

}

Con el método skip() sobrecargado nos aseguramos que de hecho se coge el número proporcionado de bytes, a menos que se llegue al final del fichero. Finalmente, utilizaremos el método closeStreamQuietly de la clase Utils para manejar las excepciones que pueden ocurrir cuando cerramos un InputStream. El código es el siguiente:

package com.javacodegeeks.android.apps.moviesearchapp.util;

import java.io.IOException;
import java.io.InputStream;

public class Utils {
	public static void closeStreamQuietly(InputStream inputStream) {
		try {
			if (inputStream != null) {
				inputStream.close();
			}
		} catch (IOException e) {
			// ignore exception
		}
	}
}

Dando permisos para realizar HTTP requests

Finalmente, para realizar las HTTP requests deben darse los permisos correspondientes. Asi , añadimos el android.permission.INTERNET al AndroidManifest.xml del proyecto, quedando el archivo como sigue:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
			package="com.javacodegeeks.android.apps.moviesearchapp"
			android:versionCode="1"
			android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">
	<activity android:name=".MovieSearchAppActivity" android:label="@string/app_name">
		<intent-filter>
			<action android:name="android.intent.action.MAIN" />
			<category android:name="android.intent.category.LAUNCHER" />
		</intent-filter>
	</activity>
</application>

<uses-sdk android:minSdkVersion="3" />
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

</manifest>

Asi , tenemos preparada la infraestructura para ejecutar HTTP GET requests, En los siguiente tutos, utilizaremos estas clases para recuperar datos XML e imágenes según las necesidades de nuestra aplicación.