Monday, June 22, 2009

MigLayout for JavaFX Reloaded

JavaFX 1.2 brought many changes to the layout system in the JavaFX runtime. MigLayout for JavaFX has been fully updated to take advantage of these changes. The updated version is part of JFXtras v0.5 that was released a short time ago.

Getting Started with MigLayout

The first thing you need to do is download JFXtras version 0.5 and add the JFXtras-0.5.jar file to your project. You will also need to obtain the MigLayout v3.7 (for Java) jar file. This jar file is included in the JFXtras source distribution (in the lib directory) or you can get it directly from the MigLayout site. One other thing you should always keep handy is the MigLayout cheat sheet.

Simple Test

There are several examples of MigLayout usage included in the source distribution of JFXtras. You will find them in the tests/org/jfxtras/scene/layout directory. Let's start by taking a look at one of them: MigSimpleTest.fx. The code for this example is listed below.
package org.jfxtras.scene.layout;

import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import org.jfxtras.scene.ResizableScene;

Stage {
    title: "Mig Centering Test"
    scene: ResizableScene {
        width: 200
        height: 200
        fill: Color.PINK

        content: MigLayout {
            constraints: "fill"
            width: 100
            height: 100
            layoutInfo: MigNodeLayoutInfo {
                constraints: "center"
            }
        }
    }
}

This example shows the use of the layoutInfo variable that was added to the Node base class in JavaFX v1.2. The LayoutInfo class was created to provide a generic way of specifying layout constraints for any node in the scene graph. A subclass of LayoutInfo called MigNodeLayoutInfo is used to hold the specialized constraints string for a node in a MigLayout container. In the example above, the only node constraint specified is center which will keep the rectangle centered in the container.

Note: The node constraints string in the JavaFX version of MigLayout has the same syntax as the component constraints in the Java version. In JavaFX, we deal in nodes instead of components. See the MigLayout cheat sheet for details.
In this particular case, the rectangle also stays centered in the scene thanks to the use of a JFXtras ResizableScene which automatically passes resize information on to the MigLayout container.

LayoutInfo Convenience Functions

Setting the layoutInfo variable with a new MigNodeLayoutInfo class every time can get tedious. There are two convenience functions in the MigLayout class that reduce the amount of typing required. The first is the nodeConstraints function. It has the following signature:
public function nodeConstraints( constraints:String ):MigNodeLayoutInfo

This function simply constructs and returns a new MigNodeLayoutInfo object with the given constraints string. This is a module-level function in the MigLayout.fx file and therefore needs to be imported like this:

import org.jfxtras.scene.layout.MigLayout.*;
Using this new function, you might rewrite the Rectangle literal above as:
Rectangle {
    width: 100
    height: 100
    layoutInfo: nodeConstraints( "center" )
}
The second convenience function is named migNode and is used to associate a constraints string with a scene graph node that was created at some other point in the program. The function's signature is:
public function migNode( node:Node, constraints:String ):Node
This function is also a module-level function located in the MigLayout.fx file and therefore can be imported into your program using the same import statement as the one given above for the nodeConstraints function. Rewriting our simple test code to use this function might look something like this:
package org.jfxtras.scene.layout;

import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import org.jfxtras.scene.ResizableScene;
import org.jfxtras.scene.layout.MigLayout.*;

var blackRect = Rectangle {
    width: 100
    height: 100
}

Stage {
    title: "Mig Centering Test"
    scene: ResizableScene {
        width: 200
        height: 200
        fill: Color.PINK

        content: MigLayout {
            constraints: "fill"
            content: [
                migNode( blackRect, "center" )
               // ... other nodes are added here ...
            ]
        }
    }
}
This form is recommended when you have a non-trivial scene graph declaration. It allows you to easily see what nodes are being managed by your container and what their constraints are.

Migrating from a Previous Version

Those who have used MigLayout in JavaFX 1.0 or 1.1 will notice a few changes:

  • The layout constraints are now set using the constraints variable instead of the layout variable. The old name conflicts with the new layout facilities in JavaFX 1.2.
  • The fitParent variable has been removed from the MigLayout class. If you want your MigLayout container to expand to fill the entire scene, just use JFXtras' ResizeableScene class as above.
  • The MigNode class has been removed since it is no longer needed. The new layoutInfo variable in the Node class gives us a way to associate constraints with a node.
  • Since the MigNode class (and therefore MigNode.fx) no longer exists, the migNode convenience function was moved to the MigLayout.fx file.

Wrap Up

The MigLayout container released in JFXtras 0.5 was much improved for JavaFX 1.2. It not only incorporates the new layout system, but several bugs were fixed and it is now compatible with the latest release of MigLayout. There are more details about this updated version of MigLayout for JavaFX in the just-released Pro JavaFX™ Platform book of which I was fortunate enough to be a co-author.

Lastly, Amy Fowler has also updated her wonderful article on JavaFX layout. I highly recommend it!

Friday, June 12, 2009

Creating XYCharts in JavaFX

Note: This entry is the second in a series on the new chart components in JavaFX 1.2. Like the first entry, this one is also an excerpt from the upcoming "Pro JavaFX Platform" book written by Jim Weaver, Stephen Chin, Weiqi Gao, and myself. You can find out more by clicking on the book image to the right of this page. -- Dean The remaining five types of charts are all meant to work with XY data. These charts are all subtypes of the XYChart base class. As such they are rendered against a background grid and include a horizontal and vertical axis. Bar Chart A bar chart plots data from a sequence of BarChart.Series objects. Each series contains a sequence of BarChart.Data objects that each contains the value (the height of the bar) and the category that the bar belongs to. Therefore a bar chart data object can be declared as shown in the following code snippet.
BarChart.Data {
  category: “Category Name”
  value: 42
}
The horizontal axis for the bar chart is of type CategoryAxis. The public categories sequence variable must contain category names that match those given to the BarChart.Data objects. The vertical axis of a bar chart is a ValueAxis. As of JavaFX 1.2, the only concrete implementation of a ValueAxis is the NumberAxis class. A NumberAxis represents a range of numeric values and has variables that allow for control over the appearance and the number of tick marks and label that are shown along the axis. A typical axis declaration for a bar chart is shown in the listing below. The code declares a category axis with three categories and a number axis that ranges from 0 to 100 with a labeled tick mark every 20 units for a total of 6 labels (counting the label at 0).
BarChart {
  categoryAxis: CategoryAxis {
    categories: [ "Category 1", "Category 2", "Category 3" ]
  }
  valueAxis: NumberAxis {
    label: "Y Axis"
    upperBound: 100
    tickUnit: 20
  }
}
The next listing shows a complete program that displays a bar chart showing the sales report for Acme, Incorporated from 2007 to 2009. The data to be displayed in the chart is defined at the top. Each year will be it’s own category. The data values are generated from the sales figures for each of the three products over the three different years. The vertical axis is a NumberAxis that shows the number of units sold and is defined such that it can accommodate the largest sales value. It will have a labeled tick mark every 1,000 units starting at 0 and ending at 3,000. This gives a total of four labels along the vertical axis. The public data variable accepts the sequence of BarChart.Series objects to plot. Each BarChart.Series object also has its own public data variable, which accepts a sequence of BarChart.Data objects. A for expression is used to generate the actual sequence of BarChart.Data objects from the sequences defined at the beginning of the listing. Finally, the categoryGap variable (highlighted) is used to increase the spacing between the yearly data to provide more differentiation between the categories. The complete source code is in the BarChartIntro.fx file from the ChartIntro example project.
def years = [ "2007", "2008", "2009" ];
def anvilsSold = [  567, 1292, 2423 ];
def skatesSold = [  956, 1665, 2559 ];
def pillsSold = [ 1154, 1927, 2774 ];

Stage {
  title: "Bar Chart Intro"
  scene: Scene {
    content: [
      BarChart {
        title: "Acme, Inc. Sales Report"
        titleFont: Font { size: 24 }
        categoryGap: 25
        categoryAxis: CategoryAxis {
          categories: years
        }
        valueAxis: NumberAxis {
          label: "Units Sold"
          upperBound: 3000
          tickUnit: 1000
        }
        data: [
          BarChart.Series {
            name: "Anvils"
            data: for (j in [0..<sizeof years]) {
              BarChart.Data {
                category: years[j]
                value: anvilsSold[j]
              }
            }
          }
          BarChart.Series {
            name: "Rocket Skates"
            data: for (j in [0..<sizeof years]) {
              BarChart.Data {
                category: years[j]
                value: skatesSold[j]
              }
            }
          }
          BarChart.Series {
            name: "Earthquake Pills"
            data: for (j in [0..<sizeof years]) {
              BarChart.Data {
                category: years[j]
                value: pillsSold[j]
              }
            }
          }
        ]
      }
    ]
  }
}
Caution If you forget to declare the axes for a XYChart then no data will show up on your chart. This is one of the few times you cannot rely on the default values of the chart. In the previous example, if the CategoryAxis or the ValueAxis were left undeclared then no bars would have been drawn on the sales chart. The resulting chart is shown in below. Much like the pie charts, the default look for bar charts is a clean, modern look with lighting and shading affects built right in. There is also a drop-in replacement for BarChart named BarChart3D, which can be used to give the chart a 3-dimensional appearance. Line and Area Charts A line chart can be constructed using the LineChart class. It can show one or more LineChart.Series objects each of which contain a sequence of LineChart.Data objects. This pattern should be familiar now. It is the same for an AreaChart, whose data variable accepts a sequence of AreaChart.Series objects each of which contain a sequence of AreaChart.Data objects. We will use these charts to plot the mathematical functions sine and cosine as shown here: The source code for this program can be found in the LineAreaChartIntro.fx file in the ChartIntro example project. To plot the sine and cosine functions, we need to create a horizontal axis that goes from 0 to 2π and a vertical axis that goes from -1.0 to 1.0. The listing below shows two functions that generate these NumberAxis objects. You may be wondering why these axis objects are being generated by functions rather than just declaring them as variables and reusing them for both charts. The answer lies in the fact that the Axis base class is derived from Node. Like any Node, an Axis can only appear in the scene graph once. Since we are not allowed share these axis objects between our two charts, we must write functions that create new axis objects each time they are called.
/**
 * An x axis that goes from 0 to 2*PI with labels every PI/2 radians.
 * The labels are formatted to display on 2 significant digits.
 */
function createXAxis() {
  NumberAxis {
    label: "Radians"
    upperBound: 2 * Math.PI
    tickUnit: Math.PI / 2
    formatTickLabel: function(value) {
      "{%.2f value}"
    }
  }
}

/**
 * A y axis that that goes from -1 to 1 with labels every 0.5 units.
 */
function createYAxis() {
  NumberAxis {
    upperBound: 1.0
    lowerBound: -1.0
    tickUnit: 0.5
  }
}
The createXAxis function illustrates the use of the formatTickLabel function variable to format the values that will be used as tick labels on the axis. In this case, we format the numbers to keep only two significant digits after the decimal point. In addition, the code to create the y-axis shows how to set a non-zero lower bound for an axis. The next code listing shows the code that creates the LineChart object and adds it to the scene. This code follows the same pattern we’ve seen before. LineChart has a public data variable that takes a sequence of LineChart.Series objects. In this case we have one sequence for the sine wave and one for the cosine wave. Within each series, the data sequence is populated by a range expression that generates the LineChart.Data objects, which correspond to the points along the sine or cosine curves. Since we can’t show any data without our axes, we use the createXAxis and createYAxis functions shown earlier. Normally a line chart will plot a symbol at each data point – a circle, triangle, square or some such shape. The lines of the chart then connect these symbols. In this case, we have so many data points that the symbols would obscure the lines. So we tell the chart not to generate the symbols by setting the showSymbols variable to false. Another default setting for line charts is for the data to cast a shadow on the chart’s background. Since this makes the lines of this particular chart more difficult to see we turn off the drop shadow by setting dataEffect to null.
function createAreaChart() {
  AreaChart {
    title: "Area Chart"
    translateX: 550
    xAxis: createXAxis()
    yAxis: createYAxis()
    data: [
      AreaChart.Series {
        name: "Sine Wave"
        data: for (rads in [0..2*Math.PI step 0.01]) {
          AreaChart.Data {
            xValue: rads
            yValue: Math.sin( rads )
          }
        }
      }
      AreaChart.Series {
        name: "Cosine Wave"
        data: for (rads in [0..2*Math.PI step 0.01]) {
          AreaChart.Data {
            xValue: rads
            yValue: Math.cos( rads )
          }
        }
      }
    ]
  }
}
Scatter and Bubble Charts Scatter and bubble charts are just like the other three XY charts we’ve looked at. The classes, ScatterChart and BubbleChart respectively, have a public data variable that accepts a sequence of series object. You guessed it: ScatterChart.Series and BubbleChart.Series. Each of these series has a public data variable that holds a sequence of their respective data objects: ScatterChart.Data and BubbleChart.Data. However, the BubbleChart.Data class also contains a radius variable that is used to set the size of the bubble for each data point. The figure below shows an example of a scatter chart and a bubble chart. In this program, the charts are just plotting the distribution of points generated by the javafx.util.Math.random function. Using the JavaFX math functions allow our code to remain portable to mobile devices. We will discuss that more in Chapter 10. The radius of the bubbles in the bubble chart is determined by the order in which the points are generated. The bubbles start small and get bigger as points are generated. You can roughly tell the order in which each point was generated. This is simply an interesting way to view the randomness of the random number generator. The code for this program is found in ScatterBubbleChartIntro.fx in the ChartIntro example project. The code to create a scatter chart is pretty straightforward. First we define a function that creates a number axis that ranges from 0.00 to 1.00 with labels every 0.25 units. The tick labels are once again formatted to keep only 2 digits after the decimal points. The createAxis function itself takes a String parameter to use as the label for the number axis. This allows the reuse of the function to create labels for both the x- and y-axes. The ScatterChart has only one data series and that series contains a sequence of 100 data objects whose x and y values are generated randomly. The only extra bit of customization done here is to hide the legend since we do only have the one data series.
/**
 * An x axis that goes from 0 to 1.0 and displays labels every 0.25 units.
 * The labels are formatted to display on 2 significant digits.
 */
function createAxis( label:String ) {
  NumberAxis {
    label: label
    upperBound: 1.0
    tickUnit: 0.25
    formatTickLabel: function(value) {
      "{%.2f value}"
    }
  }
}

/**
 * Create a scatter chart that displays random points.
 */
function createScatterChart() {
  ScatterChart {
    title: "Scatter Chart"
    legendVisible: false
    xAxis: createAxis( "X Axis" )
    yAxis: createAxis( "Y Axis" )
    data: [
      ScatterChart.Series {
        data: for (i in [1..100]) {
          ScatterChart.Data {
            xValue: Math.random()
            yValue: Math.random()
          }
        }
      }
    ]
  }
}
The source code that creates the bubble chart, shown below, is very similar. The big difference here is the radius variable of the BubbleChart.Data class. This is unique to the bubble chart data and, as previously mentioned, allows us to control the size of the bubble on the plot. The radius values scale based on the axes of the plot. In our case the axes both go from 0.0 to 1.0. Therefore a bubble radius of 0.5 would create a bubble that filled the entire chart (if centered) since its diameter would be 1.0. If our axes went from 0 to 10 instead, then a bubble with a radius of 0.5 would be smaller, taking up only 1/10 of the chart. The code in the listing below creates bubbles whose radius varies from 0.001 to 0.1 as the value of i goes from 1 to 100.
function createBubbleChart() {
  BubbleChart {
    title: "Bubble Chart"
    legendVisible: false
    translateX: 550
    xAxis: createAxis( "X Axis" )
    yAxis: createAxis( "Y Axis" )
    data: [
      BubbleChart.Series {
        data: for (i in [1..100]) {
          BubbleChart.Data {
            xValue: Math.random()
            yValue: Math.random()
            radius: i / 1000.0
          }
        }
      }
    ]
  }
}
This concludes part two of this series. In the third and final excerpt, we will take a look at adding interaction to your charts and all of the customization options available in the chart API.

Tuesday, June 9, 2009

Creating Charts in JavaFX

Note: This entry is an excerpt from the upcoming "Pro JavaFX Platform" book written by Jim Weaver, Stephen Chin, Weiqi Gao, and myself. You can find out more by clicking on the book image to the right of this page. -- Dean The chart components included in JavaFX give developers an easy way to let the users of their applications visualize a wide variety of data. There are six kinds of charts supported in JavaFX 1.2:
  • An Area Chart displays quantitative data like a line chart but with the area between the line and the horizontal axis shaded. Good for comparing the magnitude of two or more series of data.
  • The Bar Chart is a good way to show data in a way that makes it easy to see how the data changes over time or under a set of different conditions. The data is represented as rectangular area or, in the case of a 3D chart, a cubic volume whose height corresponds to the value of the data point being displayed.
  • Bubble Charts plot data points on a 2-dimensional grid and have the extra ability to display the relative magnitudes of the data by controlling the diameter of the point (or bubble) displayed at each XY coordinate.
  • A Line Chart is a simple way to display 2-dimensional data points where each point is connected to the next point in the data series by a line.
  • Pie Charts are typically used to display the relative percentages of a series of values on a circle. The value of each piece of data, as a percentage of the total, dictates how much of the circle’s area it takes up. In other words, the chart shows how big a slice of the pie each value represents.
  • The Scatter Chart is used to plot the points of one or more series of data. These charts are typically used to show the correlation (or not) of the data by comparing how the data points are clustered (or not).
One of these things is not like the others. Other than the pie chart, all of these charts are meant to handle 2-dimensional data points as pairs of XY coordinates. The class hierarchy, shown below, of the chart components in the javafx.scene.chart package reflects this fact. The ChartDemo program, which is included with the Chapter 5 examples and is shown below, displays an example of each of these types of charts. In the next sections we’ll take a look at how to use each of these different charts and the many different ways that they can be customized.
Click the image to launch the demo
Common Chart Properties The Chart abstract base class contains several public variables that are common to all charts. One such property that all charts share is a title. The following public variables in the Chart class control the style, position, and content of the title displayed on a chart.
  • title is a String whose contents will be displayed as the title of the chart. Setting this variable to null or an empty string (its default value) causes the chart to be rendered without a title.
  • titleFill controls the fill color of the title text. Since it is of type Paint, it can be a solid color as well as a linear or radial gradient.
  • titleFont allows you to set the Font to be used to render the title text.
  • titleGap is a Number that specifies the number of pixels to leave as a gap between the title and the content of the chart.
  • titleSide is an enumeration that specifies which side of the chart the title will appear on. Its type is javafx.scene.chart.part.Side and its possible values are TOP, BOTTOM, LEFT, and RIGHT.
All charts are also capable of displaying a legend. The legend is very useful when your charts are displaying more than one data series. It allows the user to easily see which of the plotted data points belongs to each of the data series. The public variables below affect how the legend is presented on a chart.
  • legendGap is a Number that specifies the number of pixels to leave as a gap between the legend and the content of the chart.
  • legendSide specifies which side of the chart the legend will appear on. Like titleSide, the possible values are TOP, BOTTOM, LEFT, and RIGHT.
  • legendVisible is a Boolean value that controls whether the legend will be shown on the chart or hidden.
The Chart class also has a public-read variable named legend that provides a reference to the actual Legend object used by the chart. This object can be used to customize many aspects of the legend and will be discussed later in the section on customization. Pie Chart Getting a basic pie chart on the screen is very straightforward. All you really need is a sequence of PieChart.Data objects and a title string. For each value that you want to display in your pie chart you just create a PieChart.Data object and supply the value and a text string to use as the label for the value. The sequence of data objects is then used in the pie chart’s declaration. Since every chart is-a Node, you can just insert the chart into your scene graph in order to display it. The listing below demonstrates how to create and display a PieChart. Notice that we have used the titleFont variable to make the chart’s title stand out a little more. The source code is from PieChartIntro.fx, which can be found in the ChartIntro example project.
Stage {
  title: "Pie Chart"
  scene: Scene {
    content: [
      PieChart {
        title: "What Is Your Favorite Pie?"
        titleFont: Font { size: 24 }
        data: [
          PieChart.Data {
            value: 21
            label: "Pumpkin"
          }
          PieChart.Data {
            value: 33
            label: "Apple"
          }
          PieChart.Data {
            value: 17
            label: "Cherry"
          }
          PieChart.Data {
            value: 29
            label: "3.14159"
          }
        ]
      }
    ]
  }
}
This chart is rendered as shown in the image below. Note that right out of the box, the charts have a modern look with lighting and shading effects baked right in. You can also see that by default the title appears at the top of the chart. If you prefer a more 3-dimensional look to your pie charts, you can use the PieChart3D class instead of a PieChart. Everything in the listing above (aside from the class name) can remain the same and the result will have the appearance of a 3-dimensional disk instead of a circle. That concludes this entry. In the next entry, I'll continue this excerpt by taking a closer look at XYCharts.

Sunday, June 7, 2009

Old Posts

For reference, here are some posts from my old blog at java.net and some of my posts from javafxpert.com: A Guide to the Future (of Swing Applications): March 19, 2007 Swing Application Framework Hacks Unleashed For Smarty Pantses: April 2, 2007 Beans Binding For Me? You Shouldn't Have: April 3, 2007 JavaFX Skins Game: January 1, 2009 Part 3 of Building a JavaFX Calculator: January 15, 2009 Spotlight Effects for JavaFX: January 25, 2009