First steps with JavaFX
This tutorial will introduce
- Clean separation of application UI and logic
- Using Builders supporting Fluent API
- Effects
- Layouts
- Property Bindings
- EventHandler
Abstract
The Golden Ratio (Golden Mean, Golden Section) is defined as φ = (√5 + 1) / 2.
Objective
Create an UI to find the golden section by using a slider. Add value labels and an indicator to get green when golden action is hit.
The Model
Create observable double a and b values and a boolean value to indicate whether the golden section has been hit:
private DoubleProperty aValue = new SimpleDoubleProperty(0.0); private DoubleProperty bValue = new SimpleDoubleProperty(0.0); private BooleanProperty sectioAurea = new SimpleBooleanProperty(false);
Method to calculate the golden section:
private void reCalculate(){ double b = aValue.substract(100).multiply(-1).get(); setBValue(b); double result1 = aValue.get()/bValue.get(); result1 = Math.round(result1*100.0)/100.0; double result2 = (aValue.get()+bValue.get())/aValue.get(); result2 = Math.round(result2*100.0)/100.0; sectioAurea.set(result1 == result2); }
Please note: get() returns the primitive double value, getValue() respectively returns the Double instance.
Next attach listeners to re-caculate the golden section indicator on value changes:
private void attachListener(){ChangeListener onChangeListener = new ChangeListener(){ @Override public void changed(ObservableValue arg0, Number arg1, Number arg2) { reCalculate(); } }; getAValueProperty().addListener(onChangeListener); getBValueProperty().addListener(onChangeListener); }
Here is the complete model code width appropriate getter and setter methods:
package de.jensd.javafx.aurea; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; /** * * @author Jens Deters */ public class SectioModel { private DoubleProperty aValue = new SimpleDoubleProperty(0.0); private DoubleProperty bValue = new SimpleDoubleProperty(0.0); private BooleanProperty sectioAurea = new SimpleBooleanProperty(false); public SectioModel() { attachListener(); } private void attachListener(){ ChangeListener onChangeListener = new ChangeListener(){ @Override public void changed(ObservableValue arg0, Number arg1, Number arg2) { reCalculate(); } }; getAValueProperty().addListener(onChangeListener); getBValueProperty().addListener(onChangeListener); } public DoubleProperty getAValueProperty() { return aValue; } public double getAValue() { return aValue.get(); } public void setAValue(double d) { aValue.set(d); } public DoubleProperty getBValueProperty() { return bValue; } public double getBValue() { return bValue.get(); } public void setBValue(double d) { bValue.set(d); } public BooleanProperty getSectioAureaProperty() { return sectioAurea; } public boolean isSectioAurea() { return sectioAurea.get(); } private void reCalculate(){ double b = aValue.substract(100).multiply(-1).get(); setBValue(b); double result1 = aValue.get()/bValue.get(); result1 = Math.round(result1*100.0)/100.0; double result2 = (aValue.get()+bValue.get())/aValue.get(); result2 = Math.round(result2*100.0)/100.0; sectioAurea.set(result1 == result2); } }
The UI
Title Label and usage hint
Text titleText = TextBuilder.create(). text("Sectio Aurea"). effect(ds). font(Font.font(null, FontWeight.BOLD, 32)). build(); Text descriptionText = TextBuilder.create(). text("Move the slider until your hit the golden section.\nHint: use the cursor keys for exact control."). textAlignment(TextAlignment.CENTER). build();
Indicator
Stop[] falseStops = new Stop[]{new Stop(0.0, Color.WHITE), new Stop(0.3, Color.RED), new Stop(1.0, Color.DARKRED)}; falsePaint = new RadialGradient(0.0, 0.0, -10.0, -10.0, 50, false, CycleMethod.NO_CYCLE, falseStops); Stop[] trueStops = new Stop[]{new Stop(0.0, Color.WHITE), new Stop(0.3, Color.GREENYELLOW), new Stop(1.0, Color.DARKGREEN)}; truePaint = new RadialGradient(0.0, 0.0, -10.0, -10.0, 50, false, CycleMethod.NO_CYCLE, trueStops); ellipse = EllipseBuilder.create(). radiusX(30). radiusY(30). fill(falsePaint). build(); DropShadow ds = new DropShadow(); ds.setOffsetY(3.0f); ds.setColor(Color.color(0.4f, 0.4f, 0.4f)); ellipse.setEffect(ds);
Slider
slider = SliderBuilder.create(). min(model.getAValue()). max(model.getBValue()). showTickMarks(true). showTickLabels(true). majorTickUnit(20). minorTickCount(3). prefWidth(300). blockIncrement(0.1f). build();
Status Labels
aValueText = TextBuilder.create().build(); bValueText = TextBuilder.create().build(); aureaText = TextBuilder.create().build();
Solve Button
solveButton = ButtonBuilder.create().text("Solve").build();
Add the solve action
solveButton.setOnAction(new EventHandler() { @Override public void handle(ActionEvent arg0) { model.setAValue(61.8); } });
Leave a Reply