|
This article takes a detailed look at Java 2DTM Cosmic Art Applet, Java 2DTM/Swing. Cosmic Art Applet was built with ForteTM for Java Community Edition. Forte offers a wide range of capabilities that make coding in the Java programming language faster and easier. At the end of the article, you can run the applet, and download the source code. But first, let's take a look at what the Cosmic Art Applet can do.
The Applet
Java 2D Cosmic Art Applet produces spiraling and unfolding patterns. The
Cosmic Art applet uses an algorithm based on ellipses that rotate about
their center point and scale according to a positive floating point scaling
factor (Scale Fac, on the GUI input). If the scaling factor is 1.0, there
is no change in the size of the ellipses. If the scaling factor is less than one, the ellipses decrease in size, and if it's greater than one, the ellipses increase in size. The upper left hand corner of the
Other input fields control the The Draw button kicks off the Java 2D scale, translate, rotate and draw operations. Draw can be invoked successively to overlay multiple patterns. The Erase button clears the draw panel, which can be used to terminate a Draw operation.
Below the input fields, there is a Before we jump into the development process and coding particulars, we need some background on Forte. About Forte CESun's Forte for Java, Community Edition is available for download at no charge. It is cross-platform compatible, enabling Java 2 development on Solaris, Linux, and Windows platforms. Forte CE is written entirely in the Java programming language and features extensive support of open standards. Key features of Forte CE:
Forte for Java, CE is part of a larger Forte family. Other products are scheduled for release later this year, including Forte for Java, Internet Edition and Forte for Java, Enterprise Edition.
Sun is gearing up to release Forte CE to the open source community under the Mozilla Public License model. The Mozilla model has been well-accepted by both open source groups and commercial software vendors. The modular "plug-in" nature of the Forte CE software makes it the perfect candidate for open source. The goal is to dramatically increase the level of innovation around development tools. This move marks the first step in a new open tools framework initiative, which will support developers, ISVs and application providers. This community will collaborate to produce a set of complementary and interoperable Java components. Complete set of Forte CE Open Source FAQs Getting Started With Forte CEFirst, download the software After you've installed and started up, create a new project, which gives a name to the current IDE state, which is stored for compilation, execution and debugging. To create a project, choose New Project from the Projects menu. A window appears for the new project name entry. I chose Ellipses--Second Generation. The system asks if you would like to start with a new file system or keep the old one--reply New. This choice provides a clean slate on which to construct your applet or application. You will then be prompted whether to save the current project. If you click Yes, any changes you have made to your current project will be saved before it is closed and a new project is opened. The file chooser will then appear prompting you to pick a directory to mount. The packages and files in any directories you mount will then be available for inclusion in your project. In our case, we chose Cancel because we will build the applet from the applet template. Using the TemplateSelect the Project tab in the Explorer window, then right-click on Project node (in my case Project Ellipses--Second Generation). Clicking on Add New, then clicking on the Classes node, brings up the window shown below.
I select Swing Forms, then
A The Form Editor window is located at lower left of the above screen. Let's expand the form window, and prepare to build some of the components required for the applet. To allow the Form Editor toolbar to be completely displayed, I right click on any free space within the toolbar, which brings up a checkbox list of toolbars. I deselect Edit and Debug, then pull all the toolbars to the left using their handles, depicted by raised vertical grooves. The result looks like the window shown below with the checkbox menu visible.
Now we're ready to click on the Absolute Layout icon from the Form Editor toolbar. The icons all have ToolTips, so just hover the mouse to check them out.
Absolute Layout is not part of the standard Java layout sets. It is a design aid
provided with the IDE, that allows components to be easily resized and moved by
dragging. This layout is particularly useful for prototyping. Property settings
are not required, and there are no formal limitations within the layout. But it
is not recommended for production applications or applets. The fixed location of
components in the form does not change, even when the environment changes. This
can lead to significant distortions in appearance on different platforms or under
changing look-and-feel environments. The solution is to design with Absolute
Layout, then when the design is complete, switch to Using the Component Palette is the easiest way to add new components. The Component Pallet is a toolbar on the Main Window, which holds commonly used visual components that you can add to the Form Editor window. Components are added by clicking on the component in the palette, then clicking on the desired location in the Form Editor window.
To build a display area for 2D graphics, I click on
The results of this operation are immediately reflected in the
Let's take a look at the
Once the basic components have been established in the Form Editor window, the
Component Inspector is used to fine tune things. First, the text labels need to
be changed. After selecting Component Inspector, from the top level View menu,
the text field of the properties list is changed by first selecting
Refer to the following screen shot.
The rest of the text labels are changed in similar fashion by clicking on the component in the Form Editor window, then using the Component Inspector to change the text property. Changing to GridBagLayout
Once the GUI is finalized, it's time to switch to the A Few Words About GridBagLayout
GridBagLayout is the most flexible and complex layout manager. Components are
placed in a grid of rows and columns, allowing specified components to span
multiple rows and/or columns. The Rows are not necessarily the same height, and
the columns are not necessarily the same width. Essentially
The first step in using the
The following is typical of Forte generated
GridBagConstraints--Instance Variables
As I mentioned earlier, the
I didn't use the customizer, because the changes I needed were so easily
accomplished using the Component Inspector. The second column of my
Code CompletionOne of the greatest things Forte CE offers (in my opinion) is code completion. If you type a few characters of a variable name, then hit CNTL+SPACE, the system presents possibilities for completing the variable name based on the pool of known variables. If you type an object name and a period, then pause or hit CNTL+SPACE, the system presents you with a list of possible methods and arguments. Double clicking on one of these entries completes the code by inserting the method name. Having all applicable methods at your fingertips is an unbelievable time saver. In the editor, I type the object name graphics. Then I hit CNTL+SPACE. A window like the one shown below appears showing me the possible method calls on this object. Let's take a look at this capability in action.
The Applet Code--Screening Input Data
Now let's get into the code. When the Draw button is clicked, the
The
In the example above, the
If the
if ( xCent < -1000 | xCent > 1000 ) {
If the input value satisfies this logic test, then there is a problem. The data
is out of range. The inputError variable is set true, and an
informative message is entered into the input field, in the manner just
described.
The rest of the input data screening code is very similar to this example. The 2D CodeNote: this code has been extracted from surrounding logic.
The first method fired against the
The next method is
Now we come to the powerful
The
The user inputs an
x = xCent - w / 2;
y = yCent - h / 2;
establishes the upper left hand corner, by converting the user's ellipse center
input values. So now we have the upper left hand corner of the rectangle, and the
width and height. That's all we need, let's define an elp = new Ellipse2D.Double( x, y, w, h );
Remember that the The code below defines translation constants that will be used later to reposition the user space after each scaling transformation, so that the ellipses remain centered.
The following syntax completes the transformation in this section:
Here, a rotation is fired against the
A
elpTrans =
afn.createTransformedShape( elp );
This is done to preserve the stroke width during later scaling operations. Otherwise, as the ellipse is scaled up or down, the drawing would take on huge bold strokes, or tiny strokes if the scale factor was less than one.
Then the Graphics2D object's draw method is fired with g2d.draw( elpTrans ); Scaling and TranslatingThe previous code illustrated drawing the first ellipse, which is not scaled. It is rotated into initial position, then drawn. The following code represents the more general case, where the ellipses are rotated, translated and scaled. Note: this code has been extracted from surrounding logic, which will be explored later.
Composing transforms refers to stacking transforms to be applied. The last
transform fired against the
In our case the key operations that are sensitive to this ordering are translate,
then scale. The translate transform adjusts the user space so that when the
scaling takes place, the ellipse remains centered. Since rotation is done about
the ellipse center point The translation constant (defined earlier) is presented below again. This value, as an argument to the translate method, moves the user space so that the ellipse will appear centered after each scaling operation.
As before, a shape object is created by the following syntax:
elpTrans =
afn.createTransformedShape( elp );
And the ellipse is drawn into a buffer by firing the draw method.
g2d.draw( elpTrans );
The following code turns off the drawing functionality if the scaling
calculations produce results that are wildly outside the viewable area. The
The Need for a Timer
Every program that performs animation by painting at regular intervals needs an
animation loop. Generally, this loop should be in its own thread. It should never
be in the Notes on TimersGenerally, a timer supports executing a task periodically, or just once at some future time. Timers are valuable tools because they simplify the job of scheduling activities. Swing Timer
The The important difference between using the Swing timer class and creating your own thread, is that the Swing timer class uses just one thread for all timers. It deals with scheduling actions and putting its thread to sleep internally. Utility Timer and TimerTask Classes
Timers are not the exclusive domain of GUI applications. In version 1.3 of the Java
platform, support for timers was added to the
Both timer facilities have the same basic support for delayed and periodic
execution. But the Choosing a Timer Class
As we have seen, the
The new utility timer classes provide control over how many timer threads are
created. Each
The Implementing javax.swing.Timer
For this applet, I've chosen the
Timer definition: private Timer timer;The Timer is instantiated with a first argument designed to fire an action every
30 milliseconds. The second argument, this, refers to the current object (the
applet), which implements ActionListener. The Timer fires the
actionPerformed method.
Firing the
The
The code below illustrates the use of the timer object and the
The start method is fired when the applet is loaded, or the user revisits a page that contains the applet. During the applet loading phase, the Timer is not
started, because the Boolean variable drawing is not true. If the applet window
is minimized during a drawing operation, the stop method is called, which calls
stopAnimation. Since the Timer is running, its stop method is
called. This shuts down the drawing operation. When the applet window is
maximized, the applet's start method is fired, which calls
startAnimation. This time, since the drawing Boolean is true, the
Timer is restarted by firing its start method. When the line
timer.start() is executed, the drawing operation resumes.
The synchronized keyword is used on both the
The
jPanel1.updateBuffer();
jPanel1.repaint();
Calling the repaint() method is the proper way to trigger
application or applet driven painting. The paint() method is called
when it's time to render. Swing factors the paint() call into three
separate methods, which are called in the following order:
protected void paintComponent(Graphics g) protected void paintBorder(Graphics g) protected void paintChildren(Graphics g)Swing programs should override paintComponent() instead of
overriding paint().
Now let's take a look at the named inner class which is instantiated to generate
the jPanel1.repaint();Here's the inner class which extends JPanel.
Most of the code above has been covered earlier in the article (albeit extracted from its surrounding logic). The if and else structures control the direction of
ellipse rotation, and the start/stop points for the transform operations.
The most important thing to discover about the
Remember that our
jPanel1.updateBuffer();
jPanel1.repaint();
Also remember that jPanel1 is an instantiation of the inner class
DrElp. So the first method call, JPanel1.updateBuffer()
does some extensive initialization on its first time through, based on the binary
switch firstPass. After that, when the binary switch drawing is true, the code
is focused on preparing a buffer for later output. That output takes place when
the line jPanel1.repaint() is executed. The
paintComponent() method is called automatically by the
repaint() method.
Let's look at
Just inside
If the firstPass Boolean is true, and the buffer variable is null,
as it is upon applet startup, or after the JPanel has been erased,
then the block above is executed to set up the drawing attributes and the buffer.
First the size of the JPanel is interrogated with a call to
getSize(). Then the Insets object insets is created with a call to
getInsets. As we learned in the GridBagLayout section,
an Insets object has four public members--top, bottom, left, and
right. In the general case, the members represent the number of pixels from each
edge of the component, to the drawing area of the component. A calculation is
then made, using this data, to construct a width and height reflecting the area
within the component that is usable for drawing operations. These calculations
have been done for the general case, where component borders or title bars may
actually obscure the drawing area. In the case of our JPanel
component, the getInsets method returns all 0s, because the
JPanel has no border, title bar, etc.
Next, the
Then the buffer object is used to create a g2d = buffer.createGraphics();After that, we set the background color, which interrogates the background color of the parent applet, then uses this color to fill the JPanel
rectangle drawing area. The fillRect method specifies the area to be
filled, starting with an integer x and y value.
Remember that our coordinate scheme specifies the upper left corner as the
0,0 point. So the method call here begins the fill at the inset
values (in this case 0s), to clear the component boarder (if a
border exists), then specifies the rest of the rectangle using the
JPanel's width and height. The call looks like this:
g2d.fillRect( insets.left,
insets.top, size.width, size.height );
That completes the basic setup, now let's see how the paintComponent method uses
the buffer. The method is defined like this:
As mentioned earlier, execution of jPanel1.repaint() from within the
actionPerformed() method, triggers calls to the
paintComponent method. When paintComponent( Graphics g
) is invoked by Swing, the Graphics object parameter g is
pre-configured with the appropriate state for drawing the JPanel.
Programmers who override paintComponent must use this
Graphics object (or one derived from it), to render output. In this
case, the Graphics object is cast to a Graphics2D
object.
Graphics2D graphics = (Graphics2D)g;Then the resulting Graphics2D object is used to render the output
from the buffer. The syntax below handles the generation of this output.
graphics.drawImage( buffer, insets.left,
insets.top, this );
The drawImage method draws the image specified by the first
argument. The image is drawn with it's top left corner at point specified by the
second and third arguments. In this case insets.left and insets.top are both
0. Again, the use of insets makes sure that the component border
does not obscure output. The process that draws the image can notify the
specified observer (the third argument), in this case, the current object this.
If the buffer is null, then the call to And that about wraps it up. As an epilogue, let's take a look at using the Forte for Java CE debugger, which can be very helpful in determining exactly what is going on, when things seem confusing.
Using The Forte CE DebuggerThe debugger can be used to present "snapshots" of the system state during execution. By placing breakpoints at key positions throughout the source code, the debugger can halt and display details of the current environment at that point in the source. You can effectively step through code, monitoring execution as it occurs. The debugger can also be connected to an already-running process. Debugger WindowThe Debugger Window is a three-tabbed display with tabs for Breakpoints, Threads, and Watches. In the right half of the window, is the property sheet pane which displays the properties and their current values for the node selected in the left pane. BreakpointsTo add a breakpoint, right click on a line of code in the Editor Window and select Add/Remove Breakpoint from the pop-up menu, or choose Add/Remove Breakpoint from the Debug menu or toolbar in the Main Window. The current line is highlighted in blue to indicate that a breakpoint has been set. Breakpoints can also be set explicitly by selecting New Breakpoint from the Debug menu, which brings up the Add Breakpoint dialogue box. In this mode, the type of breakpoint (either exception, method, or line) can be chosen from the combo box, then settings can be entered specifying exception class name, class name and method name, or class name and line number. The screen shot below illustrates a breakpoint I added by right-clicking the mouse on the desired line, then clicking the pop-up Add/Remove Breakpoint option, which functions as a toggle. The blue line indicates the breakpoint. The cyan line indicates where the debugger is currently stopped. In this case, one line after the breakpoint, as a result of a Trace Over command which will be explained later.
Setting WatchesWatches can be set from the Debug menu on the Main Window, or from the contextual menu of a variable you have selected in the Editor. In the Editor, double-click a variable to select it, then right-click to bring up the menu of context choices, including Add Watch. Standard watches, as described above, refer to the value of a variable of that name currently in scope. It is possible to create a fixed watch, which always refers to the variable in the context in which the watch was created. In other words, if another variable in the currently executing scope has the same name, the fixed watch will refer back to the previous value. In this applet, there are no variables of the same name in different scopes. The Debugging SessionNow let's execute a debug session. Click on Start Debugging from the Debug menu to begin. In my example, a breakpoint is set at the statement: scaleFact = Float.parseFloat(jTextField7.getText());. After the debugger presents the applet's Swing GUI, I click the applet's Draw button to begin the applet's processing. The debugger runs to the set breakpoint then halts. The debug window shows variables that have been selected for watches. One of the most useful attributes of this debugger is the ability to hover the mouse over variables and interrogate their values using tool tip style pop-up boxes. With this feature, the current state of the applet is easily revealed. Selecting the Continue option from the Debug menu jumps to the next breakpoint. Or, successive Java statements can be executed by selecting Trace Over from the debug menu, or by pressing the F8 key. A useful technique for stepping through the program is to just hit F8 over-and-over, while watching the cyan execution bar march down through the program listing in the edit window. Here is a summary of the basic debugger commands.
The screen shot below illustrates four watches in the Debugger Window, and a pop-up showing a data value in the Edit window.
Threads
The Threads tab displays all thread groups in the current debugging process.
These thread groups are expandable hierarchies; each group containing other
thread groups or single threads, which in turn contain When a thread is suspended:
The The Debugger Window displays the following properties for each running thread:
Name - Thread name (according to the thread class).
The screen capture below illustrates expanded thread information derived from my applet.
Note: There is an extensive user's manual available at Help | Documentation from the Forte CE Main Window. Preparing to Run the AppletThe Java Plug-in is required to run this applet. The plug-in directs applets to run using the Java 2 Runtime Environment, Standard Edition (JRE) instead of the web browser's default virtual machine. Sun's JRE provides a Java Compatible environment for the widely adopted web browsers, and ensures that applets have access to the latest Java 2 technologies. The HTML ConverterTo compile the applet code and initiate it locally, rather than downloading from the server, requires the HTML Converter. The HTML converter modifies HTML syntax to access the Java 2 JRE Plug-in. The converter can operate on a single file, or an entire directory of HTML files, including all sub-directories, if desired. The original HTML files are automatically saved in an alternate directory. The following screen capture shows the HTML Converter's GUI:
Running the AppletIf you've done a fast-forward to this point, you'll need the Java 2 Plug-in, see commentary above. Run the applet Code ListingClick here to view ElpGen2.java or right-click and select Save Link As to download the code. ConclusionForte for Java Community Edition made the development of this applet much easier and more fun. The Form Editor makes initial GUI design a ten minute exercise. And the code completion function presents an object's available methods at a glance. The debugging facilities also expedite the development process. Once you've used an IDE, it's almost impossible to go back to writing code the "old fashioned" way. Reference TextsThe Java Tutorial, Third Edition, Mary Campione, Kathy Walrath and Alison Huml, Addison-Wesley, 2000 Java 2D API Graphics, Vincent J. Hardy, Sun Microsystems Press/Prentice Hall, 2000 Java How to Program, 3rd Edition, Deitel & Deitel, Prentice Hall, 1999 Java 2 Platform, Laura Lemay and Rogers Cadenhead, Sams, 1999 Reference URLsDownload Forte for Java Community Edition Using Timers in Swing Applications
Acknowledgements
Special thanks to Sun's Kathy Walrath for the About the AuthorMichael Meloan, a frequent contributor to the Java Developer Connection, began his professional career writing IBM mainframe and DEC PDP-11 assembly languages. He went on to code in PL/I, APL, C and Java. In addition, his fiction has appeared in WIRED, BUZZ, Chic, L.A. Weekly, and on National Public Radio.
| |||||||||||||||||||||
|
| ||||||||||||