jueves, 18 de septiembre de 2008

Fechas y Calendarios en Java

Centro mi objetivo en construir un widget que nos sirva de calendario para mostrar y elegir fechas con JavaFX. El funcionamiento del calendario es el usual. Se muestran los días del mes en que se está, los días de la semana, el mes y el año seleccionado y la posibilidad de navegar por distintos meses y años con dos botones. Cuando se navega se debe obtener un nuevo calendario mensual actualizado. El día de hoy debe estar marcado. El widget devuelve el día seleccionado, por defecto el día de hoy.

JavaFX no tiene ninguna utilidad propia para el manejo de fechas y calendarios. He visto algunos calendarios de ejemplo realizados en swing más o menos correctos. Sabía que la forma en la que se utilizan fechas en Java es con una clase que guarda milisegundos desde el año 1970. Buscando más referencias encuentro java.util.Date que ya no está en uso y las actuales java.util.Calendar y java.util.GregorianCalendar.

Mientras que con Date se necesita formatear la fecha obtenida con simpleDateFormat o DateFormat, Calendar tiene métodos propios para gestionar el formateo de strings con sus fechas/tiempos.

En concreto me interesan dos métodos de la clase Calendar, .set() que establece la hora y .getTime() que formatea la salida a un string. Con estas dos ya podemos construirnos nuestro po-gramita.

jueves, 11 de septiembre de 2008

Arrastrar nodos. Objeto evento de ratón.

Me dispongo a construir una barra lateral que me permita recorrer el contenido que se muestre en una zona delimitada. Mi idea es construir una barra que permita arrastrar y soltar un botón para que se mueva solidariamente al contenido. De la página del señor SilveiraNeto, veo cómo arrastrar y soltar un nodo. Ya había probado este efecto antes y había reparado en que esa era la primera vez que veía que una función tomaba como parámetro un objeto del tipo MouseEvent. Ahora que ya ví que Flex gestiona un objeto evento para estos menesteres también, comprendo que es con éste con el que se quieren gestionar de forma sencilla los gestos del ratón o teclado.

El detalle interesante es que una vez creado y asociado el objeto de evento de ratón, podemos adquirir de él información como e.getDragY que nos dice la distancia en el eje Y que ha recorrido el puntero durante el arrastre. Edificante.

Con este detalle sólo me queda hacer referencia al valor del desplazamiento producido por el arrastre del nodo y tomarlo como valor de movimiento para el nodo visible, bien sea haciendo un bind y o un bind translateY.

En otro orden de cosas, para detectar un doble click utilizo una propiedad de evento de ratón heredada de swing, .getClickCount() que me dice el número de clicks que se han realizado. Me gustaría saber cuál es el intervalo de tiempo que utiliza para el conteo de clicks de ratón. Compruebo que funciona como espero cuando hago unas líneas tal que así:



onMouseClicked(e:MouseEvent){
if (e.getClickCount() == 2) then { ... }
}




HBox/VBox en CustomNode

Esto se contendría en la entrada de Anodino, no sé si ya lo habré comentado. Al crear un CustomNode que contiene un HBox, construido por ejemplo con un for, en el visor de NetBeans se puede ver como se amontonan los nodos que deben ir distribuidos. SIN EMBARGO, cuando se hace referencia a este nodo desde otro se comprueba que se muestra correctamente. Brujería. 

¿Porqué?.

For/If. Sentencia y Expresión.

Ya venía utilizando la sentencia for para crear un array de nodos en JavaFX, había leído que for había substituido a foreach para recorrer los elementos de una secuencia, de esta forma:

content: for (item in lista where item.id.length>4) { item }

Esto devuelve una secuencia de objetos cuyo id tenga mayor longitud de cuatro para la propiedad content de algún nodo. (Utiliza un modo parecido a sql con las secuencias). También se puede utilizar for como una sentencia como se hace de manera habitual en todos los lenguajes de programación:

for (n in [1..5]) {java.lang.System.out.println(n);}

En algunas situaciones me he preguntado como utilizar una expresión del tipo x?y:z que se utiliza en otros lenguajes (unos cuantos) para asignar un valor aplicando una condición tipo if(x) {y} else {z}. Bien, buscando en Google se me dirige al señor James Weaver  y lo deja bien clarito. Tanto for como if se pueden utilizar en modo de sentencia o en modo expresión. Quiere esto decir que tienen la utilidad esperada de un lenguaje de programación y además nos sirven para asignar valores. En el caso de if podríamos utilizar:

var x:Number = if (a<=3) 34 else 23;

Que asigna el valor 34 a x siempre que a sea menor o igual que 3 y 23 en caso contrario. Siempre hay que poner entre paréntesis las condiciones del if. Esta simplificación del lenguaje seguramente tendrá detractores entre la gente sesuda pero sin duda es una simplificación sin pérdida de funcionalidad para el programador de RIA que requiere un lenguaje de script ágil. Cuando se utiliza if para asignación no se deben utilizar llaves.


lunes, 8 de septiembre de 2008

Diálogos y bloqueos de eventos

Estaba en implementar el modo en el que al abrir un nuevo frame se inhabilitara el frame que lo llama. Visto que no tenían lugar los métodos .setEnabled y otros propios de swing, me dispongo a implementar un modo satisfactorio. En JavaFX existe aunque no lo tenga en las herramientas del IDE NetBeans, una clase del paquete application que se llama Dialog y que es similar a la homónima en Swing. Sin embargo no contempla modos en los que se puede abrir la nueva ventana y siempre que se abre lo hace sin deshabilitar el frame que lo llama. Supongo que ésto tendrá que ver con que para los nodos jfx no existe la propiedad enabled, al menos no es accesible fácilmente. 

Recordando cómo funcionaba la política en Flex de paso de eventos entre jerarquía de nodos y habiendo encontrado una propiedad que en principio parecía escondida, llamada blockMouse aplicable para todos los nodos, he podido hacer que un evento sobre un nodo visible no permita recibirlo al nodo que se situa por debajo. De este modo puedo cubrir mi frame padre con un manto semitrasparente con la propiedad de bloqueo puesta a true y así impedir que se pueda interactuar con el padre mientras el hijo esté funcionando.

jueves, 4 de septiembre de 2008

Segunda Entrega JavaFX. Clases y Objetos

Esta segunda entrega es cortita en extensión, habla del uso de clases y objetos en JavaFX y cómo se relacionan con clases Java. También menciona la particularidad de que las clases JavaFX pueden extender múltiples clases. Quiero dejar en este post también el enlace hacia lo que hasta ahora es la mejor compilación de documentación relativa al compilador y SDK preview de JavaFX. Se trata de un CHM que contiene tanto el API como la referencia más aceptada al lenguaje, así como algunos ejemplos y la documentación relativa al compilador de JavaFX. Se puede encontrar en esta página de GeekyCoder.


Importando Clases

Las sentencias import se comportan de igual modo que en Java. La sintaxis es:

import PackageName.ClassName;
import PackageName.*;

Si las sentencias import están presentes, deben aparecer antes que cualquier otro código de aplicación (no de la definición package). El lenguaje JavaFX define su propio espacio de trabajo para su librería de clases propias (packages javafx.*), pero las clases de Java también se pueden importar:

import javafx.application.*;
import java.lang.System;
 
...
 

Definiendo Clases

La sintaxis para especificar una clase es la palabra class seguida del nombre de la clase, opcionalmente la palabra clave extends, y una lista separada por comas de las clases base, una llave abierta, una lista de atributos y funciones que acaban todos en punto y coma y una llave cerrada.

JavaFX soporta herencia múltiple y define alguna terminología y reglas nuevas:

  • Una clase plana es cualquier clase que extiende directa o indirectamente una clase escrita en Java.
  • Una clase de componente es cualquier clase que no es una clase plana.
  • Por defecto, las clases escritas en JavaFX son clases de componente.
  • Las clases pueden extender, como máximo una clase plana. Si extiende una clase plana, será una clase plana. Una clase puede también extender cualquier número de clases componente o interfaces Java.

Nota: Una clase plana es realmente traducida a una clase Java, mientras que una clase componente es traducida a una clase Java y a una interfaz Java.

Es posible declarar una clase como pública, lo que significa que se puede acceder desde cualquier script. De otro modo, la clase sólo será accesible desde el contenedor del script (esta es la opción por defecto).

JavaFX no soporta constructores, debes usar literales de objeto en su lugar. Para imitar el comportamiento de un constructor, define la función estática que devuelva un nuevo objeto e invoca esa función.

Definiendo Objetos

Como se describió en la anterior entrada, la forma preferible de instanciar una clase es con un objeto literal. Esta forma de emplazamiento de objeto usa una sintaxis declarativa que consiste en el nombre de la clase seguida de una lista delimitada con llaves de inicializadores de atributos. Cada inicializador consiste en el nombre del atributo seguido de una coma, seguida de una expresión que define su valor. Sin embargo, es también posible usar la palabra new cuando se crea un objeto:

import java.io.File;
 
var tmpPath = "/home/users/docs/tmp.txt"
var myFile = new File("tmp.txt");

(http://javafx.com/releases/preview1/docs/reference/JavaFX-Language.html)

miércoles, 3 de septiembre de 2008

Clientes WebService con NetBeans 6.1

Leyendo el libro de descarga gratuita Mastering EJB 3.0 encuentro una referencia a la página xmethods.net donde se puede encontrar una lista de servicios web de acceso gratuito y público. Posee una gran cantidad de servicios postales/geográficos enfocados en Norteamérica. Para probar como funciona un cliente de WebService es una fuente muy buena de referencia. Ofrece una descripción básica del servicio y la referencia al fichero wsdl (wiss-dull) con el que se construye el cliente. También he encontrado el sitio: webservicex.net.

La construcción del cliente con NetBeans 6.1 no  puede ser más sencilla. Si el tipo de proyecto lo soporta (de momento sólo he encontrado que el tipo de proyecto JavaFX no lo hace),  añadimos un WebClient al proyecto, le indicamos la URL del fichero wsdl y creará todos los artefactos locales para la comunicación con el servicio. 

Una vez construidos los artefactos no tenemos más que arrastrar el método a la clase desde donde queramos hacer uso del método del servicio web para que cree un prototipo de acceso a este port. La gran mayoría de los ports que se ofrecen son tipos de datos compuestos, así que hay que añadir algún método para poder gestionar la información correctamente (.getCleanText por ejemplo para el servicio de Profanity).

La información sobre cómo está diseñado el servicio web así como todos los métodos expuestos se puede ver en la especificación del wsdl. Bajo el apartado Web Services References se tienen todos los servicios web añadidos al proyecto. Podemos abrir cada wsdl en esta vista. Una vez abierto tenemos tres tipos de vista, source, wsdl y partner, donde se detallan los ports del servicio.

lunes, 1 de septiembre de 2008

Funcionamiento de menú con JavaFX.

No seré yo el que ponga un pero al trabajo que realiza el personal en los códigos con JavaFX a los que se tienen acceso. Y más teniendo en cuenta que he sido testigo de la evolución y por tanto de las dificultades técnicas que ha sufrido esta tecnología. Pongo aquí cómo implemento un funcionamiento de menús sobre un par de arrays de nodos construidos como instancias de un nodo genérico de menú, algo más complejo que un CustomNode. (A ver cuándo puedo poner el visualizador de código tan útil en Blogger).

La clase nodo contiene un rectángulo con relleno de color aleatorio, un texto y además una variable timeline para el efecto de "aparecer".


/*
* una.fx
*
* Created on 22-sep-2008, 10:58:54
*/

package com.megestiono.widgets;

/**
* @author Alvaro
*/

import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.geometry.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.Font;
import javafx.scene.FontStyle;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.animation.Interpolator;

public class Nodo extends CustomNode {

public attribute texto:String="N/A";
attribute opa:Number=0;
public attribute t = Timeline {
repeatCount: 1
keyFrames : [
KeyFrame {
time : 1s
values : [
opa => 1 tween Interpolator.LINEAR
]
}
]
}

public function aparece():Void{
t.start();
}

public function create(): Node {


var rnd = new java.util.Random();

return Group {
opacity: bind opa
content: [Rectangle {
x: 0, y: 0
width: 200, height: 200
fill: Color.rgb(rnd.nextInt(255),rnd.nextInt(255),rnd.nextInt(255))

},Text {
font: Font {
size: 20
style: FontStyle.PLAIN
}
x: 10, y: 40
content: texto
}]
};
}
}

Nodo {texto:"UnTexto"}

//La ventana desde donde los gestiono con un menú rudimentario construido con rectángulos.

import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.layout.*;
import javafx.scene.geometry.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.*;
import javafx.application.WindowStyle;

var secuencia1:String[] = ["Uno","Dos","Tres"];
var secuencia2:String[] = ["Cuatro","Cinco","Seis"];
var arraynodos1:Nodo[] = [Nodo{texto:secuencia1[0]},Nodo{texto:secuencia1[1]},Nodo{texto:secuencia1[2]}];
var arraynodos2:Nodo[] = [Nodo{texto:secuencia2[0]},Nodo{texto:secuencia2[1]},Nodo{texto:secuencia2[2]}];
var nodos:Nodo[] = arraynodos1;

Frame {
title: "Menus"
width: 300
height: 300
closeAction: function() {
java.lang.System.exit( 0 );
}
visible: true
windowStyle: WindowStyle.TRANSPARENT

stage: Stage {
content: [VBox{
spacing:10
content:[HBox{
spacing:10
content:[Rectangle {
x: 0, y: 0
width: 50, height: 30
fill: Color.FORESTGREEN
onMousePressed: function (e){
for (nodo in nodos){
nodo.visible = false
}
nodos

[0].visible = true;
nodos
[0].t.start();
}
}, Rectangle {
x: 0, y: 0
width: 50, height: 30
fill: Color.FORESTGREEN
onMousePressed: function (e){
for (nodo in nodos){
nodo.visible = false
}
nodos

[1].visible = true;
nodos
[1].t.start();
}
}, Rectangle {
x: 0, y: 0
width: 50, height: 30
fill: Color.FORESTGREEN
onMousePressed: function (e){
for (nodo in nodos){
nodo.visible = false
}
nodos

[2].visible = true;
nodos
[2].t.start();
}
}, Rectangle {
x: 0, y: 0
width: 50, height: 30
fill: Color.RED
onMousePressed: function (e){

if (nodos == arraynodos2){
nodos = arraynodos1;
nodos

[0].visible = true;
nodos
[0].t.start();
} else {
nodos = arraynodos2;
nodos

[0].visible = true;
nodos
[0].t.start();
}
}
}]
}
,
Group{
content:bind nodos
},
Rectangle {
x: 270, y: 20
width: 50, height: 50
fill: Color.BLACK
onMouseClicked: function (e){
java.lang.System.exit( 0 );
}
}
]
}
]
}
}