This question already has an answer here:
Repaint is not functioning properly as required
(1 answer)
Closed 8 years ago.
I want to pause the execution of a Swing Program for a specified amount of time. Naturally the first thing that I used was Thread.sleep(100) (since, I am a noob). Then I got to know that my program is not thread safe so I decided to use Timer with some suggestions from fellow programmers. The problem is I am unable to get any sources from where I can learn how to delay the thread, using Timer. Most of them use Timer for delaying execution. Please help me solve this problem. I have provided a compileable code snippet below.
import javax.swing.*;
import java.awt.*;
public class MatrixBoard_swing extends JFrame{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MatrixBoard_swing b = new MatrixBoard_swing();
}
});
}
MatrixBoard_swing(){
this.setSize(640, 480);
this.setVisible(true);
while(rad < 200){
repaint();
rad++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
int rad = 10;
public void paint(Graphics g){
super.paint(g);
g.drawOval(400-rad, 400-rad, rad, rad);
}
}
EDIT: My trial for a Timer implementation(please tell me if it is wrong):
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MatrixBoard_swing extends JFrame implements ActionListener{
Timer timer;
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MatrixBoard_swing b = new MatrixBoard_swing();
}
});
}
MatrixBoard_swing(){
this.setSize(640, 480);
this.setVisible(true);
timer = new Timer(100, this);
timer.start();
}
int rad = 10;
public void paint(Graphics g){
super.paint(g);
g.drawOval(400-rad, 400-rad, rad, rad);
}
#Override
public void actionPerformed(ActionEvent arg0) {
repaint();
rad++;
if(rad >= 200){
timer.stop();
}
}
So instead of...
while(rad < 200){
repaint();
rad++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You simply need to turn the logic around a little...
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
rad++;
if (rad < 200) {
repaint();
} else {
((Timer)evt.getSource()).stop();
}
}
});
timer.start();
Basically, the Timer will act as the Thread.sleep(), but in a nice way that doesn't break the UI, but will allow you to inject a delay between execution. Each time it executes, you need to increment your value, test for the "stop" condition and update otherwise...
Take a look at How to Use Swing Timers and the other 3, 800 questions on the subject on SO...
Related
I'm trying to make a 2D game in Java, but when I call the repaint() method in a thread there's an odd grey-only window.
Here's the source code I have so far:
Spaceshooter.java
package spaceshooter;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Spaceshooter extends JFrame implements KeyListener, Runnable {
private Player player = new Player(5, 186, this);
private boolean up, down;
public Spaceshooter(String title) {
super(title);
this.setFocusable(true);
this.addKeyListener(this);
}
#Override
public void paint(Graphics gr) {
super.paint(gr);
gr.setColor(Color.BLACK);
gr.fillRect(0, 0, 800, 500);
player.paintPlayer(gr);
}
public static void main(String[] args) {
Spaceshooter shooter = new Spaceshooter("Spaceshooter");
new Thread(shooter).start();
shooter.setSize(800,500);
shooter.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
shooter.setVisible(true);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 38) {
up = true;
down = false;
} else if (e.getKeyCode() == 40) {
down = true;
up = false;
}
}
#Override
public void keyReleased(KeyEvent e) {
down = false;
up = false;
}
#Override
public void run() {
while(true) {
if (up) {
player.moveUp();
} else if (down) {
player.moveDown();
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException ex) {
Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Player.java
package spaceshooter;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Toolkit;
public class Player {
private int x, y;
private Component comp;
public Player(int x, int y, Component comp) {
this.x = x;
this.y = y;
this.comp = comp;
}
public void moveUp() {
y -= 5;
}
public void moveDown() {
y += 5;
}
public void paintPlayer(Graphics gr) {
gr.drawImage(Toolkit.getDefaultToolkit().getImage("images/player.png"), x, y, comp);
}
}
Thanks for your answers in advance!
What is EDT ?
Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe". All GUI related task, any update should be made to GUI while painting process must happen on the EDT, which involves wrapping the request in an event and processing it onto the EventQueue. Then the event are dispatched from the same queue in the one by one in order they en-queued, FIRST IN FIRST OUT. That is, if That is, if Event A is enqueued to the EventQueue before Event B then event B will not be dispatched before event A.
Any task you perform which may take a while, likely to block the EDT, no dispatching will happen, no update will be made and hence your application FREEZES. You will have to kill it to get rid of this freezing state.
In your program, aside from creating your JFrame and making it to visible from the main thread (which we also should not do):
while(true) {
if (up) {
player.moveUp();
} else if (down) {
player.moveDown();
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException ex) {
Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
}
}
you are posting repaint() request from the thread which you sent to sleep right immediately after that.
SwingUtilities.invokeLater():
Swing provides a nice function SwingUtilities.invokeLater(new Runnable(){}) for posting repaint request to the EDT. All you have to do is to write:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});
Now some more thing to mention:
We should not implements a GUI component with Runnable. Make another class implementing Runnable, make your computation inside that then use SwingUtilities to post the component update request.
we should not made custom painting on JFrame directly. JFrame is a top level component. It is more like a container which contains your whole app. If you want custom painting use a custom component MyCanvas extends JComponent.
we should not override paint() function. Instead paintComponent(g) will serve our custom painting purposes nicely.
Learn to use Swing Timer class for timely repeated GUI rendering task.
Tutorial Resource and References:
The Event Dispatch Thread
EventQueue
How to Use Swing Timers
Lesson: Performing Custom Painting
This is just the begining of the game, where there are two squares, one can be controlled by arrow keys and other by mouse, they can fire balls on each other and simultaneously can be saved, the one getting maximum hits will win...
In this code when I fire from the second square there is a long line which goes towards the second player and whole of the game has to halt..
package raship;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.io.IOException;
public class raship extends Applet implements KeyListener, MouseMotionListener, MouseListener, Runnable
{
int width,flag1=0,flag2=0,height,x1,y1,x2,y2,calc1,calc2x,calc2y;
Thread t=null;
public void init()
{
//Toolkit toolkit=Toolkit.getDefaultToolkit();
t=new Thread();
width=getSize().width;
height=getSize().height;
x1=0;y1=height/2;
x2=width-10;y2=height/2;
addMouseListener(this);
addMouseMotionListener(this);
addKeyListener(this);
setBackground(Color.gray);
repaint();
}
public void keyPressed(KeyEvent e)
{
int c=e.getKeyCode();
System.out.println(c);
if(c==KeyEvent.VK_LEFT)
{
System.out.println("yeah it's on");
x1-=10;
}
else if(c==KeyEvent.VK_UP)
y1-=10;
else if(c==KeyEvent.VK_RIGHT)
x1+=10;
else if(c==KeyEvent.VK_DOWN)
y1+=10;
if(x1>=0 && y1>=0 && y1<=height-20 && x1<=3*width/4)
repaint();
}
public void keyReleased(KeyEvent arg0) {
}
public void keyTyped(KeyEvent arg0) {
}
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e)
{
x2=e.getX();
y2=e.getY();
if(x2>=5*width/8 && x2<=width-20)
repaint();
}
public void mouseClicked(MouseEvent e)
{
flag2=1;
calc2x=x2;
calc2y=y2;
System.out.println(calc2x);
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
}
public void paint(Graphics g)
{
width=getSize().width;
height=getSize().height;
g.setColor(Color.green);
g.fillRect(x1, y1, 20, 20);
g.setColor(Color.red);
g.fillRect(x2, y2, 20, 20);
if(flag2==1)
{
g.setColor(Color.yellow);
while(true)
{
calc2x-=1;
System.out.println(calc2x);
g.fillOval(calc2x,calc2y,10,10);
try {
Thread.sleep(4);
} catch (InterruptedException e) {e.printStackTrace();}
if(calc2x<10)
{
flag2=0;
break;
}
}
}
}
#SuppressWarnings("static-access")
public void run()
{
if(flag2==1)
while(true)
{
{
repaint();
System.out.println("calc2x="+calc2x);
if(calc2x<10)
{
flag2=0;
}
try
{
t.sleep(4);
} catch (InterruptedException e) {e.printStackTrace();}
calc2x-=1;
}
}
}
}
NEVER have Thread.sleep(...) in a paint method. EVER. This puts all your drawing to sleep. In fact simply calling Thread.sleep(...) in your GUI thread will be enough to put the GUI to sleep, but its worse still in a paint method, since that method must be called over and over, and needs to be blazing fast and over in the blink of an eye or less.
Instead:
Create a Swing JApplet, not an AWT Applet
Override the paintComponent method of a JPanel to do your drawing
Use a Swing Timer to do your game loop.
Edit
You state in comment:
#HovercraftFullOfEels if you can write the syntax of swing timer and swing applet it would be of great help....
You appear to be wanting me to write tutorials for you. I wish I had all the time to do that, but alas, I don't, and I feel that it would be much more efficient for both you and me for you to check out the decent tutorials with sample code that are already in existence just waiting for you to learn from. For example, please check out the following links:
The Java Tutorials, The Really Big Index
Java Applets
Using Swing Components
How to make Java Applets
How to Use Swing Timers
I'm attempting to make a program in java that uses a robot to press a specific key every few seconds. It has a GUI with a start and stop button and a label which tells which state its in. I've got everything working so far except that when I click "start" it runs the loop for my robot function (which is infinite) it doesn't enable the stop button like I thought it would. I know its something stupid with where the infinite loop is placed but I'm not sure how to make it work correctly.
I don't do a lot of java work, this was just a fun thing I thought to try but got stuck part way through. Any help is appreciated.
import java.awt.AWTException;
import java.awt.FlowLayout;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Main extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private static boolean running = false;;
private JButton start_button;
private JButton stop_button;
private JLabel tl;
private static int i = 0;
Robot robot;
void start() {
JFrame frame = new JFrame("Helper");
tl = new JLabel("Running: " + running);
start_button = new JButton("Start");
stop_button = new JButton("Stop");
stop_button.setEnabled(false);
frame.add(tl);
frame.add(start_button);
frame.add(stop_button);
frame.setSize(300, 100);
frame.setVisible(true);
frame.setLayout(new FlowLayout());
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setLocation(400, 400);
try {
robot = new Robot();
} catch (AWTException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
robot.setAutoDelay(200);
start_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(false);
stop_button.setEnabled(true);
running = true;
tl.setText("Running: " + running);
while (running) {
robot_loop(robot);
}
}
});
stop_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(true);
stop_button.setEnabled(false);
running = false;
tl.setText("Running: " + running);
}
});
}
public static void main(String[] args) {
new Main().start();
}
private static void robot_loop(Robot robot) {
robot.keyPress(KeyEvent.VK_NUMPAD0);
robot.keyRelease(KeyEvent.VK_NUMPAD0);
System.out.println("numpad 0 pressed! - " + i);
i++;
}
}
I've adapted my comment into an answer.
The actionPerformed method of those event listeners are invoked on Swing's event dispatch thread, and since you're entering into an infinite loop, it'll cause the GUI to freeze. You could create a thread inside of your actionPerformed method and do your work inside of the new thread. Though the next issue you'd run into is finding a nice way to stop the thread whenever the user presses the stop button.
What's cool is that you've already got all the logic to do this in your code. So getting it to work is as simple as changing:
start_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(false);
stop_button.setEnabled(true);
running = true;
tl.setText("Running: " + running);
while (running) {
robot_loop(robot);
}
}
});
To do your work on its own thread:
start_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(false);
stop_button.setEnabled(true);
running = true;
tl.setText("Running: " + running);
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override public void run() {
while (running) {
robot_loop(robot);
}
}
});
}
});
The code above makes use of the executors framework (java.util.concurrent.*) rather than directly creating a thread. Another alternative as nachokk suggested would be to use a timer java.util.Timer or javax.swing.Timer (either should be fine in this case).
You can do something like this using SwingTimer
int delay = 400*1000;// you can inject this property
ActionListener taskPerformer = new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt2) {
robot_loop(robot);
}
};
Timer timer = new Timer(delay, taskPerformer);
timer.start();
i use from a class that extended from jframe and it has a button(i use from it in my program)
i want when run jframe in my program the whole of my program pause
until i press the button.
how can i do it
in c++ getch() do this.
i want a function like that.
Pausing Execution with Sleep, although I doubt that is the mechanism that you'll want to use. So, as others have suggested, I believe you'll need to implement wait-notify logic. Here's an extremely contrived example:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
#SuppressWarnings("serial")
public class PanelWithButton extends JPanel
{
// Field members
private AtomicBoolean paused;
private JTextArea textArea;
private JButton button;
private Thread threadObject;
/**
* Constructor
*/
public PanelWithButton()
{
paused = new AtomicBoolean(false);
textArea = new JTextArea(5, 30);
button = new JButton();
initComponents();
}
/**
* Initializes components
*/
public void initComponents()
{
// Construct components
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
add( new JScrollPane(textArea));
button.setPreferredSize(new Dimension(100, 100));
button.setText("Pause");
button.addActionListener(new ButtonListener());
add(button);
// Runnable that continually writes to text area
Runnable runnable = new Runnable()
{
#Override
public void run()
{
while(true)
{
for(int i = 0; i < Integer.MAX_VALUE; i++)
{
if(paused.get())
{
synchronized(threadObject)
{
// Pause
try
{
threadObject.wait();
}
catch (InterruptedException e)
{
}
}
}
// Write to text area
textArea.append(Integer.toString(i) + ", ");
// Sleep
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
}
}
}
}
};
threadObject = new Thread(runnable);
threadObject.start();
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(400, 200);
}
/**
* Button action listener
* #author meherts
*
*/
class ButtonListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent evt)
{
if(!paused.get())
{
button.setText("Start");
paused.set(true);
}
else
{
button.setText("Pause");
paused.set(false);
// Resume
synchronized(threadObject)
{
threadObject.notify();
}
}
}
}
}
And here's your main class:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class MainClass
{
/**
* Main method of this application
*/
public static void main(final String[] arg)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PanelWithButton());
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
});
}
}
As you can see, this example application will continually write to the text area until you click the button that reads 'Pause', whereupon to resume you'll need to click that same button which will now read 'Start'.
You don't say what you mean by pause. What is your app doing?
As a rule of thumb you CAN'T pause a UI app. User interface applications run from a message processing loop. Message comes in, message is dispatched, loop waits for another message. An app still needs to handles things like the user clicking on buttons, resizing the window, closing the app and so forth so this loop runs continuously.
If you want your application to "pause" in the sense of prevent the user doing something, just grey out whatever button or menu it is you don't want users to be doing.
If your app is running a thread in the background and wish it to suspend that action until you resume it, you can do so fairly easily like this.
MyThread mythread = new MyThread();
// Main thread
void pause() {
mythread.pause = true;
}
void resume() {
synchronized (mythread) {
mythread.pause = false;
mythread.notify();
}
}
class MyThread extends Thread {
public boolean pause = false;
public void run() {
while (someCondition) {
synchronized (this) {
if (pause) {
wait();
}
}
doSomething();
}
}
}
It is also possible to use Thread.suspend(), Thread.resume() to accomplish similar but these are inherently dangerous because you have no idea where the thread is when you suspend it. It could have a file open, be half way through sending a message over a socket etc. Putting a test in whatever loop controls your thread allows you do suspend at a point when it is safe to do so.
This answer entirely depends on whether I understand your question correctly, please give a bit more info if you want better answers. Here goes:
Pausing in a loop scenario
boolean paused;
while(true ) {
if(paused)
{
Thread.sleep(1000); // or do whatever you want in the paused state
} else {
doTask1
doTask2
doTask3
}
}
Threads:
You can also put those tasks into a seperate thread and not on the GUI thread which is typically what you would do for long running operations.
Pausing a thread is very easy. Just call suspend() on it. When you want to unpause call resume(). These methods however are dangerous and have been deprecated. Better or rather safer way to do it would be similar to the above by checking a pause flag.Here is a short example I had lying around in my snippets. Cant exactly remember where I got it in the first place:
// Create and start the thread
MyThread thread = new MyThread();
thread.start();
while (true) {
// Do work
// Pause the thread
synchronized (thread) {
thread.pleaseWait = true;
}
// Do work
// Resume the thread
synchronized (thread) {
thread.pleaseWait = false;
thread.notify();
}
// Do work
}
class MyThread extends Thread {
boolean pleaseWait = false;
// This method is called when the thread runs
public void run() {
while (true) {
// Do work
// Check if should wait
synchronized (this) {
while (pleaseWait) {
try {
wait();
} catch (Exception e) {
}
}
}
// Do work
}
}
} // Create and start the thread
MyThread thread = new MyThread();
thread.start();
while (true) {
// Do work
// Pause the thread
synchronized (thread) {
thread.pleaseWait = true;
}
// Do work
// Resume the thread
synchronized (thread) {
thread.pleaseWait = false;
thread.notify();
}
// Do work
}
class MyThread extends Thread {
boolean pleaseWait = false;
// This method is called when the thread runs
public void run() {
while (true) {
// Do work
// Check if should wait
synchronized (this) {
while (pleaseWait) {
try {
wait();
} catch (Exception e) {
}
}
}
// Do work
}
}
}
Hope this helps
try my java pause button:
package drawFramePackage;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Milliseconds2 implements ActionListener, MouseListener{
JFrame j;
Timer t;
Integer onesAndZeros, time, time2, placeHolder2;
Boolean hasFired;
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new Milliseconds2();
}
public Milliseconds2(){
j = new JFrame();
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.setSize(new Dimension(300, 300));
j.setVisible(true);
j.addMouseListener(this);
onesAndZeros = new Integer(0);
time = new Integer(0);
time2 = new Integer(0);
placeHolder2 = new Integer(0);
hasFired = new Boolean(true);
t = new Timer(2400, this);
time = (int) System.currentTimeMillis();
t.start();
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
if (onesAndZeros.equals(0)){
t.stop();
if (hasFired){
time2 = t.getDelay() - ((int) System.currentTimeMillis() - time);
}
else{
time2 -= (int) System.currentTimeMillis() - placeHolder2;
}
if (hasFired){
hasFired = false;
}
onesAndZeros = -1;
}
if (onesAndZeros.equals(1)){
//System.out.println(time2);
t.setInitialDelay(time2);
t.start();
placeHolder2 = (int) System.currentTimeMillis();
onesAndZeros = 0;
}
if (onesAndZeros.equals(-1)){
onesAndZeros = 1;
}
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
time = (int) System.currentTimeMillis();
hasFired = true;
System.out.println("Message");
}
}
Freezing your Main Thread will effectively freeze the entire program and could cause the operating system to think the application has crashed, not quite sure so correct me if I'm wrong. You could try to hide/disable the controls and enable them again when the user clicks on your button.
UI performs task using message driven mechanism.
If you have a button in your UI and you want to run something when that button is pressed, you should add an object of ActionListener to your button. Once the button is pressed, it fires the ActionListener object to perform a task, e.g.:
button.addActionListener(new ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
If you want to stop something when you press a pause button, you will defnitely need a Thread. This is more complicated than the former case.
I am creating an touch screen application using Swing and have a request to change one of buttons so that it will behave like a keyboard when the button is held down.
(First of all, I am not sure that the touch screen will allow the user to "hold down" the button, but pretend that they can for now)
I was going to go down the path of starting a loop when mousePressed was called and then ending the loop when mouseReleased was called. This will involve starting a thread and having to deal with synchronization as well as invokeLater() to get events back on the EventQueue.
Is there a very simple way to do what I want? I hope I am just not seeing the API to do it.
javax.swing.Timer is your friend. And here's an article with some more info.
I would do it like this:
Listen to mousePressed and schedule a java.util.Timer to be launched at a later time.
The Timer does the action and set itself to schedule again.
Listen to mouseReleased to cancel the Timer.
I went with the java.swing.Timer since it will automatically post back to the Swing EventQueue and that is what I am looking for. Thanks for the help.
Here's how you can do it by subclassing JButton:
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
public class TypomaticButton extends JButton implements MouseListener {
private boolean autotype = false;
private static Thread theThread = null;
private String myName = "unknown";
private int
speed = 150,
wait = 300,
decrement = (wait - speed) / 10;
TypomaticButton(Action action){
super(action);
myName = action.getValue(Action.NAME).toString();
addMouseListener(this);
}
TypomaticButton(String text){
super(text);
setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
myName = text;
addMouseListener(this);
}
#Override
public void mouseClicked(MouseEvent arg0) {}
#Override
public void mouseEntered(MouseEvent arg0) { }
#Override
public void mouseExited(MouseEvent arg0) { }
#Override
public void mousePressed(MouseEvent arg0) {
autotype = true;
theThread = new Thread(new Runnable() { // do it on a new thread so we don't block the UI thread
#Override
public void run() {
for (int i = 10000; i > 0 && autotype; i--) { // don't go on for ever
try {
Thread.sleep(wait); // wait awhile
} catch (InterruptedException e) {
break;
}
if(wait != speed){
wait = wait - decrement; // gradually accelerate to top speed
if(wait < speed)
wait = speed;
}
SwingUtilities.invokeLater(new Runnable() { // run this bit on the UI thread
public void run() {
if(!autotype) // it may have been stopped meanwhile
return;
ActionListener[] als = getActionListeners();
for(ActionListener al : als){ // distribute to all listeners
ActionEvent aevent = new ActionEvent(getClass(), 0, myName);
al.actionPerformed(aevent);
}
}
});
}
autotype = false;
}
});
theThread.start();
}
#Override
public void mouseReleased(MouseEvent arg0) {
autotype = false;
wait = 300;
}
void speed(int millisecs){
speed = millisecs;
decrement = (wait - speed) / 10;
}
void stop(){
autotype = false;
if(theThread != null){
theThread.interrupt();
}
}
}
It accelerates too.
Hope that helps.