JavaFX and Scala - Like Milk and Cookies - Brazil

47
JavaFX e Scala – Como Leite com Bolacha Rafael Afonso Independente, Magna Sistemas [email protected] tweet: @rucafonso Stephen Chin JavaFX Evangelist, Oracle [email protected] tweet: @steveonjava

description

 

Transcript of JavaFX and Scala - Like Milk and Cookies - Brazil

Page 1: JavaFX and Scala - Like Milk and Cookies - Brazil

JavaFX e Scala – Como Leite com Bolacha

Rafael AfonsoIndependente, Magna [email protected]: @rucafonso

Stephen ChinJavaFX Evangelist, [email protected]: @steveonjava

Page 2: JavaFX and Scala - Like Milk and Cookies - Brazil

Conheça os Apresentadores

Stephen Chin

Motorciclista

Homem de Família

Rafael Afonso

Programador Java desde 2001

Interessado em Scala desde 2008

Page 3: JavaFX and Scala - Like Milk and Cookies - Brazil

Plataforma JavaFX 2.0

Experiências de Aplicações imersivas

> Animações, Videos e Gráficos Cross-platform

> Integra Java, JavaScript e HTML5 na mesma aplicação

> Nova pilha gráfica toma vantagem da aceleração de hardware para aplicações 2D e 3D

> Use sua IDE favorita: NetBeans, Eclipse, IntelliJ, etc.

Page 4: JavaFX and Scala - Like Milk and Cookies - Brazil

4

JavaFX

Scala

Page 5: JavaFX and Scala - Like Milk and Cookies - Brazil

JavaFX Com Java

Page 6: JavaFX and Scala - Like Milk and Cookies - Brazil

JavaFX em Java

> A API do JavaFX usa uma melhora do padrão JavaBeans

> Similar a outros UI toolkits (Swing, Pivot, etc.)

> Usa o Design Pattern Builder para minimizar a parte monótona.

Page 7: JavaFX and Scala - Like Milk and Cookies - Brazil

7

Vanishing Circles

Page 8: JavaFX and Scala - Like Milk and Cookies - Brazil

Esqueleto da Aplicação

public class VanishingCircles extends Application { public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Vanishing Circles"); Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); [cria os círculos…] root.getChildren().addAll(circles); primaryStage.setScene(scene); primaryStage.show(); [inicia a animação…] }}

Page 9: JavaFX and Scala - Like Milk and Cookies - Brazil

Criação dos Círculos

List<Circle> circles = new ArrayList<Circle>();for (int i = 0; i < 50; i++) { final Circle circle = new Circle(150); circle.setCenterX(Math.random() * 800); circle.setCenterY(Math.random() * 600); circle.setFill(new Color(Math.random(), Math.random(), Math.random(), .2)); circle.setEffect(new BoxBlur(10, 10, 3)); circle.setStroke(Color.WHITE); [configura os bindings…] [configura os event listeners…] circles.add(circle);}

9

Page 10: JavaFX and Scala - Like Milk and Cookies - Brazil

Configuração do Binding

circle.strokeWidthProperty().bind(Bindings .when(circle.hoverProperty()) .then(4) .otherwise(0));

10

Page 11: JavaFX and Scala - Like Milk and Cookies - Brazil

Configuração de Event Listeners

circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); }});

11

Page 12: JavaFX and Scala - Like Milk and Cookies - Brazil

Iniciando a Animação

Timeline moveCircles = new Timeline();for (Circle circle : circles) { KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random() * 800); KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random() * 600); moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40), moveX, moveY));}moveCircles.play();

12

Page 13: JavaFX and Scala - Like Milk and Cookies - Brazil

13

JavaFX Com Scala

Page 14: JavaFX and Scala - Like Milk and Cookies - Brazil

Java vs. Scala DSLpublic class VanishingCircles extends Application {

public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Vanishing Circles"); Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); List<Circle> circles = new ArrayList<Circle>(); for (int i = 0; i < 50; i++) { final Circle circle = new Circle(150); circle.setCenterX(Math.random() * 800); circle.setCenterY(Math.random() * 600); circle.setFill(new Color(Math.random(), Math.random(),

Math.random(), .2)); circle.setEffect(new BoxBlur(10, 10, 3)); circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new

EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); } }); circle.setStroke(Color.WHITE);

circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) .then(4) .otherwise(0)); circles.add(circle); } root.getChildren().addAll(circles); primaryStage.setScene(scene); primaryStage.show(); Timeline moveCircles = new Timeline(); for (Circle circle : circles) { KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random()

* 800); KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random()

* 600); moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40),

moveX, moveY)); } moveCircles.play(); }}

object VanishingCircles extends JFXApp { var circles: Seq[Circle] = null stage = new Stage { title = "Vanishing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK circles = for (i <- 0 until 50) yield new Circle { centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, .2) effect = new BoxBlur(10, 10, 3) strokeWidth <== when (hover) then 4 otherwise 0 stroke = WHITE onMouseClicked = { Timeline(at (3 s) {radius -> 0}).play() } } content = circles } }

new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) { Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) } }.play();}

14

40 Linhas1299 Caracteres

33 Linhas591 Caracteres

Page 15: JavaFX and Scala - Like Milk and Cookies - Brazil

object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {

centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}

15

Page 16: JavaFX and Scala - Like Milk and Cookies - Brazil

16

object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle { centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}

Classe base para aplicações ScalaFX

Page 17: JavaFX and Scala - Like Milk and Cookies - Brazil

17

object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {

centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}

Definição Declarativa do Stage

Page 18: JavaFX and Scala - Like Milk and Cookies - Brazil

18

object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {

centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}

Definições de propriedades inline

Page 19: JavaFX and Scala - Like Milk and Cookies - Brazil

19

object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {

centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}

Criação de Sequência Via Loop

Page 20: JavaFX and Scala - Like Milk and Cookies - Brazil

Binding em Scala

Adição/Subtração/Multiplicação/Divisão Infixas:

height <== rect1.height + rect2.height

Operadores de Agregação:

width <== max(rect1.width, rect2.width, rect3.width)

Expressões Condicionais:

strokeWidth <== when (hover) then 4 otherwise 0

Expressões Compostas:

text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled" otherwise "disabled"

20

Page 21: JavaFX and Scala - Like Milk and Cookies - Brazil

Animação em Scala

val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {

Set( circle.centerX -> random * stage.width.get,

circle.centerY -> random * stage.height.get

) }}timeline.play();

21

Page 22: JavaFX and Scala - Like Milk and Cookies - Brazil

val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {

Set( circle.centerX -> random * stage.width.get, circle.centerY -> random * stage.height.get ) }}timeline.play();

Animação em Scala

22

Sintaxe de animação como no JavaFX Script:

at (duração) {keyframes}

Page 23: JavaFX and Scala - Like Milk and Cookies - Brazil

val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {

Set( circle.centerX -> random * stage.width.get, circle.centerY -> random * stage.height.get ) }}timeline.play();

Animação em Scala

23

Sobrecarga de Operador para sintaxe de animação

Page 24: JavaFX and Scala - Like Milk and Cookies - Brazil

val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {

Set( circle.centerX -> random * stage.width tween EASE_BOTH,

circle.centerY -> random * stage.height tween EASE_IN

) }}timeline.play();

Animação em Scala

24

Sintaxe tween opcional

Page 25: JavaFX and Scala - Like Milk and Cookies - Brazil

Event Listeners em Scala

25

> Suporte a sintaxe de Closure> Argumentos para objetos eventos> 100% type-safe

onMouseClicked = { (e: MouseEvent) =>

Timeline(at(3 s){radius->0}).play()

}

Page 26: JavaFX and Scala - Like Milk and Cookies - Brazil

Event Listeners em Scala

> Suporte a sintaxe de Closure> Argumentos para objetos eventos> 100% type-safe

26

Parâmetro evento opcional:

{(event) => body}

onMouseClicked = { (e: MouseEvent) =>

Timeline(at(3 s){radius->0}).play()

}

Page 27: JavaFX and Scala - Like Milk and Cookies - Brazil

27

Red Control

Green Control

Blue Control

Opacity (Alpha)Control

Syncronizer

Values goes from 0 to 255 Enable/Disable Opacity

Pre-defined colors (from Color JavaFX class)

Colors Formatation:• Hexadecimal: #ADFF2F• RGB: rgba(173, 255, 47, 0,61)• Percent: rgba( 67%, 100%, 18%, 0,61)• HSB: hsba( 84, 82%, 100%, 0,61)

Color Selector

Page 28: JavaFX and Scala - Like Milk and Cookies - Brazil

28

Color Selector

> Versões anteriores feitas em Java Swing, JavaFX 1.3, HTML4 e HTML5.

> Objetivo é alterar os parâmetros da cor do retângulo pelos componentes Vermelho (R), Verde (G), Azul (B) e Opacidade (A – Alpha), com valores variando de 0 a 255.

> É possível escolher uma cor pré-definida no objeto scalafx.scene.paint.Color (que é um wrapper da classe javafx.scene.paint.Color).

> Também é possível exibir o valor da cor tal como usado no HTML e ainda escolher a formatação (Hexedecimal, RGB, HSL).

Page 29: JavaFX and Scala - Like Milk and Cookies - Brazil

SliderControlclass SliderControl extends HBox {

val realValue = new DoubleProperty(new SimpleDoubleProperty) def value = this.realValue def value_=(d: Double) { if (d < Min) { value() = Min } else if (d > Max) { value() = Max } else { value() = d } }

val sldValue = new Slider { // ... value <==> realValue }}

29

Propriedade usada Internamente

Operador de Duplo Binding

Wrapper de javafx.beans.property.DoubleProperty

getter e setter do valor (equivalente às properties do C#).

Equivalente a fazer realValue.set(d)

Page 30: JavaFX and Scala - Like Milk and Cookies - Brazil

Se fosse em Java, teríamos que escrever:controlRed.valueProperty.addListener( new ChangeListener<DoubleProperty>() { @Override public void changed(ObservableDouble d, Double old, Double new) { changeColor(); } }});O ScalaFX já faz isso por nós usando closures.

val currentColor = new ObjectProperty[Color](Color.WHITE, "Color")

currentColor.onChange(rectangleRegion.setStyle("-fx-background-color: " + RgbFormatter.format(currentColor(), !this.chbDisableAlpha.selected.get)))

private def changeColor { val newAlphaValue = if (controlAlpha.disabled.get()) 1.0 else (controlAlpha.value.toDouble / 255)

this.currentColor() = Color.rgb(controlRed.value.toInt, controlGreen.value.toInt, controlBlue.value.toInt, newAlphaValue)}

val controlRed = new SliderControl("R") { value = 255}controlRed.value.onChange(changeColor)

Cor do Retângulo

30

Page 31: JavaFX and Scala - Like Milk and Cookies - Brazil

Sincronização de Valores

31

val synchronizedValue = new DoubleProperty(new SimpleDoubleProperty)

val synchronizedControls = new ObservableBuffer[SliderControl] synchronizedControls.onChange((buffer, changes) => synchronizedValues(buffer, changes))

private def controlSelected(control: SliderControl) { // Método chamado ao clicar no Checkbox de sincronização if (control.selectedControl.get) synchronizedControls.add(control) else synchronizedControls.remove(control)}

// Método chamado ao adicionar/remover um elemento private def synchronizeValues(buffer: ObservableBuffer[SliderControl], changes: Seq[Change]) { changes(0) match { case Add(pos, added) => { val media = buffer.map(_.value.get).sum / buffer.size added.last.asInstanceOf[SliderControl].value <==> synchronizedValue buffer.foreach(_.value = media) } case Remove(pos, removed) => { removed.last.asInstanceOf[SliderControl].value unbind synchronizedValue } }}

controlRed.selectedControl.onChange(controlSelected(controlRed))

Adiciona duplo binding

Remove duplo binding

Buffer que reúne os controle sincronizados

Versão ScalaFX do ObservableList do JavaFX

Super trait das listas em Scala

Page 32: JavaFX and Scala - Like Milk and Cookies - Brazil

Cores pré-definidas

32

import scalafx.scene.paint.Colorimport scalafx.scene.paint.Color._

object WebColor {

val colors = List( WebColor("ALICEBLUE", ALICEBLUE), WebColor("ANTIQUEWHITE", ANTIQUEWHITE), WebColor("AQUA", AQUA), ... WebColor("WHITE", WHITE), WebColor("WHITESMOKE", WHITESMOKE), WebColor("YELLOW", YELLOW), WebColor("YELLOWGREEN", YELLOWGREEN))

}

sealed case class WebColor(name: String, color: Color)

Page 33: JavaFX and Scala - Like Milk and Cookies - Brazil

Exibição das cores pré-definidas

33

object ColorSelector extends JFXApp { private def verifyWebColor { cmbWebColor.value() = WebColor.colors.find(_.sameColor(currentColor.get)).orNull }

private def webColorSelected { if (this.cmbWebColor.value.get != null) { val color = this.cmbWebColor.value.get.color

controlRed.value() = doubleToInt(color.red) controlGreen.value() = doubleToInt(color.green) controlBlue.value() = doubleToInt(color.blue) } }

val cmbWebColor = new ComboBox[WebColor](WebColor.colors) { onAction = webColorSelected converter = StringConverter.toStringConverter((wc: WebColor) => wc.name) }}

Page 34: JavaFX and Scala - Like Milk and Cookies - Brazil

Formatação das cores

34

object Formatter { val formatters = List(HexFormatter, RgbFormatter, PercentFormatter, HsbFormatter)}

abstract sealed case class Formatter(val description: String) {

protected def colorToRgbInt(c: Color): (Int, Int, Int) = (doubleToInt(c.red), doubleToInt(c.green), doubleToInt(c.blue))

protected def formatWithAlpha(c: Color): String

protected def formatWithoutAlpha(c: Color): String

def format(c: Color, hasAlpha: Boolean): String = if (hasAlpha) formatWithAlpha(c) else formatWithoutAlpha(c)

}

object HexFormatter extends Formatter("Hexadecimal") { val HEXADECIMAL_FORMAT = "#%02x%02x%02x";

def formatWithAlpha(c: Color): String = { val (r, g, b) = super.colorToRgbInt(c) HEXADECIMAL_FORMAT.format(r, g, b).toUpperCase }

def formatWithoutAlpha(c: Color): String = formatWithAlpha(c)

}

Page 35: JavaFX and Scala - Like Milk and Cookies - Brazil

Exibição da formatação das cores

35

private def formatColor { this.txfColorValue.text() = this.cmbColorFormat.value.get.format(this.currentColor.get, !this.chbDisableAlpha.selected.get) }

val cmbColorFormat = new ComboBox[Formatter](Formatter.formatters) { promptText = "Color Format" converter = StringConverter.toStringConverter((f: Formatter) => f.description) value = RgbFormatter onAction = formatColor }

Page 36: JavaFX and Scala - Like Milk and Cookies - Brazil

Ou: Como escrever sua própria DSL em Scala.

36

Funcionamento do ScalaFX

Com citações de Stephen Colebourne (@jodastephen) para o bem de nossa sanidade!Aviso: Declarações extraídas de http://blog.joda.org e podem não refletir exatamente sua opnião ou ponto de vista.

Luc Viatour / www.Lucnix.be

Page 37: JavaFX and Scala - Like Milk and Cookies - Brazil

37

Inicialização da Aplicação

> JavaFX requer que todo código de UI seja executado na Thread da aplicação.

> Mas nossa Aplicação ScalaFX não possui método start:

object VanishingCircles extends JFXApp {

stage = new Stage { … }}

Como esse código funciona?!?

Page 38: JavaFX and Scala - Like Milk and Cookies - Brazil

38

DelayedInit

> Introduzido no Scala 2.9> Como usar:1. Estender um trait especial chamado DelayedInit2. Implementar um método do tipo:

def delayedInit(x: => Unit): Unit3. Guardar a closure init e chamá-la no Thread da

Aplicação

Para mim, Scala não inova o suficiente e adiciona demais – uma combinação letal.

Joda diz…

Page 39: JavaFX and Scala - Like Milk and Cookies - Brazil

39

> ScalaFX define um conjunto de proxies que espelham a hierarquia de JavaFX

> As classes JavaFX são “implicitamente” acondicionadas (wrapped) quando se chama a API do ScalaFX

> Mas a prioridade de implicit de Scala ignora a hierarquia de tipos!

JFXNode

JFXShape

JFXCircle

?!

Hierarquia de Conversões implicitas

SFXNode

SFXShape

SFXCircle

Page 40: JavaFX and Scala - Like Milk and Cookies - Brazil

Precedência de Implicits de N Níveis

> Scala dispara uma exceção se dois implicits têm a mesma precedência.

> Classes que são estendidas têm uma precedência menor

> Você pode empilhar os traits com profundidade de n níveis para reduzir a precisão para n.

40

Bem, pode ser type safe, mas é também silencioso e bem mortal.

Joda diz…

object HighPriorityIncludes extends LowerPriorityIncludes {…}trait LowerPriorityIncludes {…}

Page 41: JavaFX and Scala - Like Milk and Cookies - Brazil

Propriedades

> JavaFX suporta propriedades do tipo Boolean, Integer, Long, Float, Double, String, e Object

> Propriedades usam Genéricos para segurança de tipos.

> Mas genéricos não suportam primitivos…> JavaFX soluciona isso com 20 interfaces e 44

classes para todos os tipos de combinações de tipos somente-leitura ou graváveis.

> Podemos melhorar?

41

Page 42: JavaFX and Scala - Like Milk and Cookies - Brazil

@specialized

> Anotação especial que gera variantes primitivas da classe.

> Melhora a performance ao evitar boxing/unboxing

> Diminui a duplicação de código (ScalaFX tem apenas 18 classes de Propriedades/Valores)

42

Qualquer que seja o problema o sistema de tipos deve ser parte da solução.

Joda diz…

trait ObservableValue[@specialized(Int, Long, Float, Double, Boolean) T, J]

Page 43: JavaFX and Scala - Like Milk and Cookies - Brazil

Bindings

43

> Como Scala sabe a ordem de avaliação?

text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled" otherwise " disabled"

E por que esse operador esquisito de binding?!?

Page 44: JavaFX and Scala - Like Milk and Cookies - Brazil

Regra de Precedência de Operadores

44

> Primeiro caractere determina a precedência

Maior Precedência

10. (all letters)9. |8. ^7. &6. < >5. = !4. :3. + *2. / %1. (todos os outros caracteres especiais)

Menor Precedência

11. Operadores de atribuição que terminam com igual> Mas não começam com

igual> E não podem ser:

<= >= !=

Exceção são os operadores de atribuição cuja prioridade é menor ainda…

Page 45: JavaFX and Scala - Like Milk and Cookies - Brazil

Precedência de Operadores

45

Pessoalmente, acho que o objetivo de sintaxe aberta e flexível (DSLs arbitrárias) não compensam o esforço

Joda diz…

text <== when (rect.hover || circle.hover

&& !disabled) then textField.text + " is

enabled" otherwise "disabled"

911 10

7 105 3

10

Page 46: JavaFX and Scala - Like Milk and Cookies - Brazil

Conclusão

> Você pode usar Scala e JavaFX juntos.> ScalaFX fornece APIs mais simples, feita sob

medida para Scala.> Experimente ScalaFX hoje e ajude a contribuir

com APIs para a futura versão 1.0!

http://code.google.com/p/scalafx/

Page 47: JavaFX and Scala - Like Milk and Cookies - Brazil

47

Stephen [email protected]: @steveonjava

Desconto especial de 40% para o JustJava. Entre em apress.com e digite o código PJVF73

Obrigado!