An Overview of ShowFruits.java
How ShowFruits.java works
ShowFruits.java starts with import statements for each used class. Note that we have explicitly mentioned each class (rather than using java.awt.* and so on). Explicitly mentioning a required class is always better than importing the entire package using the '*' wildcard.
//Used store and draw the Fruit Image import java.awt.Toolkit; import java.awt.Image; import java.awt.Graphics; import java.awt.Dimension; //Used for laying out components on the container import java.awt.GridBagLayout; import java.awt.GridBagConstraints; //Arraylist stores the fruits and their properties import java.util.Arrays; import java.util.ArrayList; import java.util.ListIterator; //Used to access input file import java.io.BufferedReader; import java.io.FileReader; import java.io.File; //Swing components used in the program import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JApplet; import javax.swing.JFileChooser; //Used to provide OS-specific look and feel. import javax.swing.UIManager;
The signature of the ShowFruits applet and the field variable declarations follow the import statements. Of particular interest are boolean flag variables that are used to manage the slideRunner thread and maintain the index of the currently displayed slide.
public class ShowFruits extends JApplet {
//The currently displayed Slide
private volatile int currSlide = 0;
//Variable used to pause and restart threads
private volatile boolean keepRunning;
private boolean paused;
//Slide Cycling Thread
private Thread slideRunner;
The volatile modifier is used in concurrent applications to ensure data view consistency. Threads often maintain private working copies of shared variables in a memory area called a cache. The Java VM always obtains the current value of a volatile variable from shared memory rather than from a cache so that the most current value is used. In effect, volatile primitives cannot be cached.
The UIManager class is used to set the look and feel of our application. Currently, the windows ‘look and feel’ works only on windows XP. This feature is only available with the newer versions of J2SE
public void init() {
try {
//Customize Application to mimic windows look and feel
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception e) {
System.out.println(e.getMessage());
}
The following File chooser component pops up a 'Select Input File' dialogue. If the user selects a file and clicks 'OK', chooser.showOpenDialog(this) is set to APPROVE_OPTION. We only proceed with the applet if the user selects a file. Otherwise, System.exit(0) is used to quit the application.
If the user selects a file, the inputFile Object is set to the file selected by the user. The createSlides function is called with the inputFile File object as an input parameter. Note that we catch the custom InputFileFormatException (this Exception is thrown if the input file format is incorrect - refer to chapter 5). If the user selects the wrong type of file or an incorrectly formatted file, the application exits through the System.exit(0) call in the 'catch' block.
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Select Input File:");
int result = chooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
try {
File inputFile = chooser.getSelectedFile();
createSlides (inputFile);
}
catch (InputFileFormatException iffe) {
System.out.println(iffe.getMessage());
destroy();
System.exit(0);
}
}
else {
System.exit(0);
}
Now, let us take a look at how the components are positioned on the container using the GridBagLayout class. The following statement sets the layout of the ShowFruits JApplet. The statement is equivalent to 'this.setLayout(new GridBagLayout());'
//Layout set to GridBagLayout setLayout(new GridBagLayout());
A GridBagConstraints object is created and used to set component position. The 'gridx' option places the component in the column indicated by its value (0-first column, 1-second column and so on). The 'gridy' option places a component in the row indicated by its value (0-First row, 1-second row and so on). The 'fill' option is used to extend the length of a component to occupy the entire width of the window (VERTICAL extends the length to the fit entire length of the container). In our case, the fill option displays the property label and packs the rest of the row with spaces. The custom image panel is set to span two columns by setting gridwidth = 2. Note that the value of the property labels is set using the fruit at currSlide = 0 (the first position) in the array list. The name labels are set to the string literal containing the property names ('Name', 'Color' etc.)
//Constrain our components to a specific place in the container
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL; //Left Alignment
//'Name' JLabel: First Row, First Column
nameLabel.setText("Name: ");
c.gridx = 0;
c.gridy = 0;
//Add component and layout constraints to JApplet container
getContentPane().add(nameLabel,c);
//Fruit Name JLabel: First Row, Second Column
fruitName.setText(myFruits.get(currSlide).name);
c.gridx = 1;
c.gridy = 0;
//Add component and layout constraints to JApplet container
getContentPane().add(fruitName,c);
.
.
.
.
//Fruit calories JLabel: Third row, Second Column
fruitCalories.setText(String.valueOf(myFruits.get(currSlide).calories));
c.gridx = 1;
c.gridy = 2;
//Add component and layout constraints to JApplet container
getContentPane().add(fruitCalories,c);
.
.
.
.
//Fruit Image Custom Component: Entire Fourth Row
c.gridx = 0;
c.gridwidth = 2;
c.gridy = 3;
//Add component and layout constraints to JApplet container
getContentPane().add(imagePanel,c);
The startSlideRunner() function creates and starts the thread that iterates over the slides. This function is invoked at the very end of the applet's init() method.
startSlideRunner();
The createSlides method is an exact copy of the createFruits application from chapter 5 which used args[0] to get to the input file specified while issuing the 'Java' command. This file uses the file object (inputFile) that holds a reference to the input file selected by the user in the JFileChooser dialogue. If the file is of the wrong type or format, the custom InputFileFormatException is thrown by this method (and caught by the init() method). Also, a placeholder image (noimage.PNG) is stored in the image property for fruits whose image file cannot be found (CreateFruits set the string to 'No Image' by calling the three argument constructor).
if (imageFile.exists()) {
myFruits.add(new Fruit(fruitProperties[0],
Arrays.asList(fruitProperties[1].split(",")),
fruitProperties[2],
java.lang.Integer.parseInt(fruitProperties[3])));
}
else {
myFruits.add(new Fruit(fruitProperties[0],
Arrays.asList(fruitProperties[1].split(",")),
"noimage.PNG",
java.lang.Integer.parseInt(fruitProperties[3])));
}