Aquí hay una pequeña demo del componente TextHTML en acción. Incluido el uso de LINKS en los textos de una aplicación JavaFX. También pueden ver AQUI una demo Java Web Start.
La biblioteca memeFX está AQUI
Mostrando entradas con la etiqueta how to. Mostrar todas las entradas
Mostrando entradas con la etiqueta how to. Mostrar todas las entradas
viernes, 24 de abril de 2009
sábado, 18 de abril de 2009
Como mejorar la partida de Java Web Start
Aquí hay un truco para mejorar la velocidad de partida de las aplicaciones JavaFX Web Start.
Todo lo que necesitas hacer es agregar una linea a tus archivos JNLP.
El elemento update se usa para indicar la preferencia sobre como Java Web Start maneja las actualizaciones. El elemento update puede contener los siguientes atributos opcionales:
Atributo check: El atributo check indica la preferencia sobre cuando el cliente JNLP debe chequear por actualizaciones, y puede tener uno de los siguientes tres valores:
1. "always": Siempre chequea por actualizaciones antes de lanzar la aplicación.
2. "timeout" (default): Chequea por actualizaciones hasta alcanzar un umbral de tiempo (timeout) antes de lanzar la aplicación. Si el chequeo de actualización no se completa antes del timeout, la aplicación es lanzada, y el chequeo de actualización continua por "atras" del proceso de la aplicación.
3. "background": Lanza la aplicación mientras chequea si existen actualizaciones simultaneamente, por "atras".
La opción 3 mejorará el tiempo de partida de las aplicaciones JavaFX Web Start.
Todo lo que necesitas hacer es agregar una linea a tus archivos JNLP.
<update check="background">
El elemento update se usa para indicar la preferencia sobre como Java Web Start maneja las actualizaciones. El elemento update puede contener los siguientes atributos opcionales:
Atributo check: El atributo check indica la preferencia sobre cuando el cliente JNLP debe chequear por actualizaciones, y puede tener uno de los siguientes tres valores:
1. "always": Siempre chequea por actualizaciones antes de lanzar la aplicación.
2. "timeout" (default): Chequea por actualizaciones hasta alcanzar un umbral de tiempo (timeout) antes de lanzar la aplicación. Si el chequeo de actualización no se completa antes del timeout, la aplicación es lanzada, y el chequeo de actualización continua por "atras" del proceso de la aplicación.
3. "background": Lanza la aplicación mientras chequea si existen actualizaciones simultaneamente, por "atras".
La opción 3 mejorará el tiempo de partida de las aplicaciones JavaFX Web Start.
Etiquetas:
como,
configuracion,
how to,
howto,
java web start,
javafx,
jnlp,
jws,
mas,
mejora,
mejorar,
partida,
performace,
rendimiento,
tip,
trick,
truco,
velocidad
jueves, 16 de abril de 2009
Un detalle para obtener una mejor performance
Este detalle (junto con incluir canSkip:true en los timelines asociados a animaciones o transiciones gráficas de algún tipo) puede hacer una gran diferencia en la performance gráfica de JavaFX.
Tengo un CustomNode cuyo contenido son unos nodos almacenados en una secuencia:
Digamos que se necesita reemplazar con frecuencia los nodos en objs y que tengo una función que realiza este trabajo:
Como muestra el video, eso es ineficiente. Al estar enlazado el contenido de objs con el contenido del componente e ir cambiando el contenido de objs, se va gatillando trabajo para refrescar el componente.
Es más eficiente crear una secuencia temporal y traspasar al final todos los nodos a la secuencia asociada al componente:
Habría que probar si esto también es aplicable cuando se realizan cambios a los elementos en la secuencia (y no sólo cuando estos se reemplazan). Es probable que ocurra lo mismo, porque al actualizar uno a uno los elementos, es muy probable que también se vaya gatillando trabajo parcial, mientras si se reemplaza toda la secuencia de una vez, ese trabajo debería ocurrir una sóla vez... ¿?
Tengo un CustomNode cuyo contenido son unos nodos almacenados en una secuencia:
public class myComponent extends CustomNode {
var objs:Node[];
public override function create(): Node {
Group {
content: bind objs
};
};
}
Digamos que se necesita reemplazar con frecuencia los nodos en objs y que tengo una función que realiza este trabajo:
function paint():Void {
delete objs;
for (element in someGroup) {
...
insert newNode into objs;
...
}
}
Como muestra el video, eso es ineficiente. Al estar enlazado el contenido de objs con el contenido del componente e ir cambiando el contenido de objs, se va gatillando trabajo para refrescar el componente.
Es más eficiente crear una secuencia temporal y traspasar al final todos los nodos a la secuencia asociada al componente:
function paint():Void {
var tmpObjs:Node[];
for (element in someGroup) {
...
insert newNode into tmpObjs;
...
}
objs = tmpObjs;
}
Habría que probar si esto también es aplicable cuando se realizan cambios a los elementos en la secuencia (y no sólo cuando estos se reemplazan). Es probable que ocurra lo mismo, porque al actualizar uno a uno los elementos, es muy probable que también se vaya gatillando trabajo parcial, mientras si se reemplaza toda la secuencia de una vez, ese trabajo debería ocurrir una sóla vez... ¿?
Etiquetas:
animacion,
animaciones,
como,
fps,
frames,
grafica,
grafico,
graphic,
how to,
javafx,
per second,
performance,
por segundo,
problema,
rendimiento,
resolver,
solucion,
velocidad
martes, 14 de abril de 2009
Un "truco" para obtener el alto de un Font
Estoy construyendo un componente de texto HTML para mi librería de JavaFX, y me encontré con el siguiente problema.
¿Cómo obtengo el alto de un font?
No confundan el obtener el alto de un TEXT con obtener el alto de un FONT.
Si uno tiene un texto y quiere obtener sus dimensiones, basta con:
El asunto es que si uno pone "....." como contenido del texto, el alto es de apenas unos pixeles.
Probablemente ustedes no se vayan a encontrar con este problema con mucha frecuencia, pero para el componente que estoy creando es decisivo... entonces, aquí va una solución bastante... mmm.... "simple"... pero funciona.
No es el ideal recurrir a este tipo de "trucos", pero me pareció mejor que tener que incluir las APIs de Java para el manejo de Fonts, que si entregan todo tipo de información sobre las fuentes... espero que en próximas versiones de JavaFX la API incluya una alternativa más "elegante", es decir, que entregue directamente las medidas asociadas a las fuentes.
¿Cómo obtengo el alto de un font?
No confundan el obtener el alto de un TEXT con obtener el alto de un FONT.
Si uno tiene un texto y quiere obtener sus dimensiones, basta con:
var myText = Text {
content: "Mi texto"
...
Font {
size:24
...
}
}
var ancho = myText.boundsInLocal.width;
var alto = myText.boundsInLocal.height;
El asunto es que si uno pone "....." como contenido del texto, el alto es de apenas unos pixeles.
Probablemente ustedes no se vayan a encontrar con este problema con mucha frecuencia, pero para el componente que estoy creando es decisivo... entonces, aquí va una solución bastante... mmm.... "simple"... pero funciona.
var myFont = Font {
size=24
...
}
var tmpText = Text {
content: "Aj"
font: myFont
}
var myText = Text {
content: ".........."
font: myFont
...
}
var altoFont = tmpText.boundsInLocal.height;
var anchoTexto = myText.boundsInLocal.width;
No es el ideal recurrir a este tipo de "trucos", pero me pareció mejor que tener que incluir las APIs de Java para el manejo de Fonts, que si entregan todo tipo de información sobre las fuentes... espero que en próximas versiones de JavaFX la API incluya una alternativa más "elegante", es decir, que entregue directamente las medidas asociadas a las fuentes.
martes, 7 de abril de 2009
Cómo compilar clases Java en JavaFX, que contienen delete o insert
Como saben, los términos "insert" y "delete" están reservados por JavaFX para insertar y borrar elementos en las secuencias. Esto puede producir un inconveniente cuando uno incluye dentro de un proyecto JavaFX una clase Java donde se utiliza llamadas a métodos denominados "insert" o "delete".
Esto ocurre, por ejemplo, si se utiliza "delete" para borrar un archivo. El compilador muestra el siguiente error:
La solución es simple, sólo hay que incluir el término entre << ... >>
Lo interesante es que esta sintaxis permite utilizar cualquier texto como un identificador:
La salida del script es:
Noten que el identificador "english variable" incluso incluye un salto de línea (CR/LF).
Esto ocurre, por ejemplo, si se utiliza "delete" para borrar un archivo. El compilador muestra el siguiente error:
Sorry, I was trying to understand an expression
but I got confused when I saw 'delete' which is a keyword.
new java.io.File("temp").delete();
^
La solución es simple, sólo hay que incluir el término entre << ... >>
new java.io.File("temp").<<delete>>();
Lo interesante es que esta sintaxis permite utilizar cualquier texto como un identificador:
def <<english
variable>> = "cualquier simbolo sirve como identificador";
def <<русская переменная>> = <<english
variable>>;
println(<<русская переменная>>);
La salida del script es:
cualquier simbolo sirve como identificador
Noten que el identificador "english variable" incluso incluye un salto de línea (CR/LF).
lunes, 30 de marzo de 2009
Usando FX.deferAction()
Al desarrollar el componente de acordeón de imágenes, se producía un "pestañeo extraño" al momento de solicitar a los elementos que actualizaran su posición. Le dí algunas vueltas al código hasta comprobar que: no era resultado de algo producido por un error en el código, sino por algo relacionado con el refresco de la pantalla (o algo por el estilo ¿?).
Así que recurrí un poco al instinto y a la memoria... y... recorde haber leido algo sobre deferAction. Así que incluí el código que solicita el ajuste de posición, dentro de la llamada de esta función y el problema terminó:
El siguiente código:
Pasó a ser este:
Sólo anidé el for dentro de FX.deferAction(function():Void { ... });
Sí descargan la biblioteca memeFX y quitan el FX.deferAction en ImagesAccordion.fx, quizás puedan ver el problema que me llevó a esta solución (no sé si el problema sea reproducible en otras máquinas). Tampoco me quedó claro que produce el problema ni por qué lo resuelve deferAction, voy a buscar información al respecto. Por ahora, sólo comparto el "tip" en caso que alguien tenga un problema similar.
http://code.google.com/p/memefx/
Así que recurrí un poco al instinto y a la memoria... y... recorde haber leido algo sobre deferAction. Así que incluí el código que solicita el ajuste de posición, dentro de la llamada de esta función y el problema terminó:
El siguiente código:
public-read var active: Integer on replace {
for (item in pictures) {
item.adjust();
};
};
Pasó a ser este:
public-read var active: Integer on replace {
FX.deferAction(function():Void {
for (item in pictures) {
item.adjust();
};
});
};
Sólo anidé el for dentro de FX.deferAction(function():Void { ... });
Sí descargan la biblioteca memeFX y quitan el FX.deferAction en ImagesAccordion.fx, quizás puedan ver el problema que me llevó a esta solución (no sé si el problema sea reproducible en otras máquinas). Tampoco me quedó claro que produce el problema ni por qué lo resuelve deferAction, voy a buscar información al respecto. Por ahora, sólo comparto el "tip" en caso que alguien tenga un problema similar.
http://code.google.com/p/memefx/
jueves, 19 de marzo de 2009
Timelines de duración variable y el at(..)
Uno de los inconvenientes de trabajar en un ambientea tan nuevo como el JavaFX script, es que no abundan las fuentes de información.
Una de las cosas que no tenía claras (seguramente porque no investigué lo suficiente, o no miré mejor los ejemplos), era cómo se hace que un timeline tenga duración variable.
Bueno, ahora descubrí que diablos es el at(..) en los timeline y cómo se hace para que la duración sea variable.
Primera cosa, cuando uno usa at(), en realidad está usando una "abreviatura" en la manera de definir un timeline. La idea es que sólo sea una opción breve para escribir el código, pero por lo mismo, no es la forma más "potente".
Tomemos este ejemplo:
En él, definimos una variable x, que al segundo 0s -de iniciado el timeline- asume un valor de 0.0, luego, x asumirá gradualmente los valores entre 0.0 y 100.0, alcanzando el máximo valor (100.0) en 3s segundos.
El asunto que me faltaba comprender era... ¿como cambiaba el tiempo en at(3s)?, porque no acepta ni variables ni bind dentro de los paréntesis.
Pues resulta que con at(..) no lo vas a conseguir (por lo menos eso entiendo en este momento), la forma correcta y potente de usar las lineas de tiempo es esta:
De hecho, esta forma de definir un timeline permite anidar timelines o agregar un action: en cada KeyFrame, de manera que una acción sea ejecutada cada vez que una etapa de la animación o transición se completa.
Una de las cosas que no tenía claras (seguramente porque no investigué lo suficiente, o no miré mejor los ejemplos), era cómo se hace que un timeline tenga duración variable.
Bueno, ahora descubrí que diablos es el at(..) en los timeline y cómo se hace para que la duración sea variable.
Primera cosa, cuando uno usa at(), en realidad está usando una "abreviatura" en la manera de definir un timeline. La idea es que sólo sea una opción breve para escribir el código, pero por lo mismo, no es la forma más "potente".
Tomemos este ejemplo:
var x:Number;
Timeline {
repeatCount: 1
keyFrames: [
at (0s) { x => 0.0}
at (3s) { x => 100.0}
]
}.play();
En él, definimos una variable x, que al segundo 0s -de iniciado el timeline- asume un valor de 0.0, luego, x asumirá gradualmente los valores entre 0.0 y 100.0, alcanzando el máximo valor (100.0) en 3s segundos.
El asunto que me faltaba comprender era... ¿como cambiaba el tiempo en at(3s)?, porque no acepta ni variables ni bind dentro de los paréntesis.
Pues resulta que con at(..) no lo vas a conseguir (por lo menos eso entiendo en este momento), la forma correcta y potente de usar las lineas de tiempo es esta:
var dur=3s;
var x:Number;
Timeline {
repeatCount: 1
keyFrames : [
KeyFrame {
time : 0s
values: [x=>0]
},
KeyFrame {
time: bind dur
values: [x=>100]
}
]
}.play();
De hecho, esta forma de definir un timeline permite anidar timelines o agregar un action: en cada KeyFrame, de manera que una acción sea ejecutada cada vez que una etapa de la animación o transición se completa.
var dur=3s;
var dur2=5s;
var x:Number;
Timeline {
repeatCount: 1
keyFrames : [
KeyFrame {
time : 0s
values: [x=>0]
},
KeyFrame {
time: bind dur
canSkip: true
values: [x=>50]
action: function() {
println("completo etapa 1");
};
},
KeyFrame {
time: bind dur2
canSkip: true
values: [x=>200]
action: function() {
println("completo etapa 2");
};
}
]
}.play();
lunes, 23 de febrero de 2009
Cómo crear una aplicación móvil con JavaFX y Netbeans 6.5
Este video muestra lo sencillo que es crear y probar aplicaciones móviles desarrolladas en JavaFX script con Netbeans 6.5.
Básicamente, sólo tienes que crear una aplicación y luego indicar que la vas a ejecutar con el emulador.
Básicamente, sólo tienes que crear una aplicación y luego indicar que la vas a ejecutar con el emulador.
lunes, 19 de enero de 2009
JavaFX: Bind with inverse. Ajusta tamaño de círculo a tamaño de la ventana
La instrucción BIND es una de las cosas interesantes de JavaFX Script. Esta permite declarar la DEPENDENCIA entre una variable y otros elementos del programa. Por ejemplo, si al declarar un circulo, se define que el color de este dependerá de una variable X, al cambiar el color asignado a X se produce la actualización automática del círculo en pantalla, sin necesidad de ordenar al programa que repinte.
Esto significa que cuando se cambie el valor de 'b' en algún lugar del programa, se asignará el nuevo valor de 'b' a 'a', pero también quiere decir que cuando se modifique el valor de 'a' también se traspasará el valor de 'a' a 'b'.
¿Qué utilidad puede tener esta instrucción? ... pues, por ejemplo, se puede usar para ajustar el tamaño de un círculo al tamaño de la ventana que lo contiene... o para conocer las medidas de la ventana y cuando se cambia su tamaño.
En el siguiente ejemplo, se usa 'bind with inverse' para asignar el tamaño inicial de la ventana (300x300 pixeles)... pero luego, cuando se ajusta manualmente el tamaño de la ventana, la misma instrucción 'bind with inverse' pasa de regreso el tamaño actualizado de la ventana a las variables 'h' y 'w' que originalmente definieron su tamaño (h=height=alto y w=width=ancho).
Ahora bien, la instrucción BIND puede ir acompañada de la instrucción WITH INVERSE, por ejemplo:Circle {
radius:20
fill:bind colorcirculo
}
var a=bind b with inverse;
Esto significa que cuando se cambie el valor de 'b' en algún lugar del programa, se asignará el nuevo valor de 'b' a 'a', pero también quiere decir que cuando se modifique el valor de 'a' también se traspasará el valor de 'a' a 'b'.
¿Qué utilidad puede tener esta instrucción? ... pues, por ejemplo, se puede usar para ajustar el tamaño de un círculo al tamaño de la ventana que lo contiene... o para conocer las medidas de la ventana y cuando se cambia su tamaño.
En el siguiente ejemplo, se usa 'bind with inverse' para asignar el tamaño inicial de la ventana (300x300 pixeles)... pero luego, cuando se ajusta manualmente el tamaño de la ventana, la misma instrucción 'bind with inverse' pasa de regreso el tamaño actualizado de la ventana a las variables 'h' y 'w' que originalmente definieron su tamaño (h=height=alto y w=width=ancho).
package experimentsize;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.shape.*;
import javafx.scene.paint.*;
import java.lang.System;
// tamaño inicial de la ventana
var w=300.0 on replace {
println("ajusto ancho de la ventana a {w}");
};
var h=300.0 on replace {
println("ajusto alto de la ventana a {h}");
};
// borde horizontal de la ventana
var bh=16.0;
// borde vertical de la ventana (incluye titulo)
var bv=36.0;
Stage {
title: "Ajusta al tamaño de ventana"
width: bind w with inverse
height: bind h with inverse
scene: Scene {
content: {
Circle {
translateX: bind (w - bh) / 2
translateY: bind (h - bv) / 2
radius:bind
if (w < h) then (w - 50) / 2 else ( h - 80) / 2
fill:Color.RED
}
}
}
}
domingo, 18 de enero de 2009
JavaFX: Objetos arrastrables con "efectos"
Este ejemplo incluye 3 clases:
1) Una clase (SpringInterpolator) que define un nuevo tipo de "interpolador" que genera un movimiento "elástico" (de resorte)
2) Una clase (DraggableObj) que permite que los elementos asignados sean "arrastrables" (drag and drop).
3) La clase principal (Main) que implementa la aplicación y que agrega 4 elementos para arrastrar por la ventana.
archivo SpringInterpolator.fx
archivo DraggableObj.fx
archivo Main.fx
1) Una clase (SpringInterpolator) que define un nuevo tipo de "interpolador" que genera un movimiento "elástico" (de resorte)
2) Una clase (DraggableObj) que permite que los elementos asignados sean "arrastrables" (drag and drop).
3) La clase principal (Main) que implementa la aplicación y que agrega 4 elementos para arrastrar por la ventana.
archivo SpringInterpolator.fx
package DraggableClass;
import javafx.animation.SimpleInterpolator;
import java.lang.Math;
public class SpringInterpolator extends SimpleInterpolator {
// amplitud de la onda
// controla que tan lejos puede llegar el objeto desde su punto final
public-init var amplitude:Number = 1.0;
// determina el peso del objeto
// hace el movimiento de la onda mas grande y llega mas lejos
public-init var mass:Number = 0.058;
// rigidez del movimiento de la onda o resolte
// hace la onda mas corta o ceñida
public-init var stiffness:Number = 12.0;
// hace la onda de fase, de modo que el objeto
// no acabe en el lugar de descanso.
// esta variable generalmente no cambia
public-init var phase:Number = 0.0;
// si actua como un resorte normal o rebota
public-init var bounce:Boolean = false;
// usado para calculos internos
var pulsation:Number;
init {
this.pulsation = Math.sqrt(stiffness / mass);
}
// ecuacion del resorte
override public function curve(t: Number) : Number {
var t2 = -Math .cos(pulsation * t + phase + Math.PI) * (1 - t) * amplitude ;
// usa el valor absoluto de la distancia si rebota
if(bounce) {
return 1 - Math.abs(t2);
} else {
return 1 - t2;
}
}
}
archivo DraggableObj.fx
package DraggableClass;
import javafx.scene.Group;
import javafx.scene.CustomNode;
import javafx.scene.Node;
import javafx.scene.shape.*;
import javafx.scene.paint.*;
import javafx.animation.Timeline;
import javafx.animation.Timeline.*;
import javafx.animation.Interpolator;
import javafx.animation.Interpolator.*;
public class DraggableObj extends CustomNode {
public var content: Node[];
var endX = 0.0;
var endY = 0.0;
var startX = 0.0;
var startY = 0.0;
var op:Number=100;
var scale:Number=100;
// produce un parpadeo rapido al
// al soltar los objetos arrastrados
var t = Timeline {
repeatCount: 3
keyFrames: [
at (0s) { op => 100.0 },
at (0.05s) { op => 0.0 }
]
}
// instancia el interpolador de resorte
def spring = SpringInterpolator { bounce: true};
// escala objetos con efecto de resorte
var s = Timeline {
keyFrames: [
at (1.5s) { scale => 100.0 tween spring},
]
};
// escala objectos de tamano original
// a tamano 50% mas grande al hacer
// click sobre ellos
var i = Timeline {
keyFrames: [
at (0s) { scale => 100.0 },
at (0.3s) { scale => 150.0},
]
};
override function create() : Node {
// produce una variacion constante en la
// opacidad de los elementos arrastrables
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) { op => 70.0 },
at (0.3s) { op => 100.0 tween Interpolator.EASEBOTH}
]
}.play();
Group {
translateX: bind endX
translateY: bind endY
content: bind content
opacity: bind op/100.0
scaleX: bind scale/100.0
scaleY: bind scale/100.0
onMouseDragged: function(e) {
endX = e.dragX-startX;
endY = e.dragY-startY;
}
onMousePressed: function(e) {
startX = e.dragX-endX;
startY = e.dragY-endY;
s.stop();
i.playFromStart();
}
onMouseReleased: function(e) {
t.playFromStart();
s.time=0s;
s.play();
}
};
}
}
archivo Main.fx
package DraggableClass;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.input.MouseEvent;
var newCircle=DraggableObj{
translateX:700
translateY:200
content: Circle {
radius: 100
fill: Color.RED
opacity:0.75
}
};
// este objeto incluye un ARRAY (arreglo) de figuras
var newCircle2=DraggableObj{
translateX:200
translateY:200
content: [
Circle {
opacity:0.5
fill:Color.BLUE
radius:70
},
Rectangle {
translateX:-50
translateY:-50
width:100
height:100
rotate:45
fill:Color.GREEN
opacity:0.5
}
]
};
var newImage=DraggableObj{
translateX:200
translateY:500
content: ImageView {
opacity:0.8
preserveRatio:true
fitWidth:200
image: Image {
// url: "file:/C:/Users/myuser/Pictures/2008a.png"
url: "http://www.google.com/intl/en_ALL/images/logo.gif"
}
}
}
var newRect=DraggableObj{
translateX:700
translateY:500
content: Rectangle {
width:100
height:100
rotate:45
fill:Color.PURPLE
opacity:0.5
}
};
Stage {
title: "Application title"
width: 1200
height: 750
scene: Scene {
// Incluye un ARRAY de objetos
content: [
newCircle,
newCircle2,
newImage,
newRect
]
}
}
Etiquetas:
arrastrable,
arrastrar,
boucing,
bounce,
codigo fuente,
como,
contenedor,
drag and drop,
draggable,
efecto,
efectos,
ejemplo,
elastico,
example,
how to,
javafx,
objeto,
resorte,
source code,
spring
Suscribirse a:
Entradas (Atom)