Java FX 2.0 - A Developer's Guide
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Java FX 2.0 - A Developer's Guide

on

  • 16,364 views

A University talk about JavaFX 2.0 do

A University talk about JavaFX 2.0 do

Statistics

Views

Total Views
16,364
Views on SlideShare
13,656
Embed Views
2,708

Actions

Likes
7
Downloads
509
Comments
0

8 Embeds 2,708

http://www.developpez.net 1555
http://www.xenonique.co.uk 1135
http://translate.googleusercontent.com 8
http://paper.li 4
http://a0.twimg.com 2
http://webcache.googleusercontent.com 2
http://fiveholiday55.blogspot.com&_=1326662056395 HTTP 1
https://s4-eu4.ixquick-proxy.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Java FX 2.0 - A Developer's Guide Presentation Transcript

  • 1. JavaFX 2.0 – A Java Developers GuideStephen ChinChief Agile Methodologist, GXShttp://steveonjava.com @steveonjavaPeter PilgrimOracle Java Championhttp://www.xenonique.co.uk/blog/ @peter_pilgrim
  • 2. Meet the Presenters Stephen Chin Peter Pilgrim @steveonjava @peter_pilgrim Football, Music & Partner Family Man Motorcyclist
  • 3. This is a Participatory Session!>  Every now and then we will say something interesting… and it will go out in a tweet.>  Follow @steveonjava to watch the tweets and retweet the ones you like to your followers>  Feel free to create your own tweets using the hash tags "#JavaFX #Devoxx">  We have some goodies for folks who play. J 3
  • 4. At the end of the day, on the one hand we have computer systems, and on the other, people. Connecting them together, and allowing people to interact with computer systems in a compelling way, requires graphical user interfaces Chris Oliver, Creator of F3http://blogs.oracle.com/chrisoliver/entry/epitaph 4
  • 5. JavaFX 2.0 PlatformImmersive Desktop ExperienceCombining the Best of JavaFX and WebTechnologies>  Leverage your Java skills with modern JavaFX APIs>  Integrate Java, JavaScript, and HTML5 in the same application>  New graphics stack takes advantage of hardware acceleration for 2D and 3D applications>  Integrate in Swing applications using JFXPanel>  User your favorite IDE: NetBeans, Eclipse, IntelliJ, etc.
  • 6. DemoVideo Fracture
  • 7. Headline News >  RT @Stephan007: The #JavaOne11 channel on #Parleys passed 51.800 views! Most viewed: "Introduction to #JavaFX 2.0", close to 11,000 - @JavaFX4You, 9th November 2011 >  The most interesting announcement came when Oracle demoed both Java and JavaFX applications running on a Windows tablet, an iPad, and a "Linux-based tablet". - Eric Bruno, Dr Dobbs Journal, 5th October 2011 7
  • 8. Platform Support>  Windows fully supported l  For 3D you need one of the supported graphics cards (ATI, NVidia, Intel)>  Mac OS X preview available>  Linux Planned for end of 2012 8
  • 9. Windows OS RequirementsOperating System (32-Bit and 64-Bit) Browsers (32-Bit and 64-Bit)Windows XP Home and Professional Internet Explorer 7 and 8with Service Pack 3 ChromeWindows Vista Home Basic, Home Internet Explorer 7, 8, and 9Premium, Business, and Ultimate with Firefox 3.5, 3.6, and 4Service Pack 2 ChromeWindows 7 Home Premium, Internet Explorer 8 and 9Professional, and Ultimate Firefox 3.5, 3.6, and 4 Chrome 9
  • 10. Supported Graphics Cards Supported Graphics ProcessingGraphics Card Units (GPUs)NVIDIA Mobile GPUs: GeForce 8M and 100M series or higher, NVS 2100M series or higher, and Mobility Quadro FX 300M series or higher Desktop GPUs: GeForce 8 and 100 series or higher Workstation GPUs: Quadro FX 300 series or higherATI Mobile GPUs: Mobility Radeon HD 3000, 4000, and 5000 series Desktop GPUs: Radeon HD 2400, 3000, 4000, 5000, and 6000 seriesIntel Mobile GPUs: GMA 4500MHD and GMA HD Desktop GPUs: GMA 4500 and GMA HD 10
  • 11. Building JavaFX Applications>  Deploys in the browser or on desktop>  Pure Java APIs>  Includes builders for declarative construction>  Alternative languages can also be used for simpler UI creation l  GroovyFX l  ScalaFX l  Visage 11
  • 12. Hello Devoxx (Java Version)public  class  HelloDevoxx  extends  Application  {      public  static  void  main(String[]  args)  {          launch(args);      }      @Override      public  void  start(Stage  primaryStage)  {          primaryStage.setTitle("Hello  Devoxx");          Group  root  =  new  Group();   19 Lines        Scene  scene  =  new  Scene(root,  400,  250,  Color.ALICEBLUE);          Text  text  =  new  Text();   Characters 425        text.setX(105);          text.setY(120);          text.setFont(new  Font(30));          text.setText("Hello  Devoxx");          root.getChildren().add(text);                          primaryStage.setScene(scene);          primaryStage.show();      }  }   12
  • 13. Hello Devoxx (Builder Version)public  void  start(Stage  primaryStage)  {      primaryStage.setTitle("Hello  Devoxx");      primaryStage.setScene(SceneBuilder.create()          .width(400)          .height(250)          .fill(Color.ALICEBLUE)          .root(   25 Lines            GroupBuilder.create().children(              TextBuilder.create()                  .x(105)   418 Characters                .y(120)                  .text("Hello  Devoxx")                  .font(new  Font(30))                  .build()              ).build()          )      .build());      primaryStage.show();  }   13
  • 14. Hello Devoxx (GroovyFX Version)GroovyFX.start  {  primaryStage  -­‐>      def  sg  =  new  SceneGraphBuilder()      sg.stage(          title:  Hello  Devoxx,          show:  true)  {              scene(                  fill:  aliceblue,   17 Lines                width:  400,                  height:  250)  {   Characters 190                    text(                          x:  105,                          y:  120,                          text:  "Hello  Devoxx"                          font:  "30pt")                  }          }  }   14
  • 15. Hello Devoxx (ScalaFX Version)object  HelloDevoxx  extends  JFXApp  {      stage  =  new  Stage  {          title  =  "Hello  Devoxx"          width  =  400          height  =  250          scene  =  new  Scene  {              fill  =  BLUE   Lines 16            Text  {   163 Characters                x  =  105                  y  =  120                  text  =  "Hello  Devoxx"                  font  =  Font(size:  30)              }          }      }  }   15
  • 16. Hello Devoxx (Visage Version)Stage  {      title:  "Hello  Devoxx"      width:  400      height:  250      scene:  Scene  {          fill:  BLUE   14 Lines        content:  Text  {  Characters 129            x:  105              y:  120              text:  "Hello  Devoxx"              font:  Font  {size:  30pt}          }      }  }   16
  • 17. Architecture of JavaFX 2.0 JavaFX Public API Quantum Toolkit Prism Glass Media Web WinTk Engine Engine Java2D Open GL D3D Java Virtual Machine
  • 18. JavaFX 2.0FundamentalsProperties, Lists, Binding 18
  • 19. JavaFX 2.0 Properties>  Primitive Properties>  Object Properties>  FX List Collection Properties>  Properties are: l  Observable l  Lazy l  Type Safe 19
  • 20. Properties in Java Property Declarationpublic  class  Person  {      private  StringProperty  firstName;      public  void  setFirstName(String  val)  {          firstNameProperty().set(val);      }      public  String  getFirstName()  {          return  firstNameProperty().get();      }      public  StringProperty  firstNameProperty()  {            if  (firstName  ==  null)                firstName  =  new  SimpleStringProperty(this,  "firstName");          return  firstName;        }  }     20
  • 21. Properties in Javapublic  class  Person  {      private  StringProperty  firstName;      public  void  setFirstName(String  val)  {          firstNameProperty().set(val);      }      public  String  getFirstName()  {          return  firstNameProperty().get();   Setter Method    }   (Convenience)    public  StringProperty  firstNameProperty()  {            if  (firstName  ==  null)                firstName  =  new  SimpleStringProperty(this,  "firstName");          return  firstName;        }  }     21
  • 22. Properties in Javapublic  class  Person  {      private  StringProperty  firstName;   Getter Method    public  void  setFirstName(String  val)  {   (Convenience)        firstNameProperty().set(val);      }      public  String  getFirstName()  {          return  firstNameProperty().get();      }      public  StringProperty  firstNameProperty()  {            if  (firstName  ==  null)                firstName  =  new  SimpleStringProperty(this,  "firstName");          return  firstName;        }  }     22
  • 23. Properties in Javapublic  class  Person  {      private  StringProperty  firstName;      public  void  setFirstName(String  val)  {   Property Fetcher        firstNameProperty().set(val);   (Lazy Construction)    }      public  String  getFirstName()  {          return  firstNameProperty().get();      }      public  StringProperty  firstNameProperty()  {            if  (firstName  ==  null)                firstName  =  new  SimpleStringProperty(this,  "firstName");          return  firstName;        }  }     23
  • 24. Properties in Javapublic  class  Person  {      private  StringProperty  firstName;   Even Lazier…    public  void  setFirstName(String  val)  {          firstNameProperty().set(val);      }      public  String  getFirstName()  {          return  firstName  ==  null  ?  null  :  firstNameProperty().get();      }      public  StringProperty  firstNameProperty()  {            if  (firstName  ==  null)                firstName  =  new  SimpleStringProperty(this,  "firstName");          return  firstName;        }  }     24
  • 25. Properties in Javapublic  class  Person  {      private  StringProperty  firstName;      public  void  setFirstName(String  val)  {   With initial value        firstNameProperty().set(val);      }      public  String  getFirstName()  {          return  firstName  ==  null  ?  "Peter"  :  firstNameProperty().get();      }      public  StringProperty  firstNameProperty()  {            if  (firstName  ==  null)                firstName  =  new  SimpleStringProperty(this,  "firstName",  "Peter");          return  firstName;        }  }     25
  • 26. Demo PropertiesUndirectional, Bidirectional and Lazy binding in Java
  • 27. Listeners>  Change Listener l  Observable, old value, new value>  Invalidation Listeners l  Observable 2 7
  • 28. Property Listener Example IntegerProperty  temperature  =  new   SimpleIntegerProperty(0);     temperature.setOnChangeListener(        new  ChangeListener<Number>()  {            public  void  change(                  ObservableValue<?  Extends  Number>  ov,                Number  newValue,  Number  oldValue)            System.out.printf(“%f  %f  %f”,                ov.doubleValue(),  oldValue.doubleValue(),                newValue.doubleValue()      }   );   temperature.set(2);   temperature.set(4);     28
  • 29. Hey Ho! This is all just to say:var  temperature:  Integer  =  23  on  replace  oldValue  {      println("temperature={temperature},          oldValue={oldValue}");  }  //  Visage  (previously  JavaFX  Script)     29
  • 30. JavaFX 2.0 Observable Lists>  Extends java.util.List>  Includes addListener/removeListener>  For bulk operations use: l  addAll l  removeAll l  setAll>  Sort with l  FXCollections.sort() 30
  • 31. List Change Listenerlist.addListener(new  ListChangeListener<Item>()  {      public  void  onChanged(Change<Item>  c)  {          while  (c.next())  {              if  (c.wasPermutated())  {                  for  (int  i  =  c.getFrom();  i  <  c.getTo();  i++)  {                      int  newLoc  =  getPermutation(i);                      //  handle  move  (a.k.a.  sort)                  }              }  else  {                  for  (Item  removed  :  c.getRemoved())  {                      //  handle  removal                  }  for  (Item  added  :  c.getAddedSubList())  {                      //  handle  addition                  }  }}}});   31
  • 32. JavaFX 2.0 Observable Maps>  Extends java.util.Map  >  Includes addListener() / removeListener()  >  MapChangeListener   l  Supports add()  / remove()   l  Can get associated: l  Key l  Old Value l  New Value 32
  • 33. Binding in JavaFX 2.0>  One of the most important features of JavaFX 1.3 and prior>  Now supported via an API (that works with Observable Properties)>  Can handle most cases quite elegantly and with full type safety 33
  • 34. Binding in JavaFX 2.0Infix  Addition/Subtraction/Multiplication/Division:    heightProperty().bind(rect1.heightProperty().add(rect2.heig htProperty()));    Aggregate  Operators:    widthProperty().bind(Bindings.max(rect1.widthProperty(),   rect2.widthProperty(),  rect3.widthProperty()));    Conditional  Expressions:    strokeWidthProperty().bind(Bindings.when(hover).then(4).oth erwise(0));    Bidirectional  Binding:    heightProperty().bindBidirectional(rect1.heightProperty());   34
  • 35. Demo Image FractureLife is like a bowl of cherries
  • 36. “The Scene-graph is King.” 36
  • 37. Image View Node Image View 1 Image View Image 2 Image View 3 Image View 4 37
  • 38. Media View 1Media View 2 Media Player MediaMedia View 3 Media View 4 38
  • 39. Breaking Glass1.  Divide a bounded area into four triangles2.  Are we there yet?3.  For each triangle A.  Choose a random side and split it to two rectangles B.  Recursive descend the two triangles (step 2) 3 9
  • 40. Controls10 / 10 Break
  • 41. Button Control>  Simple Button Control>  Add and remove click event handler>  Support Cascading Style Sheets 41
  • 42. Button Codepublic  class  ButtonDemo  extends  AbstractDevoxxApplication  {      public  void  initialiseScene(Group  root)  {          VBox  vbox  =  VBoxBuilder.create().              spacing(25).layoutX(25).layoutY(25).build();          root.getChildren().add(vbox);          Button  red  =  ButtonBuilder.create()              .text("Red").style("-­‐fx-­‐base:  red;").build();          Button  yellow  =  ButtonBuilder.create()              .text("Yellow").style("-­‐fx-­‐base:  blue;")              .build();          Button  green  =  ButtonBuilder.create()              .text("Green").style("-­‐fx-­‐base:  green;")              .build();          vbox.getChildren().addAll(red,yellow,green);      }  /*  …  */  }   42
  • 43. TextField Control>  One line entry text field control>  Set OnAction event handler>  Can listen to individual key events>  Support Cascading Style Sheets 43
  • 44. TextField Demopublic  class  TextDemo  extends  AbstractDevoxxApplication  {      public  void  initialiseScene(Group  root)  {          VBox  vbox  =  VBoxBuilder.create().spacing(25)              .layoutX(25).layoutY(25).build();          root.getChildren().add(vbox);          TextField  red  =  TextFieldBuilder.create()              .text("Red  Devoxx")              .style("-­‐fx-­‐base:  red;  -­‐fx-­‐font-­‐size:  48pt;  ")              .build();          TextField  green  =  TextFieldBuilder.create()              .text("Green  Devoxx")              .style("-­‐fx-­‐base:  green;  -­‐fx-­‐font-­‐size:  24pt;  ")              .build();          vbox.getChildren().addAll(red,green);      }  /*….  */  }   44
  • 45. ListView Control>  Displays a list of items>  Reuses cells for performance>  Default cell renderer can display Strings>  For other object types l  Create custom CellFactory 45
  • 46. ListView Codepublic  void  initialiseScene(Group  root)  {                VBox  vbox  =  VBoxBuilder.create()    .spacing(25).layoutX(25).layoutY(25).build();                root.getChildren().add(vbox);                  final  ListView<String>  listView  =        new  ListView<String>();              listView.setItems(FXCollections.observableArrayList(                      "Manchester  City",  "Manchester  United",                        "Newcastle  United",  "Chelsea",                        "Tottenham  Hotspur",  "Liverpool",  /*  …  */  ));                    listView.getSelectionModel()                        .setSelectionMode(SelectionMode.MULTIPLE);                vbox.getChildren().addAll(listView);          }          /*…*/  }   46
  • 47. TreeView Control>  Displays a tree view of items>  Can dynamically populate as tree is expanded>  Default cell renderer can display Strings>  For other object types l  Create custom CellFactory 47
  • 48. TreeView Codepublic  void  initialiseScene(Group  root)  {      /*…*/      TreeView  treeView  =  TreeViewBuilder.<String>create().root(          TreeItemBuilder.<String>create().value("JVM  Languages").children(              TreeItemBuilder.<String>create().value("Static").children(                  new  TreeItem<String>("Scala"),                  new  TreeItem<String>("Java"),                  new  TreeItem<String>("Visage"),  /*…*/              ).build(),              TreeItemBuilder.<String>create().value("Dynamic").children(                  new  TreeItem<String>("Clojure"),                  new  TreeItem<String>("Groovy"),  /*…*/              ).build(),  /*…*/          ).build()      ).build();      /*…*/  }   48
  • 49. ToolBar Demo>  Toolbar displays items horizontally or vertically>  Handles arbitrary number of items>  Can add any node to the toolbar, including custom nodes>  Automatic adds an overflow hint if cannot display all toolbar items 49
  • 50. Toolbar Codepublic  class  ToolBarDemo  extends  AbstractDevoxxApplication  {      public  void  initialiseScene(Group  root)  {          /*…*/          ToggleGroup  amFm  =  new  ToggleGroup();          ToolBar  toolBar  =  ToolBarBuilder.create().items(              new  Label("Volume"),              new  Slider(),              new  ToggleButton("Loudness"),              RadioButtonBuilder.create()                  .text("AM").toggleGroup(amFm).build(),              RadioButtonBuilder.create()                  .text("FM").toggleGroup(amFm).build(),              new  Button("Tune")          ).build();          vbox.getChildren().addAll(toolBar);  }}   50
  • 51. Progress Bars and Indicators>  Progress Bars can be laid out vertically and horizontally>  Progress Indicator is a circular widget>  Progress Bar and Indicator can be indefinite>  Double property 0.0 <= x <= 1.0 51
  • 52. Progress Bar/Indicator CodeVBoxBuilder.create().spacing(10)      .alignment(Pos.CENTER).children(      new  ProgressBar(),      new  ProgressBar(.25),      new  ProgressIndicator(.75)  ).build()   52
  • 53. Accordion Control>  Accordion is a space saving components>  Accordian accept only TitledPanes>  Each TitledPane managed one scenegraph node>  Can choose the titled pane expanded by default 53
  • 54. Accordion CodeString  imageUrl  =  new  Image(ResizableImageViewDemo.class.      getResource("images/Squirrel.jpg").toExternalForm();    Accordion  accordion  =  AccordionBuilder.create().panes(      new  TitledPane("Button",  new  Button("Regular")),      new  TitledPane("ToggleButton",  new  ToggleButton("Toggle")),          imagePane  =  new  TitledPane("SquirrelImage",                                      ImageViewBuilder.create().image(imageUrl,                                      500,  500,  true,  true,  false)          ).build()      )  ).build()   54
  • 55. TabPane Control>  Alternative space saving component>  Choose a side for the Tab>  Each manages one component node>  Tab pane has two modes: l  floating l  recessed 55
  • 56. TabPane CodeAccordion  accordion;      TitledPane  imagePane;      TabPane  tabPane  =  TabPaneBuilder.create().tabs(          TabBuilder.create().text("Progress").content(              /*  Progress  Tab  Here  */          ).build(),          TabBuilder.create().text("Accordion").content(              /*  Accordion  Tab  Here  */          ).build()      ).build();  accordion.setExpandedPane(imagePane);  vbox.getChildren().addAll(tabPane);   56
  • 57. JavaFXComponents 57
  • 58. Pens, papers and inks, first! 58
  • 59. A component is anencapsulation that bindsa set of nodes with theoperations that act onthose nodes 59
  • 60. Component Types>  Non-Resizable l  javafx.scene.shape.Shape   l  javafx.scene.Group  >  Resizable l  javafx.scene.layout.Pane   l  javafx.scene.layout.Region   l  javafx.scene.layout.Control   6 0
  • 61. Group based>  Particularly using the JavaFX primitive nodes under javafx.scene.shape.*;>  Design is not meant to be resized>  Layout is fixed at construction / initialisation time 6 1
  • 62. Region based>  Region is the sub class of javafx.scene.Parent  >  Adds the ability to resize a node, layout a particular set of children 6 2
  • 63. Control based>  Allows particular subclass of Region that permits skinning of node with a stylesheet>  You need 3 classes l  The Control java.scene.control.Control l  The Skinnable java.scene.control.Skinnable l  The Behaviour com.sun.javafx.scene.control.BehaviorBase 6 3
  • 64. Control based contd>  See JFXtras.org for more examples of skinnable components written for JavaFX 2.0>  Define CSS file referencing a Skin class>  A Skin is a Region (JavaFX 2.0)com.sun.javafx.scene.control.BehaviorBase  com.sun.javafx.scene.control.SkinBase   6 4
  • 65. Skins are StackPanes>  SkinBase is a type of StackPane  >  A StackPane is a Region>  Idea of shuffling a deck of cards and only showing one 6 5
  • 66. Layout Children Methodvoid  layoutChildren()  >  Layout all of the children of a Region based component>  Compute the preferred width and height of the children (first)>  May cache those values internally as part of the component or use Constraints 6 6
  • 67. Layout Boundaries>  SDK will make a request to find the best width and height for your component.>  Your component must do the same for its children>  Implement at least computePrefWidth and computePrefHeight methods! 6 7
  • 68. Layout Methodsprotected  double  computePrefHeight(double  w)  {            return  child.prefHeight(w)  +  2  *ht;  }    protected  double  computePrefWidth(double  h)  {          return  child.prefWidth(h)  +  2  *  wt;  }   6 8
  • 69. Pedantic CompleteImplement the other four methods too>  computeMinWidth(v)>  computeMinHeight(v)>  computeMaxWidth(v)>  computeMaxHeight(v) 6 9
  • 70. DemoCustom Border Layout
  • 71. Writing JavaFX Containers
  • 72. Event Input on Node>  Mouse Events fall under the pointer iteraction (single) l  Clicked, Entered, Exited, Pressed, Dragged, Released, Wheel l  Target Entered, Target Exited>  Key Events l  Clicked, Pressed, Released
  • 73. Event Input on Node>  It is possible to consume an event and stop it bubbling further down the scene-graph hierarchy>  You can block a component from event interaction>  Using clever tricks (opaque shape) you can implement dialog / focus / modal behaviour
  • 74. LayoutSecrets
  • 75. Reuse SDK Methods 75
  • 76. Some SDK Methods>  Region#layoutInArea()  >  Region#positionInArea()  >  Node#resize()  >  Node#resizeRelocate()  
  • 77. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane>  StackPane>  TilePane>  GridPane
  • 78. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane>  StackPane>  TilePane>  GridPane
  • 79. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane>  StackPane>  TilePane>  GridPane
  • 80. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane>  StackPane>  TilePane>  GridPane
  • 81. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane A B C>  StackPane>  TilePane D E>  GridPane
  • 82. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane>  StackPane>  TilePane>  GridPane
  • 83. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane>  StackPane>  TilePane>  GridPane
  • 84. Built-in Layouts>  AnchorPane>  BorderPane>  VBox/HBox>  FlowPane>  StackPane>  TilePane>  GridPane
  • 85. DemoBox Container Pane
  • 86. Tips>  When Dragging a Node prefer to translate (X,Y)>  When Laying Out a Node prefer to set the position with layoutX, layoutY>  Given steady state conditions you can swap layout (X,Y) for translation (X,Y)
  • 87. Tips 2>  Use blocking boolean flags to prevent conflict when you animating children in a container>  If you are unsure about MT concurrency you can always use AtomicBoolean>  Prefer JavaFX Properties
  • 88. Displaying HTML in JavaFX 88
  • 89. Displaying HTML in JavaFXpublic  class  WebViewDemo  extends  Application  {          public  static  void  main(String[]  args)  {                  launch(args);          }          @Override  public  void  start(Stage  stage)  {                  WebView  webView  =  new  WebView();                  webView.getEngine().load("http://devoxx.com");                  Scene  scene  =  new  Scene(webView);                  stage.setScene(scene);                  stage.setTitle("Web  View  Demo");                  stage.show();  }}   89
  • 90. For more info on Web/HTML5… Moving to the Client – JavaFX and HTML5 Stephen Chin Kevin Nilson Thursday 10:50 AM, Room 7 10 / 10 Break 90
  • 91. JavaFX With Groovy
  • 92. Features of Groovy>  Modern language l  Closures l  AST Transforms l  Strongly typed dynamic language>  Tight integration with Java l  Very easy to port from Java to Groovy>  Declarative syntax with GroovyFX Builders l  Familiar to Groovy and JavaFX Script developers
  • 93. Java vs. GroovyFX DSLpublic  class  HelloStage  extends  Application  {   GroovyFX.start  {  stage  -­‐>        def  sg  =  new  SceneGraphBuilder(stage)      public  void  start(Stage  stage)  {            stage.setTitle("Hello  Stage");      sg.stage(title:  “Hello  Stage”,  width:  600,          stage.setWidth(600);   height:  450)  {          stage.setHeight(450);          scene(fill:  groovyblue)  {          Scene  scene  =  new  Scene();              rectangle(x:  25,  y:  40,  width:  100,          scene.setFill(Color.LIGHTGREEN);   height:  50,  fill:  red)   21 Lines        Rectangle  rect  =  new  Rectangle();          rect.setX(25);   8 Lines        }      }   430 Characters        rect.setY(40);          rect.setWidth(100);          rect.setHeight(50);   }     180 Characters        rect.setFill(Color.RED);          scene.setRoot(new  Group(rect));          stage.setScene(scene);          stage.show();      }        public  static  void  main(String[]  args)  {          launch(HelloStage.class,  args);      }  }   93
  • 94. Disappearing Circles in Groovydef  sg  =  new  SceneGraphBuilder()  def  hc  =  {  hover  -­‐>  hover  ?  4  :  0  }    sg.stage(title:  Vanishing  Circles,  show:  true)  {      scene(fill:  black,  width:  800,  height:  600)  {          50.times  {              circle(centerX:  rand(800),  centerY:  rand(600),                              radius:  150,  stroke:  white,                              strokeWidth:  bind(hover,  converter:  hc))  {                  fill  rgb(rand(255),  rand(255),  rand(255),  0.2)                  effect  boxBlur(width:  10,  height:  10,  iterations:  3)              }          }      }  }   94
  • 95. Disappearing Circles in Groovydef  sg  =  new  SceneGraphBuilder()  def  hc  =  {  hover  -­‐>  hover  ?  4  :  0  }    sg.stage(title:  Vanishing  Circles,  show:  true)  {      scene(fill:  black,  width:  800,  height:  600)  {   Builder for GroovyFX scene        50.times  {   graphs            circle(centerX:  rand(800),  centerY:  rand(600),                              radius:  150,  stroke:  white,                              strokeWidth:  bind(hover,  converter:  hc))  {                  fill  rgb(rand(255),  rand(255),  rand(255),  0.2)                  effect  boxBlur(width:  10,  height:  10,  iterations:  3)              }          }      }  }     95
  • 96. Disappearing Circles in Groovydef  sg  =  new  SceneGraphBuilder()  def  hc  =  {  hover  -­‐>  hover  ?  4  :  0  }    sg.stage(title:  Vanishing  Circles,  show:  true)  {      scene(fill:  black,  width:  800,  height:  600)  {          50.times  {              circle(centerX:  rand(800),  centerY:  rand(600),     Declarative Stage                          radius:  150,  stroke:  white,                              strokeWidth:  bind(hover,  converter:  hc))  {   definition                fill  rgb(rand(255),  rand(255),  rand(255),  0.2)                  effect  boxBlur(width:  10,  height:  10,  iterations:  3)              }          }      }  }   96
  • 97. Disappearing Circles in Groovydef  sg  =  new  SceneGraphBuilder()  def  hc  =  {  hover  -­‐>  hover  ?  4  :  0  }     Inline propertysg.stage(title:  Vanishing  Circles,  show:  true)  {   definitions    scene(fill:  black,  width:  800,  height:  600)  {          50.times  {              circle(centerX:  rand(800),  centerY:  rand(600),                              radius:  150,  stroke:  white,                              strokeWidth:  bind(hover,  converter:  hc))  {                  fill  rgb(rand(255),  rand(255),  rand(255),  0.2)                  effect  boxBlur(width:  10,  height:  10,  iterations:  3)              }          }      }  }   97
  • 98. Disappearing Circles in Groovydef  sg  =  new  SceneGraphBuilder()  def  hc  =  {  hover  -­‐>  hover  ?  4  :  0  }   Bind to properties  sg.stage(title:  Vanishing  Circles,  show:  true)  {      scene(fill:  black,  width:  800,  height:  600)  {          50.times  {              circle(centerX:  rand(800),  centerY:  rand(600),                              radius:  150,  stroke:  white,                              strokeWidth:  bind(hover,  converter:  hc))  {                  fill  rgb(rand(255),  rand(255),  rand(255),  0.2)                  effect  boxBlur(width:  10,  height:  10,  iterations:  3)              }          }      }  }   98
  • 99. Disappearing Circles in Groovydef  sg  =  new  SceneGraphBuilder()  def  hc  =  {  hover  -­‐>  hover  ?  4  :  0  }     Sequence Creation Viasg.stage(title:  Vanishing  Circles,  show:  true)  {   Loop    scene(fill:  black,  width:  800,  height:  600)  {          50.times  {              circle(centerX:  rand(800),  centerY:  rand(600),                              radius:  150,  stroke:  white,                              strokeWidth:  bind(hover,  converter:  hc))  {                  fill  rgb(rand(255),  rand(255),  rand(255),  0.2)                  effect  boxBlur(width:  10,  height:  10,  iterations:  3)              }          }      }  }   99
  • 100. Properties in Javapublic class Person {! private StringProperty firstName;! public void setFirstName(String val) { firstNameProperty().set(val); }! public String getFirstName() { return firstNameProperty().get(); }! public StringProperty firstNameProperty() { ! if (firstName == null) ! firstName = new SimpleStringProperty(this, "firstName");! return firstName; ! }! ! private StringProperty lastName;! public void setLastName(String value) { lastNameProperty().set(value); }! public String getLastName() { return lastNameProperty().get(); }! public StringProperty lastNameProperty() { ! if (lastName == null) // etc.! } !}!   100
  • 101. Properties in GroovyFXpublic class Person {! @FXBindable String firstName; ! @FXBindable String lastName;!}!   101
  • 102. Properties in GroovyFXpublic class Person {! @FXBindable String firstName; ! @FXBindable String lastName = “Smith”;!}!   Optional initializers 102
  • 103. Properties in GroovyFXpublic class Person {! @FXBindable String firstName; ! @FXBindable String lastName = “Smith”;!}!!def p = new Person()!def last = p.lastName! Get and set valuesp.firstName = “Agent”!! 103
  • 104. Properties in GroovyFXpublic class Person {! @FXBindable String firstName; ! @FXBindable String lastName = “Smith”;!}!!def p = new Person()!def last = p.lastName! Access underlyingp.firstName = “Agent”! property for binding!textField(text: bind(p.lastNameProperty()))!! 104
  • 105. Binding in GroovyFX@FXBindable  class  Time  {      Integer  hours      Integer  minutes      Integer  seconds        Double  hourAngle      Double  minuteAngle      Double  secondAngle        public  Time()  {          //  bind  the  angle  properties  to  the  clock  time          hourAngleProperty().bind((hoursProperty()  *  30.0)  +   (minutesProperty()  *  0.5))          minuteAngleProperty().bind(minutesProperty()  *  6.0)          secondAngleProperty().bind(secondsProperty()  *  6.0)      }  }   105
  • 106. Animation in GroovyFXtimeline(cycleCount:  Timeline.INDEFINITE,   autoReverse:  true)  {      at  (1000.ms)  {          change(rect1,  x)  to  200  tween  ease_both          change  rect2.yProperty()  to  200  tween  linear      }  }.play()   106
  • 107. Animation in GroovyFXtimeline(cycleCount:  Timeline.INDEFINITE,   autoReverse:  true)  {      at  (1000.ms)  {          change(rect1,  x)  to  200  tween  ease_both          change  rect2.yProperty()  to  200  tween  linear      }  }.play()   Easy animation syntax: at (duration) {keyframes} 107
  • 108. Animation in GroovyFXtimeline(cycleCount:  Timeline.INDEFINITE,   autoReverse:  true)  {      at  (1000.ms)  {          change(rect1,  x)  to  200          change  rect2.yProperty()  to  200      }  }.play()   Key frame DSL 108
  • 109. Animation in GroovyFXtimeline(cycleCount:  Timeline.INDEFINITE,   autoReverse:  true)  {      at  (1000.ms)  {          change(rect1,  x)  to  200  tween  ease_both          change  rect2.yProperty()  to  200  tween  linear      }  }.play()   Optional easing 109
  • 110. Event Listeners in GroovyFX>  Supported using the built-in Closure syntax>  Optional arguments for event objectsonMouseClicked  {  e  -­‐>      timeline  {          at(3.s)  {  change   e.source.radiusProperty()  to  0  }      }.play()  }   110
  • 111. Event Listeners in GroovyFXSupported  using  the  built-­‐in  Closure  syntax  Optional  arguments  for  event  objects  onMouseClicked  {  MouseEvent  e  -­‐>      timeline  {          at(3.s)  {  change   e.source.radiusProperty()  to  0  }      }.play()  }   Compact syntax {body} 111
  • 112. Event Listeners in GroovyFXSupported  using  the  built-­‐in  Closure  syntax  Optional  arguments  for  event  objects   Optional event parameter {event -> body}onMouseClicked  {  MouseEvent  e  -­‐>      timeline  {          at(3.s)  {  change   e.source.radiusProperty()  to  0  }      }.play()  }   112
  • 113. TableView in JavaObservableList<Person>  items  =  ...  TableView<Person>  tableView  =  new  TableView<Person>(items);      TableColumn<Person,String>  firstNameCol  =                    new  TableColumn<Person,String>("First  Name");    firstNameCol.setCellValueFactory(                  new  Callback<CellDataFeatures<Person,  String>,                                              ObservableValue<String>>()  {      public  ObservableValue<String>  call(CellDataFeatures<Person,  String>  p)        {          return  p.getValue().firstNameProperty();      }  });      tableView.getColumns().add(firstNameCol);   113
  • 114. TableView in GroovyFXdef  dateFormat  =  new  SimpleDateFormat("yyyy-­‐MM-­‐dd");    tableView(items:  persons)  {      tableColumn(property:  "name",  text:  "Name",  prefWidth:  150)      tableColumn(property:  "age",  text:  "Age",  prefWidth:  50)      tableColumn(property:  "gender",  text:  "Gender",  prefWidth:  150)      tableColumn(property:  "dob",    text:  "Birth",  prefWidth:  150,                                type:  Date,                              converter:  {  from  -­‐>  return  dateFormat.format(from)  })  }   114
  • 115. Layout in JavaTextField  urlField  =  new  TextField(“h_p://www.google.com”);  HBox.setHgrow(urlField,  Priority.ALWAYS);    HBox  hbox  =  new  HBox();  hbox.getChildren().add(urlField);    WebView  webView  =  new  WebView();  VBox.setVgrow(webView,  Priority.ALWAYS);    VBox  vbox  =  new  VBox();  vbox.getChildren().addAll(hbox,  webView);   115
  • 116. Layout in GroovyFXsg.stage(title: "GroovyFX WebView Demo", show: true) { scene(fill: groovyblue, width: 1024, height: 800) { vbox { hbox(padding: 10, spacing: 5) { textField(“http://www.yahoo.com”, hgrow: "always") button("Go”) } webView(vgrow: "always") } }} 116
  • 117. Layout in GroovyFX 117
  • 118. Layout in GroovyFXgridPane(hgap: 5, vgap: 10, padding: 25) {! columnConstraints(minWidth: 50, halignment: "right")! columnConstraints(prefWidth: 250)! label("Send Us Your Feedback", font: "24pt sanserif", ! row: 0, columnSpan: GridPane.REMAINING, halignment: "center",! margin: [0, 0, 10])!! label("Name: ", row: 1, column: 0)! textField(promptText: "Your name", row: 1, column: 1, hgrow: always)!! label("Email:", row: 2, column: 0)! textField(promptText: "Your email", row: 2, column: 1, hgrow:always)!! label("Message:", row: 3, column: 0, valignment: "baseline")! textArea(row: 3, column: 1, hgrow: "always", vgrow: "always")!! button("Send Message", row: 4, column: 1, halignment: "right")!}! 118
  • 119. Layout in GroovyFX 119
  • 120. GroovyFX Supports… 120
  • 121. GroovyFX Supports… 121
  • 122. JavaFX With Scala 122
  • 123. What is Scala 2001 2006 •  Scala Started •  Scala v2.0 2003/2004 2011 •  Scala v1.0 •  Scala 2.9.0 (latest)>  Started in 2001 by Martin Odersky>  Compiles to Java bytecodes>  Pure object-oriented language>  Also a functional programming language 123
  • 124. Why Scala?>  Shares many language features with JavaFX Script that make GUI programming easier: l  Static Type Checking – Catch your errors at compile time l  Closures – Wrap behavior and pass it by reference l  Declarative – Express the UI by describing what it should look like>  Scala also supports Type Safe DSLs! l  Implicit Conversions – type safe class extension l  Operator Overloading – with standard precedence rules l  DelayedInit / @specialized – advanced language features 124
  • 125. Java vs. Scala DSLpublic  class  HelloStage  extends  Application  {   object  HelloJavaFX  extends  JFXApp  {        stage  =  new  Stage  {      public  void  start(Stage  stage)  {          title  =  "Hello  Stage"          stage.setTitle("Hello  Stage");          width  =  600          stage.setWidth(600);          height  =  450          stage.setHeight(450);          scene  =  new  Scene  {          Scene  scene  =  new  Scene();              fill  =  LIGHTGREEN          scene.setFill(Color.LIGHTGREEN);              content  =  Seq(new  Rectangle  {   21 Lines        Rectangle  rect  =  new  Rectangle();          rect.setX(25);          rect.setY(40);   17 Lines                x  =  25                  y  =  40                  width  =  100   430 Characters        rect.setWidth(100);          rect.setHeight(50);   177 Characters                height  =  50                  fill  =  RED          rect.setFill(Color.RED);              })          scene.setRoot(new  Group(rect));          }          stage.setScene(scene);      }          stage.show();   }      }        public  static  void  main(String[]  args)  {          launch(HelloStage.class,  args);      }  }   125
  • 126. Disappearing Circles in ScalaFXobject  DisappearingCircles  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)              }          }      }  }  
  • 127. Disappearing Circles in ScalaFXobject  DisappearingCircles  extends  JFXApp  {      stage  =  new  Stage  {          title  =  "Disappearing  Circles"          width  =  800          height  =  600   Base class for JavaFX        scene  =  new  Scene  {   applications            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)              }          }      }  }     127
  • 128. Disappearing Circles in ScalaFXobject  DisappearingCircles  extends  JFXApp  {      stage  =  new  Stage  {          title  =  "Disappearing  Circles"          width  =  800   Declarative Stage        height  =  600          scene  =  new  Scene  {   definition            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)              }          }      }  }     128
  • 129. Disappearing Circles in ScalaFXobject  DisappearingCircles  extends  JFXApp  {      stage  =  new  Stage  {          title  =  "Disappearing  Circles"   Inline property        width  =  800          height  =  600   definitions        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)              }          }      }  }   129
  • 130. Disappearing Circles in ScalaFXobject  DisappearingCircles  extends  JFXApp  {      stage  =  new  Stage  {          title  =  "Disappearing  Circles"          width  =  800   Sequence Creation Via        height  =  600   Loop        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)              }          }      }  }   130
  • 131. Binding in ScalaInfix Addition/Subtraction/Multiplication/Division:height  <==  rect1.height  +  rect2.height    Aggregate Operators:width  <==  max(rect1.width,  rect2.width,  rect3.width)    Conditional Expressions:strokeWidth  <==  when  (hover)  then  4  otherwise  0    Compound Expressions:text  <==  when  (rect.hover  ||  circle.hover  &&  !disabled)   then  textField.text  +  "  is  enabled"  otherwise  "disabled"   131
  • 132. Animation in Scalaval  timeline  =  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          )      }  }  timeline.play();   132
  • 133. Animation in Scalaval  timeline  =  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          )      }  }  timeline.play();   JavaFX Script-like animation syntax: at (duration) {keyframes} 133
  • 134. Animation in Scalaval  timeline  =  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          )      }  }  timeline.play();   Operator overloading for animation syntax 134
  • 135. Animation in Scalaval  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();   Optional tween syntax 135
  • 136. Event Listeners in Scala>  Supported using the built-in Closure syntax>  Optional arguments for event objects>  100% type-safe onMouseClicked  =  {      Timeline(at(3  s){radius-­‐>0}).play()   }   136
  • 137. Event Listeners in ScalaSupported  using  the  built-­‐in  Closure  syntax  Optional  arguments  for  event  objects  100%  type-­‐safe   onMouseClicked  =  {      Timeline(at(3  s){radius-­‐>0}).play()   }   Compact syntax {body} 137
  • 138. Event Listeners in ScalaSupported  using  the  built-­‐in  Closure  syntax  Optional  arguments  for  event  objects  100%  type-­‐safe   Optional event parameter {(event) => body} onMouseClicked  =  {  (e:  MouseEvent)  =>      Timeline(at(3  s){radius-­‐>0}).play()   }   138
  • 139. About Project Visage>  “Visage is a domain specific language (DSL) designed for the express purpose of writing user interfaces.”>  Visage project goals: l  Compile to JavaFX Java APIs l  Evolve the Language (Annotations, Maps, etc.) l  Support Other Toolkits>  Come join the team!>  For more info: http://visage-lang.org/ 139
  • 140. How about JavaFX on… VisageStage  {      title:  "Hello  Stage"      width:  600      height:  450      scene:  Scene  {          fill:  Color.LIGHTGREEN          content:  Rectangle  {              x:  25              y:  40              width:  100              height:  50              fill:  Color.RED          }      }  }   140
  • 141. How about JavaFX on… VisageStage  {      title:  "Hello  Stage"      width:  600      height:  450      scene:  Scene  {          fill:  Color.LIGHTGREEN          content:  Rectangle  {              x:  25              y:  40              width:  100              height:  50              fill:  Color.RED          }      }  }   141
  • 142. How about JavaFX on… VisageStage  {      title:  "Hello  Stage"      width:  600      height:  450      Scene  {          fill:  Color.LIGHTGREEN          Rectangle  {              x:  25              y:  40              width:  100              height:  50              fill:  Color.RED          }      }  }   142
  • 143. Visage is JavaFX Script++>  Default Parameters>  New Literal Syntax For: l  Angles – 35deg,  4rad,  1turn   l  Colors – #DDCCBB,  #AA33AA|CC   l  Lengths – 5px,  2pt,  3in,  4sp  >  Null-check Dereference l  var width = rect!.width>  Built-in Bindable Maps (coming soon!) l  var fruitMap = ["red" : apple, "yellow" : banana] l  var fruit = bind fruitMap["red"] 143
  • 144. Visage and JavaFX 2.0 are made foreach other…>  Enhanced Binding l  Retains lazy evaluation properties with additional expressive power>  Integrated Collections l  Sequences and Maps automatically convert between JavaFX Observable Lists/Maps>  Built-in Animation Syntax l  Ties into JavaFX animation subsystem l  Provides consistent, clean APIs 144
  • 145. Conclusion>  You can write JavaFX applications in pure Java>  JavaFX is also usable in alternate languages>  You can get improved support using DSL libraries l  GroovyFX l  ScalaFX>  Or a dedicated UI JVM Language l  Visage
  • 146. Pro JavaFX 2 Platform Coming Soon! >  Coming 1st Quarter 2012 >  All examples rewritten in Java >  Covers the new JavaFX 2.0 APIs >  Will includes ScalaFX, GroovyFX, and Visage 146
  • 147. Professional Services peter.pilgrim@gmail.com JavaFX 2.0 in Enterprises Financial Services in London Training with Consultancy Technical Leadership 147
  • 148. JavaFX 2.0 – A Java Developers Guide Thank YouStephen Chin Peter PilgrimChief Agile Methodologist, GXS Oracle Java Championhttp://steveonjava.com @steveonjava http://www.xenonique.co.uk/blog/ @peter_pilgrim
  • 149. AttributionsAll images are Creative Commons (CC) License from Flickr.com– “You must attribute the source and you cannot change the content”Tim Ellis http://www.flickr.com/photos/tim_ellis/Lego Basics http://www.flickr.com/photos/tim_ellis/338755101/sizes/l/*PaysImaginaire* http://www.flickr.com/photos/nevrlndtink/Variable Plastic Bag http://www.flickr.com/photos/nevrlndtink/232906118/sizes/m/~Deiby http://www.flickr.com/photos/deiby/Expression http://www.flickr.com/photos/deiby/5489382677/sizes/l/Lisa Sinclair http://www.flickr.com/photos/plumandjello/fruit http://www.flickr.com/photos/plumandjello/2333263539/sizes/l/Nerovivo http://www.flickr.com/photos/dominik99/http://www.flickr.com/photos/dominik99/407716865/sizes/z/in/photostream/
  • 150. AttributionsAll images are Creative Commons (CC) License from Flickr.com– “You must attribute the source and you cannot change the content”.Guilty http://www.flickr.com/photos/roll_initiative/Arbitrary Function Generator http://www.flickr.com/photos/roll_initiative/3278642272/Loop Oh Rupert Grazer http://www.flickr.com/photos/loop_oh/Pattern at the Senckenberg Museum in Frankfurt am Main / Germany. http://www.flickr.com/photos/loop_oh/4571485915/Lili Vieira de Carvalho, Vancouver, Canada http://www.flickr.com/people/lilivc/Composition of Bowls http://www.flickr.com/photos/lilivc/367582911/sizes/l/Mykl Roventine http://www.flickr.com/people/myklroventine/19/365 Game Over http://www.flickr.com/photos/myklroventine/3210068573/sizes/l/superciliousness / Bentley Smith http://www.flickr.com/people/superciliousness/200510 carpenters tools - inside the workmans shed - all his old tools http://www.flickr.com/photos/superciliousness/57486288/
  • 151. AttributionsAll images are Creative Commons (CC) License from Flickr.com– “You must attribute the source and you cannot change the content”You’ve got a fast car http://www.flickr.com/photos/coreforce/5910961411/Core Force http://www.flickr.com/photos/coreforce/GAME AT MARBLES http://www.flickr.com/photos/9619972@N08/928099769/sizes/l/in/photostream/(Author unknown, from an antique childrens booLucs Game http://www.flickr.com/photos/9619972@N08/928099769/just.Luc http://www.flickr.com/people/9619972@N08/DIY Easel http://www.flickr.com/photos/68888883@N00/2744696593/in/photostream/Judy of the Woods http://www.flickr.com/people/68888883@N00/