7.4 Adding a Speed Control to the Animator

 < Day Day Up > 



7.4 Adding a Speed Control to the Animator

The animator as given in Exhibit 3 (Program7.1c) appears to work fine; however, the speed is fixed and cannot be changed without recompiling the program. An enhanced animator with a scrollbar to control the speed and an exit button is shown in Exhibit 4 (Program7.2). This program differs from Exhibit 3 (Program7.1c) only in that the control panel has now been added to the SimpleAnimator constructor, and the amount of time the animator waits between redrawing the frame is now controlled by the variable sleepTime. When the scrollbar is all the way to the left, the scrollbar sets the sleepTime to 10,000 and the JFrame is redrawn every 10 seconds. When it is all the way to the right the sleepTime is set to 10 and the JFrame is redrawn every 1/100 of a second.

Exhibit 4: Program7.2: Simple Animator with a Speed Control

start example

 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class SimpleAnimator1 extends JFrame {   private Ball ball = new Ball(Color.red, 20, 20, 450, 450);   private int sleepTime = 100;   /**    *  Constructor that builds the simple GUI with a scrollbar to    *  keep track of speed and an exit button. The ball object is    *  then added to the Frame to be animated.    */   public SimpleAnimator1() {     Container con = this.getContentPane();     // = = = = = = = = = = = = = = = = = = = = =     // Control Panel. It consists of the button     // and scrollbar defined below.     JPanel controlPanel = new JPanel();     JButton exitButton = new JButton("Exit");     exitButton.addActionListener(new ActionListener() {       public void actionPerformed(ActionEvent e) {         System.exit(0);       }     });     controlPanel.add(exitButton);     controlPanel.add(new JLabel("Animator Speed"));     final JScrollBar speedControl = new JScrollBar(JScrollBar.         HORIZONTAL, 4900, 10, 50, 5000);     speedControl.addAdjustmentListener(new AdjustmentListener()         {       public void adjustmentValueChanged(AdjustmentEvent e) {       sleepTime = 5000 - e.getValue();     }   });   controlPanel.add(speedControl);     con.add(controlPanel, BorderLayout.SOUTH);     // = = = = = = = = = = = = = = = = = = = = = =     // Add the ball to the JFrame.     con.add(ball, BorderLayout.CENTER);   }   /**    *  This method simply dispatches paint events to the ball    *  object, waits for 0.1 seconds, and does it again.    */   public void paint(Graphics g) {     super.paint(g);     try {       Thread.sleep(sleepTime);       repaint();     } catch(InterruptedException e) {     }   }   /**    *  Start the program.    */   public static void main(String args[]) {     // Display the Frame.     SimpleAnimator1 sa = new SimpleAnimator1();     sa.setSize(500,500);     sa.show();   } } /**  *  Purpose: This class implements a ball JPanel on which it  *           can draw a ball to displayed on the JFrame it has  *           been added to. It animates the ball by moving it  *           on the screen a little each time the paint method  *           is called.  */ class Ball extends JPanel {   Color ballColor;   int xStart, yStart, xLimit, yLimit;   Path myPath;   /**    *  Constructor that sets the ball up to run and sets the    *  limits for the size of the screen area on which it is to run.    *  It also sets the initial path over which to move the ball.    */   public Ball(Color ballColor, int xStart, int yStart,       int xLimit, int yLimit) {     this.ballColor = ballColor;     this.xStart = xStart;     this.yStart = yStart;     this.xLimit = xLimit;     this.yLimit = yLimit;     myPath = new StraightLinePath(xStart, yStart, xLimit,       yLimit, 50);   }   /**    *  This method draws the ball at the correct position on    *  the screen given by the path. When the end of path is    *  reached, a new path is created to move the ball in the    *  opposite direction.    */   public void paint(Graphics g) {     super.paint(g);     Point pos = myPath.nextPosition();     g.setColor(ballColor);     g.fillOval((int)pos.getX(), (int)pos.getY(), 15, 15);     if (! myPath.hasMoreSteps()) {       if (pos.getX() = = xStart)         myPath = new StraightLinePath(xStart, yStart, xLimit,           yLimit, 50);       else         myPath = new StraightLinePath(xLimit, yLimit, xStart,           yStart, 50);     }   } } 

end example

While this seems to be a nice improvement, the animator exhibits a very disturbing behavior when the scrollbar is pulled all the way to the left to allow the redraw to occur every 10 seconds. When this happens, the scrollbar and button appear to handle user requests incorrectly. For example, if the user pulls the scrollbar all the way to the left and then tries to immediately pull it all the way to the right, it appears to be "jammed" at the left-most position for 10 seconds and then suddenly moves to the right. Similarly, if the scrollbar is moved all the way to the left and the exit button is pressed, the program waits 10 seconds before exiting the program. It is almost as if the GUI components are improperly implemented.

This reveals the fallacy of trying to understand how to program by looking only at the external behavior of a component. Many programmers develop mental models of the behavior of components that work in a number of limited instances but eventually fail because they contain errors in understanding. In this case, a programmer might be led to believe that a bug in the GUI components needs to be fixed, when in fact the GUI components are being properly implemented. The problem here is the presence of a systemic bug in the design of the animator, which only a complete redesign can correct.

The animator is using the threads in the program incorrectly. As explained in Section 7.3, the GUI thread for this frame is responsible for calling the paint method when a repaint event occurs. The GUI thread is also responsible for handling other events, such as an Action-Event when the button is pressed or an AdjustmentEvent when the scrollbar is moved. What is happening is that in the paint method the programmer has put a call to Thread.sleep, and that call is suspending the current thread (in this case, the GUI thread) for the length of time requested in the sleep call. When the GUI thread enters the paint method and sleeps for 10 seconds, the ActionEvent and Adjustment-Event are only processed every 10 seconds, at which point the GUI thread can run again. This in effect creates a time delay of up to 10 seconds between when an event is generated and when it is handled. When this is understood, the behavior of the Button and Scrollbar makes sense, but they are still very frustrating to use.

Exhibit 4 (Program7.2) points out a very important rule in using the GUI thread: Never do anything that is time intensive in the GUI thread. Actions such as sleeping or RMI calls are seldom appropriate actions when running the GUI thread and should not be used when a program is running in a GUI thread as they will impact all the other components added to the JFrame. The number of methods that can possibly run in the GUI thread is large, but they are relatively limited in scope, so it is less of a problem than might be imagined. Obviously, all the methods defined as listeners in the AWT (such as actionPeformed for the ActionListener, or adjustmentValueChanged for an AdjustmentListener) run in a GUI thread. The paint method runs in the GUI thread, as does the "init" and most of the other methods in an applet. Other than these, programmers are unlikely to use methods running in the GUI thread unless they are writing GUI components.

As was pointed out, the problem with the animator in Exhibit 4 (Program7.2) is systemic; there is no way to fix Exhibit 4 (Program7.2) to make it work correctly. The loop running the sleep and repaint must be moved out of the GUI thread and a separate thread created to specifically handle the delay using sleep. In the next section, we implement an improved version of the animator that has a control interface but does not have the problem of the GUI components not responding. This new animator is further improved in that it is not tied to a single object, such as the Ball object in the SimpleAnimator, but can be used to animate many different types of objects.



 < Day Day Up > 



Creating Components. Object Oriented, Concurrent, and Distributed Computing in Java
The .NET Developers Guide to Directory Services Programming
ISBN: 849314992
EAN: 2147483647
Year: 2003
Pages: 162

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net