JavaDemonstrating Java Programs using the Robot Class

Demonstrating Java Programs using the Robot Class

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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-
 

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories