JavaFX è l'insieme di strumenti per la creazione di Rich Internet Application messa in campo dalla piattaforma Java. Abbiamo già fatto la conoscenza di questa tecnologia in un articolo precedente, quindi possiamo tralasciare i convenevoli e concentrarci sulle funzionalità più specifiche.
In particolare approfondiremoo, con esempi pratici, alcune funzioni multimediali. Utilizzeremo anche del sistema di Applet Extension che permette il drag'n'drop dell'applicazione dal browser al desktop: la vera innovazione di questo linguaggio.
Vedremo come:
- realizzare effetti grafici
- utilizzare immagini e componenti multimediali
- utilizzare le animazioni
- realizzare una semplice applicazione da trascinare sul desktop
Ci soffermeremo sui diversi aspetti in un percorso in cui costruiremo, esempio dopo esempio, una piccola soluzione.
Il contenitore
Per prima cosa dobbiamo costruire il contenitore che ospiterà i diversi oggetti che stiamo per osservare. Definiamo così il nostro Stage
di base:
var stage:Stage = Stage {
title : "MyApp"
scene: Scene { width: 400 height: 200
content: [
// inseriremo qui i diversi oggetti
]
}
}
Vedremo che ci sarà utile definire un'istanza di Stage
: ci permetterà di sfruttare alcune proprietà del contenitore.
Effetti grafici
Iniziamo ad esaminare alcuni oggetti del namespace javafx.scene
, che contiene forme di base, colori ed effetti.
L'oggetto javafx.scene.paint.Color
contiene una serie di colori di base, mentre gli oggetti grafici (superclassi di javafx.scene.shape.Shape
) possono essere colorati nel contenuto e nel bordo, possono avere anche degli effetti grafici, come ad esempio un punto di luce.
Iniziamo subito con del codice, che poi commenteremo:
// Definiamo una variabile per il colore del bordo
var strokeColor:Color = Color.GOLD;
// Creiamo un rettangolo contenente un punto di luce
var drawRectangle:Rectangle = Rectangle {
x: 0
y: 0
width: bind stage.width
height: bind stage.height
fill: Color.ALICEBLUE
stroke: strokeColor
strokeWidth:15
effect: Lighting {
light: PointLight {
x: bind stage.width/2
y: bind stage.height/2
z: 50
}
surfaceScale: 5
}
}
Nella sezione content della scena richiamiamo il nostro rettangolo:
content: [
// inseriremo qui i diversi oggetti
drawRectangle
]
L'effetto ottenuto è il seguente:
In questo caso abbiamo un rettangolo che ha come colore di riempimento ALICEBLUE
e un bordo dorato (GOLD). Al centro del rettangolo abbiamo posizionato un punto di luce nel quale è specificata la posizione attraverso le coordinate spaziali x
, y
e z
.
È importante sottolineare che abbiamo potuto calcolare e applicare la posizione della luce grazie alla parola chiave 'bind' e grazie al fatto che abbiamo dato un nome (stage
) alla nostra istanza di Stage
.
Riconoscere un'applet
javafx.lang.FX
è un singleton, svolge una funzione simile a quella di java.lang.System
in java, può leggere il contenuto di variabili di sistema (come javafx.user.home
, javafx.os.name
javafx.screen.width
, etc..), leggere argomenti dalla VM (virtual machine) o semplicemente chiudere l'applicazione (FX.exit()
).
Inoltre, la funzione FX.getArgument("isApplet")
ci dà informazioni circa il contesto in cui stiamo utilizzando l'applicazione. In pratica, leggiamo se è presente isApplet
tra gli argomenti di lancio.
Aggiungiamo all'esempio precedente un paio di dichiarazioni:
// Dichiariamo due valori booleani
var isApplet = "true".equals(FX.getArgument("isApplet") as String);
var inbrowser = not isApplet;
// Poi dichiariamo un colore,
// che dipende dal valore assunto dalla variabile
var strokeColor:Color = bind {
if(inbrowser) { Color.BEIGE }
else { Color.GOLD }
}
In questo modo il bordo del rettangolo cambierà colore a seconda che lanciamo l'applicazione come applet o meno.
Immagini
Per includere le immagini nella nostra applicazione possiamo semplicemente inserirle all'interno del package dell'applicazione. Per mostrarle poi sfruttiamo gli oggetti:
javafx.scene.image.Image
javafx.scene.image.ImageView
Image
si occupa della gestione del sorgente dell'immagine che vogliamo utilizzare e ImageView
di renderizzarla nell'applicazione, come ogni altro nodo (javafx.scene.Node
):
var logo:Image = Image {
url: "{__DIR__}logo.png"
width: 50
height: 50
}
var imageView:ImageView = ImageView{
x: 10
y: 10
image: logo
}
Componenti multimediali
Proviamo ora a inserire una canzone come musica di sottofondo. Definiamo un oggetto javafx.scene.media.Media
che si occupa della gestione del sorgente:
var baseURL:String = "music";
var filename:String = "sound.wav";
var song = Media {
source: "{__DIR__}{baseURL}/{filename}"
onError: function(e:MediaError) {
println("errore {e}");
}
}
Includiamo l'oggetto Media
in un'istanza di javafx.scene.media.MediaPlayer
per la gestione del componente:
var mediaPlayer:MediaPlayer = MediaPlayer{
media: song
}
Infine, al verificarsi di un qualsiasi evento nel quale vogliamo far partire il contenuto multimediale, eseguiamo:
mediaPlayer.play();
In conclusione, la gestione del file multimediale è delegata all'oggetto javafx.scene.media.Media
, in cui è possibile specificare caratteristiche come la durata, o l'altezza e la larghezza se si tratta di un file video.
La fruizione del contenuto multimediale, invece, è delegata all'oggetto javafx.scene.media.MediaPlayer
, che si occupa di integrarlo nell'applicazione.
Il Deployment delle applet
È importante spendere qualche parola sul deployment delle applet realizzate con JavaFX. Una volta compilato il sorgente e lanciato il packager (vedi articolo precedente), viene creata una cartella dist
, nella quale troviamo il file .jar
dell'applicazione, due file .jnlp
e un file HTML.
Nel file HTML troviamo il codice necessario all'embed dell'applet sulla nostra pagina:
<script src="http://dl.javafx.com/1.2/dtfx.js"></script>
<script>
javafx(
{
archive: "percorsoApplicazione/nomeApplicazione.jar",
width: 400,
height: 200,
code: "nomeApplicazione",
name: "nomeApplicazione"
}
);
</script>
Prima di inserire questo codice nella pagina che ospiterà l'applet, perché tutto funzioni, dobbiamo caricare sul server i due file:
nomeApplicazione.jar
nomeApplicazione_browser.jnlp
e stare molto attenti agli URL.
Il file nomeApplicazione_browser.jnlp
, infatti è un descrittore dell'applicazione ed indica al plugin java del nostro browser alcune informazioni, come l'indirizzo di base dell'applicazione. Se non indichiamo al plugin l'esatta posizione dei nostri file questo potrebbe non caricare l'applet!
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="http://percorsoApplicazione/" href="rect01_browser.jnlp">
<information>
<title>nomeApplicazione</title>
<vendor>andimar</vendor>
<homepage href="http://percorsoApplicazione/"/>
<description>nomeApplicazione</description>
<offline-allowed/>
<shortcut>
<desktop/>
</shortcut>
</information>
<resources>
<j2se version="1.5+"/>
<extension name="JavaFX Runtime" href="http://dl.javafx.com/1.2/javafx-rt.jnlp"/>
<jar href="nomeApplicazione.jar" main="true"/>
</resources>
<applet-desc name="nomeApplicazione" main-class="com.sun.javafx.runtime.adapter.Applet" width="400" height="200">
<param name="MainJavaFXScript" value="nomeApplicazione">
</applet-desc>
<update check="background">
</jnlp>
Nella seconda parte dell'articolo vedremo come creare animazioni e transizioni e come trascinare un'applet e trasformarla in una applicazione desktop.
Le Animazioni
JavaFX possiede nativamente una ricca dotazione di animazioni. Grazie alla programmazione dichiarativa possiamo definire e applicare animazioni indipendentemente dagli oggetti che ne sono coinvolti. Questo ci permette di scegliere una delle animazioni disponibili e semplicemente applicarla ad uno degli oggetti che abbiamo già presenti sulla scena, poi se l'effetto non ci piace, cancellare tutto e applicare un altro movimento.
Tutto ciò di cui abbiamo bisogno è il riferimento all'oggetto grafico da animare e di impostare alcuni parametri. Vediamo subito un esempio. Dichiariamo un un oggetto testo in questo modo:
var title:Text = Text {
effect: Lighting {
light: SpotLight {
x: 0
y: 100
z: 50
pointsAtX: 400
pointsAtY: 0
pointsAtZ: 0
specularExponent: 2
color: Color. FIREBRICK
}
surfaceScale: 5
}
font : Font.font(null, FontWeight.BOLD, 26)
fill: Color.WHITE
stroke: Color.WHITE
x: 60
y: 100
content: bind "Simple applicationn on {testo}"
}
Definiamo un effetto di rotazione e lo colleghiamo al nodo grafico che ci interessa animare:
var rotateTransition = RotateTransition {
duration: 18s
node: title
byAngle: 90
fromAngle: -270
toAngle: 0
repeatCount:1
autoReverse: true
framerate: 60
}
così facendo, abbiamo definito una trasizione circolare di un gruppo di componenti grafici, della durata di 18
secondi, dall'angolo 90°
a 0°
ruotando per i 270°
rimanenti, con un framerate di 60
fotogrammi/sec. Questo ha un grande potenziale perché ci si può concentrare prima sul disegno grafico dell'applicazione e poi sulla gestione delle animazioni.
Infine, al verificarsi di un evento la facciamo partire, come per le risorse multimediali, attraverso il metodo play()
:
rotateTransition.play();
Un altro esempio:
var translateTransition = TranslateTransition {
duration: 12s
node: imageView
byX: 300
repeatCount: 2
autoReverse: true
framerate: 60
}
translateTransition.play();
Questa è una traslazione dell'oggetto imageView
di 300
pixel, della durata di 12
secondi, con frequenza di aggiornamento a 60 fotogrammi/sec.
Applet Extension
Una delle caratteristiche interessanti di JavaFX è la possibilità di trascinare, con un semplice drag'n'drop, l'applet che visualizziamo sul sito, all'interno del desktop. Questa operazione cambia il contesto operativo dell'applicazione e, come abbiamo visto nelle pagine precendenti, siamo in grado di gestire lo switch tra le due modalità.
Per implementare l'Applet Extension dobbiamo utilizzare un'estensione della classe Stage
: javafx.stage.AppletStageExtension.
Ecco un esempio:
var stage = Stage {
title: "Simple Application"
width: 400
height: 200
scene: Scene {
content: [
drawRectangle,
title
]
}
extensions: [
AppletStageExtension {
// Definisce la combinazione di eventi
// attraverso cui inizia il drag'n'drop
// dell'applet al di fuori del browser
shouldDragStart: function(e): Boolean {
return e.primaryButtonDown and title.hover;
}
//Evento di chiusura dell'operazione di trascinamento
onDragFinished: function(): Void {
inbrowser=false;
}
onAppletRestored: function(): Void {
inbrowser=true;
}
onDragStarted: function(): Void {
// appena esternalizzata
// parte la musica di sottofondo
mediaPlayer.play();
}
useDefaultClose: true
}
]
}
Attraverso l'evento shouldDragStart specifichiamo la condizione al verificarsi della quale l'applet perde l'integrazione col browser e può essere spostata a piacimento sul desktop.
Gli eventi onDragStarted
e onAppletRestore
sono invocati rispettivamente quando l'applet si stacca dal browser e quando, da applicazione, ci ritorna.
Se chiudiamo il browser con l'applicazione esternalizzata, le routine di JavaWebStart ci chiederanno se vogliamo il link dell'applicazione sul desktop, che dà la possibilità di riaprire l'applicazione in un altro momento, ndipendentemente dal browser.
Se non chiudiamo il browser, premendo la X
di chiusura l'applicazione ritorna ad essere una applet; se il browser è chiuso, chiuderemo anche l'applicazione.
Nota: perché tutto funzioni correttamente occorre ricordare di aggiungere al comando javafxpackager
l'opzione -draggable
e di aggiungere la dichiarazione draggable:true
nella funzione javafx() che richiama l'applet.
Ecco l'esempio completo:
Tutto il codice degli esempi è contenuto nel file in allegato.