Want to dive even deeper?

Take the course JavaFX for beginners by Hendrik Ebbers and become an expert!
JavaFX for beginners
by Hendrik Ebbers

Check it out!
You're watching a preview of this video, click the button on the left to puchase the full version from Richard Warburton's Channel.

JavaFX Layout Secrets with Amy Fowler

Amy Fowler gives an in-depth guide to building complex user interfaces with JavaFX Layouts. Includes preview information about JavaFX 1.3 layouts and features.

Published on
  • 51.896
  • 127
  • 3
  • 5
  • 0
  • VA JA LA FX UT YO January 13, 2010 amy.fowler@sun.com
  • AGENDA JavaFX Layout Fundamentals *Changes for 1.3 (in blue) * we reserve the right to change our minds (at least until we ship)
  • INTERFACES, 2010
  • JAVAFX LAYOUT GOALS • Make common layout idioms easy • rows/columns, forms, alignment, spacing, etc. • Don’t get in the way of creativity • animation - must allow things to move • free form shapes - no longer restricted by nested, clipped, rectangles! • Performance a major focus for 1.3
  • LAYOUT MECHANISM • Scene graph layed out once per pulse, before rendering • nodes call requestLayout() when preferred size changes • requests all coalesced for next layout pass • layout executes top-down (dirty branches only) • MUCH more efficient in 1.3 • fine-tuned calls to requestLayout • more efficient bounds calculations
  • • Resizable mixin class enables nodes to be resized externally: public public public public public public public public var width:Number var height:Number function getMinWidth():Number function getMinHeight():Number function getPrefWidth(height:Number):Number function getPrefHeight(width:Number):NUmber function getMaxWidth():Number function getMaxHeight():Number RESIZABLE VS. NOT Resizable Containers Controls Not Resizable Group, CustomNode Text, ImageView, Shapes • Resizable & non-Resizable nodes can be freely mixed • non-Resizables treated as rigid (min = pref = max) • Resize != Scale
  • LAYOUT BOUNDS • Logical bounds used for layout calculations public-read protected layoutBounds:Bounds layoutBounds boundsInLocal boundsInParent
  • LAYOUT BOUNDS CONTINUED Node type Non-Resizable (Shapes, Text, etc.) layoutBounds geometry only •no effect/clip/transforms 0, 0 width x height Resizable (Controls & Containers) •regardless of visual bounds Group union of childrens’ boundsInParent • effects/clip/transforms on children included • effects/clip/transforms on Group not included
  • TEXT LAYOUT BOUNDS • In 1.2, layoutBounds for Text was tight visual bounds • very expensive! • problematic for layout • doesn’t include leading, trailing whitespace puppy • 1.3 provides Text var for controlling how bounds calculated public var boundsType:TextBoundsType TextBoundsType.LOGICAL TextBoundsType.VISUAL (default) puppy
  • LAYOUT APPROACHES • App-managed • put nodes inside Groups • set translation to control positioning • use binding for dynamic layout behavior • Container-managed • put nodes inside Containers • Containers control location, sizing, dynamic behavior • recommended for common layout idioms • Blend them both
  • APP MANAGED POSITIONING • Position nodes by setting translation • set layoutX,layoutY for general positioning • set translateX,translateY for animation or adjustments • Transition classes modify translateX, translateY • final tx,ty => (layoutX + translateX), (layoutY + translateY) • translation != final location node.layoutX = bind x - node.layoutBounds.minX node.layoutY = bind y - node.layoutBounds.minY
  • APP MANAGED SIZING • Use binding to control dynamic sizing Stage { var scene:Scene; scene: scene = Scene { width: 300 height: 300 content: HBox { width: bind scene.width height: bind scene.height } } }
  • CONTAINER MANAGED • Put nodes inside Containers • Containers control positioning on all “managed” content • they set layoutX,layoutY (but don’t touch translateX/Y) • base all layout calcs on layoutBounds (not visual bounds) • Containers resize only Resizable nodes • treat non-Resizables (and nodes with bound width/height) as rigid VBox { spacing: 10 content: for (img in images) ImageView { image: img } } }
  • AUTO SIZING • 1.2 dichotomy between Groups and Containers: • Resizables inside Containers are resized automatically when their preferred size changes changes • Resizables inside Groups will NOT be resized when their preferred size • In 1.3, Groups will also automatically resize Resizable children when their preferred sizes change. • Two ways to turn this off: • set the child to “unmanaged” • bind the child’s width/height
  • LAYOUT & TRANSFORMS • For nodes inside Containers: • modifying effect, clip, transforms will NOT affect layout • TranslateTransition, ScaleTransition, RotateTransition will NOT affect layout • Wrap node in Group if you want transforms to affect layout Stack { content: Group { content: Rectangle { rotate: 45 // rotate will affect layout } } }
  • CONTAINERS • Container class mixes Resizable into Group • pondering change to extend directly from Parent • Abstract base class for layout containers • layoutBounds will always be (0, 0 width x height) • even if visual bounds differ
  • CONCRETE CONTAINERS • Stack, HBox,VBox, Tile, Flow, Panel, Grid (in 1.3 preview) • lay out both visible and invisible nodes • do not clip contents to fit within layout bounds • honor layout constraints set in LayoutInfo • 1.3 adds var for adding white space around content: public var padding:Insets; • 1.3 adds var for aligning on pixel boundaries: public var snapToPixel:Boolean = false;
  • HBOX & VBOX • Simple horizontal row or vertical column of nodes • Configurable spacing & alignment • Resizes Resizables to their preferred sizes HBox { spacing: 4 content: for (in in [0..4]) Thing { text: “{i}” } ] }
  • STACK • Easy back-to-front layering • z-order matches order of content[] sequence • Its preferred size is largest preferred width/height of children • Resizes Resizables to “fill” stack (up to their max size limits) Stack { content: [ Rectangle { ... Circle { ... } Label { text: “3” } ] } }
  • TILE • Lays out nodes in grid of uniform-sized “tiles” • Horizontal or vertical orientation • Wraps tiles when Tile’s size changes • Size of each “tile” defaults to largest preferred content • Resizes nodes to “fill” tile (up to their max size limits) • Configurable spacing & alignment Tile { columns: 3 hgap: 3 vgap: 3 content: for (i in [0..5]) Thing { text: “{i}” } }
  • TILE CONTINUED • In 1.3, columns var (horizontal) and rows var (vertical) used only to compute Tile’s preferred size • may not reflect actual rows/columns • In 1.3, new var controls whether tile size is fixed or recomputed as content sizes change: public var autoSizeTiles:Boolean = true; Tile { columns: 10 autoSizeTiles: false tileWidth: 150 tileHeight: 100 content: for (i in (sizeof images)) ImageView {image: Image { ... } } }
  • FLOW • Horizontal or vertical flow that wrap on width/height boundaries • Always resizes Resizables to their preferred sizes • Configurable spacing & alignment • 1.3 adds var to control the preferred wrap dimension: public var wrapLength:Number = 400; Flow { wrapLength: 300 hgap: 5 vgap: 10 content: for (i in [0..7]) Thing { ... } }
  • PANEL • Useful for custom layout on object literals • Provides function variables for container behaviors: public public public public public public public var var var var var var var minWidth:function():Number; minHeight:function():Number; prefWidth:function(h:Number):Number; prefHeight:function(w:Number):Number; maxWidth:function():Number; maxHeight:function():Number; onLayout:function():Void; Panel { onLayout: function():Void { // position/resize content nodes } }
  • IMPLEMENTING PANELS • Use convenience functions from Container! import javafx.scene.layout.Container.*; getManaged(content:Node[]):Node[] getNodePrefWidth(node)/getNodePrefHeight(node) positionNode(node, x, y) resizeNode(node, width, height) layoutNode(node, areaX, areaY, areaWidth, areaHeight, baseline, hfill, vfill, hpos, vpos) • They are smart... • handle subtracting minX, minY for positioning • deal with Resizable vs. non-Resizable nodes • honor LayoutInfo if set on node • swallow bind exceptions when width/height are bound
  • GRID • Based on Grid from JFXtra’s (thanks, Stephen!) • Supports rich, row-oriented grid layout • spanning, growing, alignment, etc Grid { hgap: 5 vgap: 8 rows: [ GridRow { cells: [ Label{}, ListView { layoutInfo: GridLayoutInfo { hspan:3 } } ]} GridRow { cells: [ ...] } ] }
  • LAYOUT INFO • Node hook to specify layout preferences: public var layoutInfo:LayoutInfoBase • Can be shared across nodes (values not copied) def sliderLAYOUT = LayoutInfo { width: 100 } def slider1 = Slider { layoutInfo: sliderLAYOUT } def slider2 = Slider { layoutInfo: sliderLAYOUT } • Should only be needed when customization is required • 3rd parties can extend LayoutInfoBase or LayoutInfo to create custom constraints
  • LAYOUT INFO public var managed:Boolean; public public public public public public var var var var var var minWidth:Number; minHeight:Number; width:Number; height:Number; maxWidth:Number; maxHeight:Number; size preference overrides alignment space around dynamic resize behavior public var hpos:HPos; public var vpos:VPos; public var margin:Insets; public public public public public public var var var var var var hgrow:Priority; vgrow:Priority; hshrink:Priority; vshrink:Priority; hfill:Boolean; vfill:boolean;
  • MANAGED VS. UNMANAGED • A managed node will have its layout managed by it parent Group Container Resizable child resized to preferred resized and positioned non-Resizable child no action positioned only • By default, all nodes are managed • An unmanaged node will be ignored (for layout) by parent • To unmanage, set bit in layoutInfo: VBox { content: [ Rectangle { layoutInfo: LayoutInfo.UNMANAGED // VBox will ignore } ...
  • OVERRIDING SIZE PREFS • Resizables have intrinsic values for min, pref, max sizes • Can use LayoutInfo to override values • To set a specific size on a Resizable, override it’s preferred: VBox { content: [ Button { // VBox will resize button to 100x100 layoutInfo: LayoutInfo { width: 100 height: 100 } }... ] } • DO NOT set width/height directly on Resizable - parent will obliterate values! (unless Resizable is unmanaged)
  • NODE ALIGNMENT • Sometimes node’s size is different from it’s allocated layout area • it cannot be resized (non-Resizable or has bound width/height) • it’s min or max size prevents it • Containers have default alignment vars for this case public var nodeHPos:HPos public var nodeVPos:VPos public enum HPos { LEFT, LEADING, CENTER, RIGHT, TRAILING } public enum VPos { TOP, PAGE_START, CENTER, BASELINE BOTTOM, PAGE_END }
  • NODE ALIGNMENT CONTINUED • LayoutInfo can be used to override alignment for specific nodes VBox { // nodeHPos defaults to HPos.LEFT content: [ Thing { text: “0” } Thing { text: “1” } Thing { text: “2” layoutInfo: LayoutInfo { hpos: HPos.CENTER } } ] }
  • BASELINE ALIGNMENT • 1.3 Containers supports roman baseline vertical alignment! HBox { nodeVPos: VPos.BASELINE content: [ ... ] } • TextOffsets mixin must be implemented by classes that want to be aligned on baseline: public var baselineOffset:Number • Text, Container, and Controls all implement TextOffsets • Classes that don’t implement TextOffsets will be treated as if baseline was on bottom edge
  • CONTENT ALIGNMENT • Container’s content sometimes doesn’t fit it’s size • Containers have vars for overall content alignment public var hpos:HPos = HPos.LEFT; public var vpos:VPos = VPos.TOP; HBox { hpos: HPos.CENTER vpos: VPos.CENTER nodeVPos: VPos.BOTTOM ... • In 1.3, HBox/VBox get var for content fill instead of align public var vfill:Boolean = false; HBox { vfill: true nodeVPos: VPos.BOTTOM ...
  • FILLING • “Filling” defines behavior when Resizable’s allocated layout area is larger than its preferred size fill = false fill = true keep node to preferred size expand node to fill layout area (up to max limit) • Stack and Tile do filling by default • HBox,VBox, Flow, and Grid do not fill by default • In 1.3 LayoutInfo can be used to change node’s fill behavior: Stack { content: [ Button { layoutInfo: LayoutInfo { vfill: false } }... ] }
  • GROWING & SHRINKING • “Growing” is priority mechanism used by Container to assign extra space when multiple nodes compete for that space node to fill the larger area (filling controls that) • applies to increasing layout area assigned to a node, NOT resizing • “Shrinking” is priority mechanism for taking away space when there is less than needed public enum Priority } { NEVER, SOMETIMES, ALWAYS • HBox supports horizontal grow/shrink • VBox supports vertical grow/shrink • Grid supports horizontal and vertical grow/shrink • Stack, Tile, Flow do not directly support grow/shrink, however...
  • GROWING & SHRINKING • Grow/shrink priorities are propagated up scene-graph • if Container has child with a grow of ALWAYS, then its grow value will be ALWAYS • enables powerful default behavior without heavy customization HBox { content: [ Button{}, Button{}, TextBox { layoutInfo: LayoutInfo { hfill: true hgrow: Priority.ALWAYS hshrink: Priority.ALWAYS } } Label{} ] }
  • 1.3 WORK IN PROGRESS • Support for layout roots • enable scene-graph branches to be laid out without affecting ancestors • useful for clipped content (scroll panes, viewports, etc) • Default LayoutInfo for Controls • sensible resizing “just works” out of the box • More efficient min/pref/max size calculations during layout • currently recalculated (for nested containers) at every level of layout pass
  • 1O LAYOUT COMMANDMENTS 1.Freely mix both app-managed and container-managed layout approaches 2.If you create a Node in a Group, then you must set its position and size, as Groups don't do layout. 3.If you create a Node in a Container, you are handing control of that node's position (and size, if its Resizable) to the Container. 4.Use layoutBounds as the basis of all layout-related calcs. 5.If a node's effect, clip, or transforms should be factored into its layout, then wrap it in a Group.
  • 1O LAYOUT COMMANDMENTS 6.Set layoutX/layoutY for stable layout position and translateX/translateY for animation or adjustments. 7.Remember layoutX/layoutY are offsets, not final location. 8.LayoutInfo is only relevant when set on a Node that has a Container as its parent, otherwise it's ignored. 9.If you need to control the size of a Resizable inside a Container, then either bind its width/height or override its preferred size using LayoutInfo. or unmanage it. 10.If you want a Resizable to automatically resize when its preferred size changes, then place it in a Container. 7.5
  • TEAM EFFORT
  • JOIN THE TEAM We’re hiring! Senior Software Engineer/Text/UI-Controls contact: brian.beck@sun.com Senior Software Engineer/Graphics/OpenGL/shaders Senior Software Engineer/Text/Unicode/bi-di/OpenType contact: srividhya.Narayanan@sun.com
  • THE END.

Comments

Michal Margiel 5 years ago
The sound is mutch better :)
Michal Margiel 5 years ago
to be honest JavaFX Eclipse plugin is more or less useless right now :(
Michal Margiel 5 years ago
You can unit test JavaFX just with JUnit you do not need any additional libraries.

Login to add comments!