En la publicación anterior Panels y Widgets en GWT vimos que GWT
provee una gran variedad de widgets que permiten diseñar una interface de
usuario de acuerdo con especificaciones propias de la aplicación. Sin embargo,
a medida que vaya utilizando nuevas técnicas de desarrollo con GWT, encontrará
la necesidad de manejar un grupo de widgets como si fuera uno solo. Es aquí
cuando comenzará a construir sus propios widgets basados en otros existentes.
Estos nuevos widgets se denominan composite
widgets.
Composite widgets no son más que una colección de
widgets, ya sea provistos dentro del conjunto de paquetes de GWT o que hayan
sido creados por otros usuarios o compañías expertas en este campo. Normalmente
un composite widget agrupa otros widgets pero se comporta como si fuera una
sola entidad que ofrece una funcionalidad específica. Por ejemplo, un composite
widget muy común corresponde a una interface de login, la cual requiere como
entrada un usuario y una contraseña para ingresar a un sistema en particular. Además
sus componentes tienen la capacidad de disparar eventos de acuerdo a las
acciones hechas sobre ellos y aprueba o desaprueba la transición a otro estado
de la página o a otra página de la aplicación. Esta interface de usuario se construye
utilizando widgets tales como Labels, TextBoxes, Buttons, Images y Panels que
facilitarán la distribución de los widgets. La interface también podrá realizar
algunos efectos como desplegar un mensaje de error cuando el usuario ha entrado
un usuario o contraseña inválidos, es decir, aquí habrá implementado un efecto
sobre la interface.
GWT ofrece la facilidad de construir componentes de
usuario de tal suerte que esto ha permitido que otras compañías construyan
librerías completas que se pueden integrar a sus proyectos. De manera que
anímese para crear las suyas y así obtener un ingreso adicional.
Estos nuevos componentes se construyen de tres maneras
diferentes:
·
Creando
un widget conteniendo widgets existentes. Este es un composite widget.
·
Creando
un nuevo widget escrito completamente en Java.
·
Creando
un widget que contiene JavaScript
utilizando métodos de JSNI (JavaScript Native Interface).
En este artículo solamente cubriremos la primera forma
para crear widgets: composite widgets.
Construyendo
un Composite widget
La construcción de un composite requiere que la nueva
clase extienda la clase Composite,
la cual es tiene la capacidad de contener otros widgets escondiendo los métodos
del widget contenido.
Típicamente un composite se conforma de un panel que a
su vez contiene otros panels y widgets. Algunos de los widgtes proporcionados
por GWT son en realidad composites. Por ejemplo, TabPanel es un composite compuesto de un TabBar y un DeckPanel.
El siguiente ejemplo que he denominado LoginComposite, es una aplicación que
ilustra paso a paso el procedimiento para crear un composite. Construiremos una
interface de login para acceder a alguna aplicación. El contenedor principal
será un DialogBox, que a su vez
contendrá un VerticalPanel que permitirá ubicar los demás widgets en una sola
columna. Los botones los pondremos dentro de un HorizontalPanel para ubicarlos uno al lado del otro en la misma
línea. El HorizontalPanel también
será parte del VerticalPanel. Es
recomendable que diseñe primero su aplicación en papel o utilizando la tableta
de su preferencia, esto facilitará la labor de codificación y visualizará con
anticipación los panesl y widgets que se ajustarán a su diseño. Con estas
indicaciones pasemos entonces a crear el proyecto.
Cree
un nuevo proyecto GWT
Desde el menú principal de Eclipse seleccione:
File
à New à Web Application Project
Deje la opción Sample
Code activa para generar la estructura de un proyecto cliente/ servidor,
aunque tan sólo utilizará la aplicación del cliente, facilitará la tarea de
crear las clases y la página html desde el principio. También tendrá que
cambiar parte del código generado por el wizard de GWT con su propio código.
En el navegador de Eclipse observe la estructura del
proyecto:
Los archivos resaltados son los principales archivos
que se utilizarán para crear la interface. Tan solo queda pendiente crear la
clase de usuario que extenderá la clase Composite.
Cree
una clase Entry Point
Abra la aplicación Login.java, borre el código original y copie el siguiente:
package
com.example.compositelogin.client;
import
com.google.gwt.core.client.EntryPoint;
import
com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.HTML;
public class Login implements
EntryPoint {
@Override
public void
onModuleLoad() {
// TODO
Auto-generated method stub
RootPanel.get().add(new LoginComposite());
}
}
|
Esta clase implementa la interface EntryPoint y será la clase a invocarse
desde la siguiente línea de la página Login.html:
<script type="text/javascript" language="javascript" src="login/login.nocache.js"></script>
|
La clase
Login.java declara RootPanel
como el contenedor principal de la aplicación. A través del método get(), RootPanel consigue donde insertar la interface dentro de la página
HTML. Si la página HTML define una etiqueta div con un identificador, el método get(“id”) de Rootpanel accede a esa sección de la página y posiciona allí la
interface construida en LoginComposite.
La siguiente línea de código se encarga de hacer todo este trabajo:
RootPanel.get().add(new
LoginComposite());
|
A continuación creará la clase que implementará la
interface. Seleccione el paquete
com.example.login.client, click derecho y seleccione New à Class.
Entre el nombre LoginComposite:
Click en Finish.
Abra el archivo LoginComposite.java
e inserte la siguiente línea de código después de la sentencia de definición
del paquete:
import
com.google.gwt.user.client.ui.Composite;
|
Esta clase se utilizará en la declaración
de la clase LoginComposite, tal y
como se muestra en la siguiente línea de código.
public class LoginComposite
extends Composite {
|
Ahora incluya las sentencias para
instanciar los panels y widgets que se utilizarán en la interface. Después de
la línea de identificación de la clase, defina las siguientes variables:
private DialogBox
dbLogin = new DialogBox();
private
VerticalPanel vpContainer = new
VerticalPanel();
private
Label lblError = new Label("Usuario o Contrase\u00F1a inv\u00E1lidos");
private
HorizontalPanel hpError = new HorizontalPanel();
private Button
btnEntrar = new
Button("Entrar");
private Button
btnCancel = new
Button("Cancelar");
private Label lblUsr = new Label("Usuario:");
private Label lblPasswd = new Label("Contrase" + "\u00F1" + "a:");
private
TextBox txtUsr = new TextBox();
private PasswordTextBox
txtPasswd = new
PasswordTextBox();
private
HorizontalPanel hpButtons = new
HorizontalPanel();
|
El editor de Eclipse notará que los tipos de las
variables declaradas no han sido importados. Para solucionar este
inconveniente, importe las siguientes clases a la clase LoginComposite:
import
com.google.gwt.user.client.ui.DialogBox;
import
com.google.gwt.user.client.ui.VerticalPanel;
import
com.google.gwt.user.client.ui.HorizontalPanel;
import
com.google.gwt.user.client.ui.Button;
import
com.google.gwt.user.client.ui.Label;
import
com.google.gwt.user.client.ui.TextBox;
import
com.google.gwt.user.client.ui.PasswordTextBox;
import
com.google.gwt.user.client.Window;
import
com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ClickEvent;
|
La clase DialogBox,
la cual es un derivado de la clase Panel,
definirá el marco de referencia de la interface de usuario. Este panel tiene la
propiedad setText que permite
definir un título a la interface. Agregue el título “Entrada al Sistema” o
sustitúyalo por otro de su preferencia:
dbLogin.setText("Entrada
al Sistema");
|
Es hora de indicarle a la clase Composite, cuál es el
widget que contendrá la totalidad de la interface:
initWidget(dbLogin);
|
initWidget es un método de la clase Composite que permite al panel dbLogin
ser envuelto (wrapped) por esta clase. Debe incluirse en el constructor de la
clase y antes de invocar otro widget o panel. Utilice initWidget solamente una vez dentro de la clase.
dbLogin será el contenedor del panel vertical vpContainer en donde se ubicarán los
widgets y panels que conformarán la interface.
El label lblError
se ha declarado previamente con el mensaje “Usuario o contraseña inválidos”, se
utilizará como primer elemento del contenedor vertical vpContainer. Asigne el valor false a la propiedad setVisible del label para que la
interface no lo despliegue cuando se cargue por primera vez en el navegador de
internet:
vpContainer.setSpacing(4);
vpContainer.add(lblError);
lblError.setVisible(false);
|
Ahora incluya los labels, cajas de texto y los botones
en el panel vertical vpContainer:
vpContainer.add(lblUsr);
vpContainer.add(txtUsr);
vpContainer.add(lblPasswd);
vpContainer.add(txtPasswd);
hpButtons.setSpacing(12);
hpButtons.add(btnEntrar);
hpButtons.add(btnCancel);
vpContainer.add(hpButtons);
|
Se ha creado el panel horizontal hpButtons como contenedor de los botones btnEntrar y btnCancel,
así quedarán posicionados horizontalmente en la misma línea o fila. El panel hpButtons se alineará verticalmente
dentro del panel vpContainer.
Los botones responden a los eventos que actúan sobre
ellos. Inserte el código que implementa los manejadores de eventos. La clase Button hereda de la clase FocusWidget el método addClickHandler el cual procesa los eventos
sobre los botones. En principio, incluya el código en el botón btnEntrar:
//Manejadores
de click
btnEntrar.addClickHandler(new
ClickHandler() {
@Override
public void
onClick(ClickEvent event) {
onbtnEntrarClick();
}
});
|
El anterior
fragmento de código hará que se ejecute el método onbtnEntrarClick() cuando el usuario de un click sobre el botón btnEntrar.
Como ejercicio cree el código para el botón btnCancel.
Por último, adicione el código para el método onbtnEntrarClick(). En él se analizará
si la combinación usuario/contraseña es válida.
Por ahora el usuario y la contraseña serán valores
estáticos dentro de la aplicación, aunque no es la mejor forma de
autenticación, el ejemplo ilustra cómo implementar el código en respuesta a los
eventos. En este caso, el usuario admin
y la contraseña admin serán los
valores para una correcta autenticación. Si la autenticación es válida, la
interface desplegará una ventana de bienvenida, en caso contrario hará visible
el label lblError:
void
onbtnEntrarClick() {
if (txtUsr.getText().equalsIgnoreCase("admin")
&& txtPasswd.getText().equalsIgnoreCase("admin")) {
lblError.setVisible(false);
Window.alert("Hola, Bienvenido a mi
aplicaci\u00F3n!");
} else {
lblError.setVisible(true);
}
}
|
Una vez haya terminado los anteriores
pasos, el código completo de la clase LoginComposite
se verá así:
package
com.gwt.tests.client;
import
com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import
com.google.gwt.user.client.ui.VerticalPanel;
import
com.google.gwt.user.client.ui.HorizontalPanel;
import
com.google.gwt.user.client.ui.Button;
import
com.google.gwt.user.client.ui.Label;
import
com.google.gwt.user.client.ui.TextBox;
import
com.google.gwt.user.client.ui.PasswordTextBox;
import
com.google.gwt.user.client.Window;
import
com.google.gwt.event.dom.client.ClickHandler;
import
com.google.gwt.event.dom.client.ClickEvent;
public class LoginComposite
extends Composite {
private
DialogBox dbLogin = new DialogBox();
private
VerticalPanel vpContainer = new
VerticalPanel();
private
Label lblError = new Label("Usuario o Contrase\u00F1a inv\u00E1lidos");
private Button
btnEntrar = new
Button("Entrar");
private Button
btnCancel = new
Button("Cancelar");
private Label lblUsr = new Label("Usuario:");
private Label lblPasswd = new Label("Contrase" + "\u00F1" + "a:");
private
TextBox txtUsr = new TextBox();
private
PasswordTextBox txtPasswd = new PasswordTextBox();
private
HorizontalPanel hpButtons = new
HorizontalPanel();
public
LoginComposite() {
dbLogin.setText("Entrada
al Sistema");
initWidget(dbLogin);
vpContainer.setSpacing(4);
vpContainer.add(lblError);
lblError.setVisible(false);
vpContainer.add(lblUsr);
vpContainer.add(txtUsr);
vpContainer.add(lblPasswd);
vpContainer.add(txtPasswd);
hpButtons.setSpacing(12);
hpButtons.add(btnEntrar);
hpButtons.add(btnCancel);
vpContainer.add(hpButtons);
//Manejadores de click
btnEntrar.addClickHandler(new
ClickHandler() {
@Override
public void
onClick(ClickEvent event) {
onbtnEntrarClick();
}
});
dbLogin.setWidget(vpContainer);
}
void
onbtnEntrarClick() {
if (txtUsr.getText().equalsIgnoreCase("admin")
&& txtPasswd.getText().equalsIgnoreCase("admin")) {
lblError.setVisible(false);
Window.alert("Hola, Bienvenido a mi
aplicaci\u00F3n!");
} else {
lblError.setVisible(true);
}
}
}
|
Modifique la página HTML
El proyecto se creó activando la opción que
genera código de ejemplo. Por esta razón, el código de la clase Entry Point se remplazó
por uno propio. Del mismo modo la página Login.html debe ser modificada.
En primer lugar remplace el título:
<title>Web
Application Starter Project</title>
|
Por:
<title>Login
Composite</title>
|
Borre
la línea:
<h1>Web
Application Starter Project</h1>
|
Y borre también las etiquetas que crean la
siguiente tabla:
<table align="center">
<tr>
<td colspan="2"
style="font-weight:bold;">Please enter your
name:</td>
</tr>
<tr>
<td id="nameFieldContainer"></td>
<td id="sendButtonContainer"></td>
</tr>
<tr>
<td colspan="2"
style="color:red;" id="errorLabelContainer"></td>
</tr>
</table>
|
Al final, el código completo de la página Login.html lucirá así:
<!doctype html>
<!-- The
DOCTYPE declaration above will set the
-->
<!--
browser's rendering engine into
-->
<!--
"Standards Mode". Replacing this declaration -->
<!-- with
a "Quirks Mode" doctype is not supported. -->
<html>
<head>
<meta http-equiv="content-type"
content="text/html; charset=UTF-8">
<!--
-->
<!-- Consider inlining CSS
to reduce the number of requested files -->
<!--
-->
<link type="text/css"
rel="stylesheet" href="Login.css">
<!--
-->
<!-- Any title is fine -->
<!--
-->
<title>Login
Composite</title>
<!--
-->
<!-- This script loads your
compiled module. -->
<!-- If you add any GWT meta
tags, they must -->
<!-- be added before this
line. -->
<!--
-->
<script type="text/javascript"
language="javascript" src="login/login.nocache.js"></script>
</head>
<!--
-->
<!-- The body can have arbitrary html,
or -->
<!-- you can leave the body empty
if you want -->
<!-- to create a completely
dynamic UI. -->
<!--
-->
<body>
<!-- OPTIONAL: include this if
you want history support -->
<iframe src="javascript:''"
id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<!-- RECOMMENDED if your web app
will not function without JavaScript enabled -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
Your web browser must have JavaScript
enabled
in order for this application to
display correctly.
</div>
</noscript>
</body>
</html>
|
Es tiempo de probar la aplicación.
Pruebe la aplicación
Abra la clase Login.java y desde el menú principal seleccione:
Run à Run As à Web Application
Copie el URL que aparece en el tab Development Mode:
Abra una página en el navegador de
internet de su preferencia y pegue el URL. Presione enter. Si no hay errores de
codificación en la aplicación la siguiente página se desplegará en el navegador
de Internet:
En el campo usuario entre el nombre cathy y como contraseña la palabra cathy123. Click en el botón Entrar. El
label lblError se hará visible en la
parte superior de la interface mostrando el mensaje de error:
Ahora pruebe de nuevo la aplicación entrando los valores admin, admin en cada campo. La respuesta será la ventana de bienvenida a
la aplicación:
En una aplicación ya terminada, en lugar de la ventana de bienvenida, tendría que aparecer la página principal de su aplicación.
Como podrá observar, la interface ha tomado los estilos definidos por defecto en el archivo descriptor Login.gwt.xml. Pero con seguridad usted querrá crear y aplicar sus propios estilos a la interface para darle un toque más personalizado. Por ejemplo, El título de de la interface podría ser más grande, con otro color. Por otra parte, el mensaje de error podría aparecer enmarcado, con letras de color rojo y con un fondo amarillo.
En la siguiente publicación se describirá
el procedimiento para aplicar estilos a los widgets, panels y composites
utilizados en su aplicación.