August 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Demonstrating Java Programs using the Robot Class

  • September 16, 2003
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 1476


Preface

Programming in Java doesn't have to be dull and boring.  In fact, it's possible to have a lot of fun while programming in Java.  This is the third lesson in a short miniseries that shows you how to use the Robot class to write programs that are both fun and useful.

New features in SDK Version 1.3

One of the new features that was released in SDK Version 1.3 was the Robot class.  According to Sun,

"This class is used to generate native system input events for the purposes of test automation, self-running demos, and other applications where control of the mouse and keyboard is needed. The primary purpose of Robot is to facilitate automated testing of Java platform implementations."

What is a Java robot?

The Robot class makes it possible for your Java program to temporarily take control of the mouse and keyboard input functionality at the operating-system level.

Several instance methods are available

The Robot class provides several instance methods, (including the following), by which your program can produce mouse and keyboard input, just as though that input were being provided by a human user.

  • mouseMove - Moves the mouse pointer to a set of specified absolute screen coordinates given in pixels.
  • mousePress - Presses one of the buttons on the mouse.
  • mouseRelease - Releases one of the buttons on the mouse.
  • keyPress - Presses a specified key on the keyboard.
  • keyRelease - Releases specified key on the keyboard.

A word of caution

A runaway Java Robot object has the ability to wrest control away from the human user, so you need to be a little careful.  For example, if you allow your Java Robot program to go into an infinite loop, making mouse moves, clicking the mouse, and entering keystrokes, you may find that the only practical way to regain control of your computer is to either turn off the power or press the reset button to force your computer to restart.

Three lessons in the miniseries

According to my current plans, this miniseries on the Robot class will consist of three lessons.  The first lesson, entitled Introduction to the Java Robot Class in Java, demonstrated the low-level nature of an object of the Robot class.  That was accomplished by showing you how to create a Java robot that can manipulate other non-Java programs, such as Windows Notepad and Internet Explorer.

The second lesson, entitled An Automated Test Program using the Java Robot Class, showed you how to use a robot to perform automatic testing on a Java GUI.

This lesson will show you how to write a robot program to provide a visual animated demonstration of the use of a Java GUI.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings and figures while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

Preview

A machine with no purpose

Many years ago when I was in engineering undergraduate school, the college set aside one day each year to invite parents to the college.  On that day, there were numerous programs, including demonstrations of interesting electrical and mechanical gadgets.

One of the gadgets that I remember was a small wooden box with an electrical toggle switch on the top.  When a person toggled the switch to the ON position, a small hand emerged from a trap door and toggled the switch back to the OFF position.  Then the hand retreated back into the trap door and disappeared.  The machine had no purpose other than to keep itself turned off.

An interesting idea

As I was trying to come up with an interesting idea for this lesson, it occurred to me that a computer simulation of that box might be interesting.

I will discuss a sample program named Robot03 in this lesson.  The sample program will show you how to write a robot program to provide a visual animated demonstration of the use of a Java GUI.

In this demonstration, the user can use the mouse to toggle any one of three toggle buttons from not selected to selected.  As in the box described above, as soon as this happens, an animated hand will move to the button and toggle it back to not selected.  Then the hand will disappear.

Discussion and Sample Code

Description of the program named Robot03

This program illustrates the use of the Robot class to automatically unselect a JToggleButton, which has been manually selected by the user.

This program provides an example of how to create and use a software robot in an animated fashion.  Such animation could be useful in demonstrating how to use a program with a GUI.

A frame with three toggle buttons

The program places a JFrame object, containing three JToggleButton objects, near the upper-left corner of the screen.  Figure 1 shows the GUI produced by the program on startup.

Figure 1 Program GUI on startup

As you can see, none of the toggle buttons are selected at startup. 

(The buttons initially appear to protrude from the screen in the Motif look and feel.)

Unfortunately, there is no practical way for me to show you the animation in this lesson.  You will need to compile and execute the program in order to view the animation in progress.

Will fire action events and toggle between two states

When the user manually clicks any of the three buttons, or presses the space bar while one of the buttons has the focus, the button becomes selected and fires an ActionEvent.

Because the button is a JToggleButton, it will toggle between being selected and not being selected.  When the button is not selected, it is colored a light shade of gray, and appears to protrude from the screen.  When the button is selected, it is colored a dark shade of gray and appears to be pushed into the screen.

Focus traversal

If you successively press the focus traversal key, the focus will traverse the components using the focus traversal policy that is in effect.

I did not implement a new focus traversal policy, nor did I change the focus traversal key.  Therefore, the default focus traversal policy and the default focus traversal key are both in effect. 

Successively pressing the TAB key will cause the focus to move from one component to the next, from left to right, and then back to the left end.

If you hold down the SHIFT key and successively press the TAB key, the focus will traverse the components in the reverse direction.

As mentioned earlier, pressing the space bar while a button has the focus will cause it to fire an ActionEvent and to toggle between being selected and not being selected.

Common event handler

A common ActionEvent handler is registered on each of the buttons.  The ActionEvent handler uses an object of the Robot class to unselect the button in an animated fashion.

The robot takes control of the mouse

As soon as the action event is fired, the robot takes control of the mouse pointer.  The mouse pointer changes from the default arrow-shaped pointer into a hand-shaped pointer.  The hand-shaped pointer becomes animated, moving from the upper-left area of the GUI to the center of the button that fired the action event.

The robot clicks the button

When the mouse pointer reaches the center of the button that fired the event, the robot automatically clicks the button with the mouse, causing the button to become unselected.  This causes the button to once again appear to protrude from the screen.  It also causes the button to change from dark gray to light gray.

Action events are disabled

Action events are disabled on the button after the user clicks the button and before the robot clicks the button.  Action events are re-enabled after the robot clicks the button.  Otherwise, the program would go into an infinite loop of firing and processing action events on the same button.

Robot relinquishes control of the mouse

When the button is clicked by the robot, the robot relinquishes control of the mouse.  The pointer returns to the upper-left area of the GUI and turns back into the arrow-shaped default pointer.  The user can then click the same, or a different button, starting the animation process all over again.

Requires V1.3 or later

Because the Robot class was released in SDK Version 1.3, this program requires V1.3 or later to compile and execute successfully.  The program was tested on my machine using SDK 1.4.1 under WinXP

The beginning of the Robot03 class

I will discuss this program in fragments.  A complete listing of the program is shown in Listing 19 near the end of the lesson.

The program named Robot03 begins in Listing 1 where an instance variable of type Robot is declared.  This variable will later be used to hold a reference to an object of the Robot class.
 

public class Robot03 extends JFrame
implements ActionListener{
Robot robot;

public static void main(String[] args){
new Robot03();
}//end main

Listing 1

Listing 1 also shows the main method, which instantiates an object of the Robot03 class.

The animated behavior of this program is primarily controlled by the ActionEvent handler registered on the buttons and by methods called by the event handler.

The constructor

The constructor begins in Listing 2.  The code in the constructor is relatively straightforward, so I won't discuss that code in detail.  However, I will review the constructor code briefly to set the stage for the discussion of the event handler that follows.
 

  public Robot03(){//constructor
try{
robot = new Robot();
}catch(AWTException e){e.printStackTrace();}

Listing 2

The code in Listing 2 instantiates a new Robot object and saves its reference in the variable named robot, which was declared in Listing 1.  Because the reference is stored in an instance variable, it is accessible throughout the class.

Prepare the JFrame for use

The code in Listing 3 executes several routine actions to prepare the JFrame object for use.
 

    setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE );
getContentPane().setLayout(
new GridLayout(1,3));
setBounds(20,20,250,100);
setTitle("Copyright 2003, R.G.Baldwin");

Listing 3

Create the buttons ...

The code in Listing 4 creates the three JToggleButton objects and adds them to the frame.

    JToggleButton b1 = new JToggleButton( "1" );
JToggleButton b2 = new JToggleButton( "2" );
JToggleButton b3 = new JToggleButton( "3" );

getContentPane().add(b1);
getContentPane().add(b2);
getContentPane().add(b3);

Listing 4

Register action listeners

The code in Listing 5 registers this object as an action listener on each of the buttons.
 

    b1.addActionListener(this);
b2.addActionListener(this);
b3.addActionListener(this);

Listing 5

Set the look and feel

The code in Listing 6 sets the look and feel to Sun's Motif and makes the GUI visible.
 

    String plafClassName =
"com.sun.java.swing.plaf.motif." +
"MotifLookAndFeel";
try{
UIManager.setLookAndFeel(plafClassName);
}catch(Exception ex){ex.printStackTrace();}

//Cause the new L&F to apply
SwingUtilities.updateComponentTreeUI(this);

//Make the frame visible
setVisible( true );
}//end constructor

Listing 6

Listing 6 also signals the end of the constructor.

Now for something a little more interesting

Now that we have gotten beyond the preliminaries, the code should become a little more interesting, and I will discuss the remaining code in more detail.

Define the ActionEvent handler

Because the class implements the ActionListener interface, the class must define the method named actionPerformed, which is declared in that interface.  The definition of the actionPerformed method is shown in Listing 7.
 

  public void actionPerformed(ActionEvent e){
JToggleButton button =
(JToggleButton)(e.getSource());
new HandleEvent(button,this).start(); }//end actionPerformed Listing 7

Causing the class to implement the interface and defining the event handler method within the class makes it possible to register this object as an ActionEvent handler on each of the buttons as shown earlier in Listing 5.

The time required to handle the event

One of the important considerations in writing event handlers has to do with the amount of time that will be required to handle the event in its entirety.  If the amount of time required to handle the event will be large, the event handler method should spawn a new thread to do the job and return immediately.  This avoids tying the event handler thread up for long periods while previous events are being handled.

Spawn a thread

Because of the animated nature of this event handler, quite a lot of time will be required to handle each event fired by a button.  The mouse pointer must move across the GUI at a relatively slow speed in order to get to and click on the button that fired the event.

Therefore, the actionPerformed method in Listing 7 spawns a new thread from the inner class named HandleEvent to perform the animation.  The actionPerformed method starts the new thread running and returns immediately.

The HandleEvent class

The HandleEvent class begins in Listing 8.  Objects of this class, which extends Thread, are spawned to handle the animation associated with action events on the buttons.
 

  class HandleEvent extends Thread{
JToggleButton button;
ActionListener listener;

HandleEvent(JToggleButton button,
ActionListener listener){
this.button = button;
this.listener = listener;
}//end constructor

Listing 8

The constructor

The constructor for the HandleEvent class is shown in Listing 7.  This constructor receives and saves references to:

  • The button that fired the event.
  • The ActionEvent listener that is registered on the button.

Click on the button

The reference to the button is needed to cause the mouse pointer to move to the button and click on that button in an animated fashion.

Temporarily disable the action listener

The reference to the listener is needed to temporarily disable the listener before the robot clicks on the button.  Otherwise, the program would go into an infinite loop with the robot causing the button to fire action events while trying to handle those events.

The run method

The run method of a Thread object is where the behavior of that thread is controlled.  The beginning of the run method for the HandleEvent class is shown in Listing 9.
 

    public void run(){
Point location =
button.getLocationOnScreen();

Listing 9

The first task in the run method is to get the screen location of the button that fired the event.  This information will be needed in order to cause the mouse pointer to move to that location and execute a mouse click.

Disable action events

As mentioned earlier, when the robot causes a mouse click to occur on the button that fired the event, that button will fire another action event.  If the action listener is still registered on that button, this will result in an infinite loop of firing and handling action events.

The code in Listing 10 temporarily un-registers the action listener from the button to prevent this from happening.
 

      button.removeActionListener(listener);

Listing 10

(The registration of the action listener on the button will be restored later.)

Move the mouse and click the button

The code in Listing 11 invokes the method named mouseMoveAndClick to cause to program to:

  • Change the mouse pointer to a hand-shaped pointer in the upper left of the GUI.
  • Move the pointer slowly from the upper left to the center of the button that fired the event.
  • Click the button that fired the event.
  • Restore the mouse pointer to the default pointer in the upper left of the GUI.

This action causes the selected button to become not selected, causing it to pop out of the screen and to change color from dark gray to light gray.
 

      mouseMoveAndClick(50,50,
location.x + button.getWidth()/2,
location.y + button.getHeight()/2);

Listing 11

The mouseMoveAndClick method

At this point, I am going to temporarily put the discussion of the run method on the back burner and discuss the method named mouseMoveAndClick, which begins in Listing 12.
 

  public void mouseMoveAndClick(int xStart,
int yStart,
int xLoc,
int yLoc){
robot.mouseMove(xStart,yStart);

Listing 12

Move pointer to the starting position

Note that the invocation of this method in Listing 11 passes the coordinates 50, 50 as the first two parameters.  This represents a point in the upper left of the GUI.

The code in Listing 12 moves the mouse pointer to this location in one step.  There is no slow-speed animation associated with this move.

Change to a hand-shaped curser and delay

The code in Listing 13 changes the appearance of the mouse pointer from its default arrow shape to the shape of a hand.  Then a one-second delay is inserted before the animation actually begins.
 

    setCursor(new Cursor(Cursor.HAND_CURSOR));
robot.delay(1000);

Listing 13

Compute the incremental mouse pointer step size

A while loop will be used to cause the pointer to move in small steps from its current location to the center of the button that fired the event.  The code in Listing 14 is used to compute the size of the steps in the horizontal and vertical directions.
 

    double div;
if ((xLoc - xStart) < 50.0) div = 15;
else if ((xLoc - xStart) < 100.0) div = 30;
else div = 60;

double xInc = (xLoc - xStart)/div;
double yInc = (yLoc - yStart)/div;

Listing 14

Double precision arithmetic

This is the sort of computation where errors associated with integer arithmetic can accumulate to produce large resulting errors.  Therefore, the computations were performed using double precision to avoid this potential problem.

Different incremental step sizes

If you examine Listing 14 carefully, you will see that the incremental step size used for the animation depends on how far the mouse needs to travel to reach the button.  This causes the animation to be more visually pleasing than was the case when I simply used the same step size regardless of distance.  (I will leave it as an exercise for the student to ponder and understand why this is the case.)

Move the mouse pointer

The code in Listing 15 initializes two variables, and then executes a while loop to move the mouse pointer in incremental steps from its starting position in the upper left of the GUI to the center of the button that fired the event.
 

    double x = xStart;
double y = yStart;
while(((int)x < xLoc) || ((int)y < yLoc)){
x += xInc;
y += yInc;
robot.mouseMove((int)x,(int)y);
robot.delay(60);
}//end while loop

Listing 15

A 60-millisecond delay is inserted between each movement of the mouse pointer to enhance the animation effect.

Click the button

Once the mouse pointer arrives at the center of the button that fired the event, the while loop terminates and the code in Listing 16 is executed.
 

    robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);

robot.delay(1000);

Listing 16

The code in Listing 16 first presses and then releases the left mouse button on the screen location that is the center of the button that fired the event.  This constitutes a click and causes the button to fire an ActionEvent and to toggle from selected to not selected.

(Because the event listener is no longer registered on the button, the event is simply ignored.  However, the button appears to pop out of the screen and to change color from dark gray to light gray when it becomes not selected.)

Finally, the code in Listing 16 inserts a one-second delay to allow the user to savor the previous action before restoring the pointer to the default arrow shape in the upper left of the GUI.

Restore the mouse pointer

The code in Listing 17 moves the mouse pointer back to its original starting position in one step, and changes its appearance from the shape of a hand to the default shape of an arrow.
 

    robot.mouseMove(xStart,yStart);
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}//end mouseMoveAndClick

Listing 17

That ends one animation cycle.  The mouse pointer is now available for the user to click on another button and start a new animation cycle.

Re-enable the action listener

Returning now to the remaining code in the run method, the code in Listing 18 re-registers the action listener object on the button.
 

      button.addActionListener(listener);
}//end run

}//end class HandleEvent

Listing 18

Thus, that button's ability to fire action events and have them handled by the action listener is restored.

That ends the class named HandleEvent, and ends the program as well.

Run the Program

I encourage you to copy the code from Listing 19 into your text editor, compile it, and execute it.  Experiment with it, making changes, and observing the results of your changes.

The Robot class was first released in SDK Version 1.3, so you will need to be running Version 1.3 or later to successfully compile and execute the program.  This program was tested on my machine using SDK 1.4.1 under WinXP

Summary

In this lesson, I have taught you how to write an animated robot program to provide a visual demonstration of the use of a Java GUI.

Complete Program Listings

A complete listing for the program discussed in this lesson is shown in Listing 19 below.
 
/*File Robot03.java
Copyright 2003 R.G.Baldwin

Illustrates the use of the java.awt.Robot class
to automatically unselect a JToggleButton, which
has been manually selected by the user.

This program provides an example of how to create
and use a software robot in an animated fashion.
Such animation could be useful in demonstrating
how to use a program.

The program places a JFrame object,containing
three JToggleButton objects near the upper-left
corner of the screen.

The three buttons are initially not selected. In
other words, they initially appear to protrude
from the screen in the Motif look and feel.

When the user manually clicks on any of the
three buttons, or presses the space bar while one
of the buttons has the focus, the button becomes
selected. At that point, it appears to be pushed
into the screen.

This causes the button to fire an action event.

The action event handler uses an object of the
Robot class to unselect the button automatically
in an animated fashion.

As soon as the action event is fired, the robot
takes control of the mouse pointer. The mouse
pointer changes from the default arrow-shaped
pointer into a hand-shaped pointer. The hand
pointer becomes animated, moving from the upper-
left area of the screen to the center of the
button that fired the action event. When the
mouse pointer reaches the center of the button,
the robot automatically clicks on the button,
causing it to become unselected. The button
once again appears to protrude from the screen.

Action events are disabled on the button before
the robot clicks the button and are re-enabled
after the robot clicks the button. Otherwise,
the program would go into an infinite loop of
firing and processing action events on the same
button.

When the button is unselected by the robot, the
robot relinquishes control of the mouse. The
pointer returns to the upper-left area of the
screen and turns back into the arrow-shaped
default pointer.

The user can then click the same, or a different
button, starting the animation process all over
again.

Tested using SDK 1.4.1 under WinXP
************************************************/

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Robot03 extends JFrame
implements ActionListener{

Robot robot;

public static void main(String[] args){
new Robot03();
}//end main
//-------------------------------------------//

public Robot03(){//constructor
//Instantiate and save a new Robot object
try{
robot = new Robot();
}catch(AWTException e){e.printStackTrace();}

//Prepare the JFrame for use
setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE );
getContentPane().setLayout(
new GridLayout(1,3));
setBounds(20,20,250,100);
setTitle("Copyright 2003, R.G.Baldwin");

//Create the buttons
JToggleButton b1 = new JToggleButton( "1" );
JToggleButton b2 = new JToggleButton( "2" );
JToggleButton b3 = new JToggleButton( "3" );

//Add the buttons to the frame
getContentPane().add(b1);
getContentPane().add(b2);
getContentPane().add(b3);

//Register an action listener on each of
// the buttons.
b1.addActionListener(this);
b2.addActionListener(this);
b3.addActionListener(this);

//Set look and feel
String plafClassName =
"com.sun.java.swing.plaf.motif." +
"MotifLookAndFeel";
try{
UIManager.setLookAndFeel(plafClassName);
}catch(Exception ex){ex.printStackTrace();}

//Cause the new L&F to apply
SwingUtilities.updateComponentTreeUI(this);

//Make the frame visible
setVisible( true );
}//end constructor
//-------------------------------------------//

//Define the event handler that is registered
// on each of the buttons.
public void actionPerformed(ActionEvent e){
JToggleButton button =
(JToggleButton)(e.getSource());
//Spawn a thread to handle the event and
// return from the event handler method.
new HandleEvent(button,this).start();
}//end actionPerformed
//-------------------------------------------//

//Objects of this inner Thread class are
// spawned to handle action events on the
// buttons.
class HandleEvent extends Thread{
JToggleButton button;
ActionListener listener;

HandleEvent(JToggleButton button,
ActionListener listener){
this.button = button;
this.listener = listener;
}//end constructor

public void run(){
//Get the button's location.
Point location =
button.getLocationOnScreen();
//Disable action events on this button
// until this process is complete. Don't
// allow the robot to generate an action
// event on this button.
button.removeActionListener(listener);
//Cause the mouse pointer to automatically
// move across the screen and click on the
// button that fired the event currently
// being processed. This will un-select
// the toggle button.
mouseMoveAndClick(50,50,
location.x + button.getWidth()/2,
location.y + button.getHeight()/2);
//Re-enable action events on this button
button.addActionListener(listener);
}//end run

}//end class HandleEvent
//-------------------------------------------//

public void mouseMoveAndClick(int xStart,
int yStart,
int xLoc,
int yLoc){
//Move the mouse pointer to the starting
// position
robot.mouseMove(xStart,yStart);

//Change the cursor to a hand and delay
setCursor(new Cursor(Cursor.HAND_CURSOR));
robot.delay(1000);

//Use double precision to avoid cumulative
// arithmetic errors. Calculate the
// incremental distance for animated mouse
// pointer movement. Make animation speed
// appropriate for distance to be traveled.
double div;
if ((xLoc - xStart) < 50.0) div = 15;
else if ((xLoc - xStart) < 100.0) div = 30;
else div = 60;

double xInc = (xLoc - xStart)/div;
double yInc = (yLoc - yStart)/div;

//Initialize, and then execute a loop to move
// the mouse pointer from the starting
// position to the center of the button.
// Sleep for 60 msec between each movement.
double x = xStart;
double y = yStart;
while(((int)x < xLoc) || ((int)y < yLoc)){
x += xInc;
y += yInc;
robot.mouseMove((int)x,(int)y);
robot.delay(60);
}//end while loop

//Press and then release the left mouse
// button when the mouse pointer is resting
// in the location of the JToggleButton.
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);

//Return the cursor to the starting position
// and restore it to a default pointer.
robot.delay(1000);
robot.mouseMove(xStart,yStart);
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}//end mouseMoveAndClick
//-------------------------------------------//

}//end class definition

Listing 19

Copyright 2003, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects, and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-
 







Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel