I have a GUI class Gui:
public class Gui extends JFrame implements Runnable
{
private JPanel outer, inner;
private JLabel[][] labels = new JLabel[22][12];
private Color[][] defaultMap, map;
Thread t;
private int row, col;
private Color color;
public Gui()
{
Container content = getContentPane();
content.setLayout(new BorderLayout());
setBackground(Color.BLACK);
setSize(1000, 1000);
setLocation(300, 0);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
defaultMap = createMap();
draw(defaultMap);
}
public Color[][] createMap()
{
Color[][] map = new Color[22][12];
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 12; j++)
{
map[i][j] = Color.WHITE;
}
}
for (int i = 0; i < 22; i++)
{
map[i][0] = Color.GRAY;
map[i][11] = Color.GRAY;
}
for (int i = 0; i < 12; i++)
{
map[0][i] = Color.GRAY;
map[21][i] = Color.GRAY;
}
return map;
}
public void draw(Color[][] map)
{
outer = new JPanel();
outer.setLayout(new BorderLayout());
outer.setBackground(Color.WHITE);
outer.setPreferredSize(new Dimension());
inner = new JPanel();
inner.setLayout(new GridLayout(22, 12, 2, 2));
inner.setBackground(Color.BLACK);
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 12; j++)
{
labels[i][j] = new JLabel();
JLabel label = labels[i][j];
label.setPreferredSize(new Dimension(20, 20));
label.setBackground(map[i][j]);
label.setOpaque(true);
inner.add(label);
}
}
add(outer);
add(inner);
pack();
}
public void move(int row, int col, Color color)
{
System.out.println(row+","+col);
map = defaultMap;
map[row][col] = color;
t = new Thread(this);
t.start();
}
#Override
public void run()
{
draw(map);
}
}
Which is called from my main class like so:
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
gui = new Gui();
gui.setVisible(true);
gui.move(2,5,Color.GREEN);
Thread.sleep(1000);
gui.move(3,5,Color.GREEN);
Thread.sleep(1000);
gui.move(4,5,Color.GREEN);
} catch (InterruptedException ex)
{
Logger.getLogger(Tetris.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
So the weird stuff is happening when the gui.move() function is called. You can ignore the rest or use it if it helps. But each time, a green block should be "added" to the gui at 2,5; 3,5; and 4,5; 1 second after the other.
The issue:
The Gui remains black for a time then immediately repaints/updates with the proper grid and colors with the first two blocks correctly colored green but it's missing the last block at 4,5. Again, the initial "defaultMap" should be painted immediately, but it is not, the JFrame is black until everything is painted together at once minus the last green block. Then each green block should be painted on 1 second after the other.
The interesting part is that the System.out.println() bit in the move method in Gui prints out the row and col as I'd expect it to... They come out about one second after the other in the terminal. So that tells me that something is going right. But I'm not sure what's going on with the Gui...
EDIT: slight difference in story. upon closer examination, I noticed that the last green block does appear for a second as soon as the entire map is painted but immediately "disappears" repainted white.
You are sleeping on the EDT (Event Dispatching Thread). You should never block the EDT.
Never call sleep or wait on the EDT
Move all long running tasks to other Threads (using Executors or SwingWorker)
For repeated or delayed update of the UI, you can use javax.swing.Timer
All UI-operations should be performed on the EDT (using SwingUtilities.invokeLater or javax.swing.Timer or SwingWorker
Read more about Swing concurrency in the swing tag wiki.
Related
I have a assignment that i had to create and array of buttons for a paint app project and everything is done but i am stuck on assigning/referencing/modifying my action listener.
The way it works(supposedly) is that i have a loop that creates, assigns a name, and adds to the action listener an Array of JButtons named myShapes. From this, I am supposed to have the actionlistener named listener(); give a int 1-6 to each jbutton, setting the current actionPerformed state.
int actionNum = 1;
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
currentAction = actionNum;
}
};
//........................................................................
String[] myShapesName = {"brushBut", "lineBut", "ellipseBut", "rectBut", "strokeBut", "fillBut"};
//String[] myLisName = {"brushButL", "lineButL", "ellipseButL", "rectButL", "strokeButL", "fillButL"};
JButton[][] myShapes = new JButton[3][2];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
myShapes[i][j] = new JButton();
myShapes[i][j].setBounds(0, 0, 50, 50);//(100, 200);
myShapes[i][j].setName(myShapesName[i+j]);
myShapes[i][j].setBackground(null);
myShapes[i][j].putClientProperty("id",
String.valueOf(myShapesName[i+j]).concat(String.valueOf(j)));
MyShapesBox.add(myShapes[i][j]);
System.out.println(listener);
}
}
the problem is that i dont know how to change the actionlistener so that it gives all 6 Jbuttons a different actionPerformed state because each state represents a different brush. I tried several ways of like trying to make a Action list array and renaming them but that gives an error or breaks to loop which is needed for the assignment and ended with this final attempt. unfortunately, this currently is beyond me. If been on this for 2 days and Im stumped.
My first thought would be to create an action class, which took a int value as part of it's constructor, for simplicity sake, I'd make it a inner class, but you could use an outer class, but you'd need to pass more details to it, for example...
public class TestPane extends JPanel {
private int currenAction;
public TestPane() {
String[] myShapesName = {"brushBut", "lineBut", "ellipseBut", "rectBut", "strokeBut", "fillBut"};
//String[] myLisName = {"brushButL", "lineButL", "ellipseButL", "rectButL", "strokeButL", "fillButL"};
setLayout(new GridLayout(3, 2));
JButton[][] myShapes = new JButton[3][2];
int action = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
myShapes[i][j] = new JButton();
myShapes[i][j].setBounds(0, 0, 50, 50);//(100, 200);
myShapes[i][j].setName(myShapesName[i + j]);
myShapes[i][j].setBackground(null);
myShapes[i][j].putClientProperty("id",
String.valueOf(myShapesName[i + j]).concat(String.valueOf(j)));
myShapes[i][j].addActionListener(new ButtonAction(action));
action++;
add(myShapes[i][j]);
}
}
}
public class ButtonAction implements ActionListener {
private int actionNum;
public ButtonAction(int actionNum) {
this.actionNum = actionNum;
}
#Override
public void actionPerformed(ActionEvent arg0) {
currenAction = actionNum;
System.out.println(actionNum);
}
}
}
I'm trying to make a simple connect four GUI by using a grid of JPanels each of which paints a colored disk when the button below them is pressed and the panels under it are full. Before adding the game rules I'm trying to just make sure the buttons and display work properly. But it is not working - only the top left panel displays a disk (after pressing button 1 6 times). here is my code:
public class ConnectFourFrame extends JFrame {
private final JPanel gamePanelsPanel; // panel to hold the game panels
private final GamePanel[][] gamePanels; // a 2D array to hold the grid of panels to display the game disks
private final JPanel buttonsPanel; // panel to hold the buttons panels
private final JPanel gameButtonsPanel; // panel to hold the game buttons to add disk to a column
private final JButton[] gameButtons; // an array to hold the game buttons
private final JPanel clearButtonPanel; // panel to hold the clear button
private final JButton clearButton; // button to clear the game grid from disks
private enum Turn {RED_PLAYER, BLUE_PLAYER}; // keeps track of which players turn it is
private Turn turn;
// no argument constructor
public ConnectFourFrame() {
super("Connect Four");
this.setLayout(new BorderLayout());
//add panels to hold the game panel and the buttons
gamePanelsPanel = new JPanel();
add(gamePanelsPanel, BorderLayout.CENTER);
buttonsPanel = new JPanel();
buttonsPanel.setLayout(new BorderLayout());
add(buttonsPanel, BorderLayout.SOUTH);
//set up game panels
gamePanelsPanel.setLayout(new GridLayout(6,7,3,3));
gamePanelsPanel.setBackground(Color.BLACK);
gamePanels = new GamePanel[6][7];
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
gamePanels[i][j] = new GamePanel(false, Color.WHITE);
gamePanelsPanel.add(gamePanels[i][j]);
}
}
//set up game and clear buttons
gameButtonsPanel = new JPanel();
gameButtonsPanel.setLayout(new GridLayout(1,7));
clearButtonPanel = new JPanel();
gameButtons = new JButton[7];
for (int i = 0; i < 7; i++) {
gameButtons[i] = new JButton("" + (i+1));
gameButtonsPanel.add(gameButtons[i]);
}
clearButton = new JButton("CLEAR");
clearButtonPanel.add(clearButton);
buttonsPanel.add(gameButtonsPanel, BorderLayout.NORTH);
buttonsPanel.add(clearButtonPanel, BorderLayout.SOUTH);
add(buttonsPanel, BorderLayout.SOUTH);
// register event handlers
ClearButtonHandler clearButtonHandler = new ClearButtonHandler();
clearButton.addActionListener(clearButtonHandler);
GameButtonHandler gameButtonHandler = new GameButtonHandler();
for (int i = 0; i < 7; i++) {
gameButtons[i].addActionListener(gameButtonHandler);
}
turn = Turn.RED_PLAYER; //set first turn to player1
}
// inner class for game button event handling
private class GameButtonHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// get the number of the pressed button
int pressedButtonNum = Integer.parseInt(((JButton) e.getSource()).getActionCommand());
// display disk in top empty panel of the column
for (int i = 5; i >= 0; i--) {
if (!gamePanels[i][pressedButtonNum - 1].isFull()) {
if (turn == Turn.RED_PLAYER) {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.RED);
turn = Turn.BLUE_PLAYER;
}
else {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.BLUE);
turn = Turn.RED_PLAYER;
}
gamePanels[i][pressedButtonNum - 1].setFull(true);
gamePanels[i][pressedButtonNum - 1].repaint();
return;
}
}
// if column is full display message to try again
JOptionPane.showMessageDialog(gamePanelsPanel, "Column " + pressedButtonNum + " is full. Try again.");
}
}
public class GamePanel extends JPanel{
private boolean isFull; // true if the panel has a disk in it. default is empty (false).
private Color diskColor; //color of disks. default is white (same as background)
public GamePanel(boolean isFull, Color diskColor) {
super();
this.isFull = isFull;
this.diskColor = diskColor;
}
public Color getDiskColor() {
return diskColor;
}
public void setDiskColor(Color diskColor) {
this.diskColor = diskColor;
}
public boolean isFull() {
return isFull;
}
public void setFull(boolean isFull) {
this.isFull = isFull;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(diskColor);
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
}
}
The problem is right here...
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
The Graphics context passed to your paintComponent method has already been translated by the components x/y position, meaning that the top/left corner of the component is always 0x0
g.fillOval(this.getWidth()/4 , this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
will probably work better
Also, calling this.setBackground(Color.WHITE); inside paintComponent is unadvisable, as it will setup a situation where by a new paint cycle will be scheduled, over and over again. Don't change the state of the UI from within a paint method
Beginner Java developer. Trying to make a Tetris applet as part of my personal projects.
I'm at the point were I can draw tetris blocks onto the screen but I cannot make it vertically go downwards every second.
Code:
public class InitialScreen extends JApplet implements ActionListener {
public JPanel cards = new JPanel();
private JPanel introPanel = new JPanel();
public CardLayout c1 = new CardLayout();
public void init() {
initiateIntroScreen();
//game();
add(cards, BorderLayout.NORTH);
setSize(500, 100);
}
private void initiateIntroScreen() {
Frame title = (Frame)this.getParent().getParent();
cards.setLayout(c1);
JLabel centralWords = new JLabel("Click the following button options: 'Play' or 'Instructions'.");
JButton playBtn = new JButton("Play!");
JButton instructionsBtn = new JButton("Instructions!");
introPanel.add(centralWords);
introPanel.add(playBtn);
introPanel.add(instructionsBtn);
cards.add(introPanel,"1");
playBtn.addActionListener(this);
playBtn.addActionListener(new MainGame(cards,c1));
}
#Override
public void actionPerformed(ActionEvent e) {
setSize(300,410);
getContentPane().setBackground(Color.BLACK);
}
So this is the initial screen for the JApplet. Has two buttons. When you press the 'Play' button it goes to the Main Game Screen.
public class MainGame extends JApplet implements ActionListener {
private JPanel cards;
private CardLayout c1;
private JPanel gamePanel = new JPanel();
public MainGame(JPanel cards, CardLayout c1) {
this.c1 = c1;
this.cards = cards;
gamePanel.add(new Tetris_Block(new int[10][20]));
}
#Override
public void actionPerformed(ActionEvent e) {
JLabel scoreLbl = new JLabel("Score:");
gamePanel.add(scoreLbl);
cards.add(gamePanel,"game");
c1.show(cards,"game");
}
This is the game screen were Tetris is played. In the constructor it calls a Tetris Block.
public class Tetris_Block extends JComponent implements ActionListener {
static Color[] colors =
{darkGray, green, blue, red,
yellow, magenta, pink, cyan};
int[][] a;
int w, h;
static int horizontalPos, verticalPos = 0;
static int size = 20;
private int verticalPos1 = 1;
public Tetris_Block(int[][] a) {
this.a = a;
w = a.length;
h = a[0].length;
square_Block();
startTimer();
}
private void nextMove() {
verticalPos++;
verticalPos1++;
}
public void square_Block(){ //Horizontal || Vertical || Colour
//Horizontal never changes for this as I just want the blocks to go down.
a[0][verticalPos] = 3;
a[0][verticalPos1] = 3;
a[1][verticalPos] = 3;
a[1][verticalPos1] = 3;
}
#Override
public void actionPerformed(ActionEvent e) {
nextMove();
square_Block();
System.out.println(verticalPos);
}
public void startTimer(){
Timer timer = new Timer(1000,this);
timer.start();
}
public void paintComponent(Graphics g) {
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
g.setColor(colors[a[i][j]]);
g.fill3DRect(i * size, j * size,
size, size, true);
}
}
}
public Dimension getPreferredSize() {
return new Dimension(w * size, h * size);
}
My aim is to make the vertical position increment by 1 every second (So it goes down the window in second intervals.
I don't think the Timer function is the problem. When I print verticalPos it prints out the incremented value every second, however it's just displaying the new location onto the screen- that is the problem.
Image of window right now.
[img]http://i.imgur.com/au5fceO.png?1[/img]
Start by adding a call to repaint in you actionPerformed method of your Tetris_Block
#Override
public void actionPerformed(ActionEvent e) {
nextMove();
square_Block();
System.out.println(verticalPos);
// This is important
repaint();
}
This will schedule a paint event on the event queue which will eventually call your paintComponent method (indirectly)
This will get the block to start moving. The next problem you will have is you're not actually "removing" the block from it's previous position, so it will continue to bleed/grow down the screen
You could solve this by passing in the color of the block to square_Block, for example...
public void square_Block(int color) { //Horizontal || Vertical || Colour
//Horizontal never changes for this as I just want the blocks to go down.
a[0][verticalPos] = color;
a[0][verticalPos1] = color;
a[1][verticalPos] = color;
a[1][verticalPos1] = color;
}
And then "rest" the blocks of the current position, update the position and then set the new block colors;
#Override
public void actionPerformed(ActionEvent e) {
square_Block(0);
nextMove();
square_Block(3);
System.out.println(verticalPos);
repaint();
}
Your design here may be faulty. You need to have a game loop that runs in a separate thread. It has to be a separate thread from the main thread so the user can still click buttons. Once you have a loop in the separate thread you need to have a method that you call for every game tick. It's in that method that you update the coordinates of the blocks.
Game loop works like this:
1. Read state of the game and draw the blocks
2. Process user input.
3. Update game state
I know this is abstract but I hope it helps. Google about java games and game loops.
Right now i change the background color of a button by using
button.setBackground(Color.WHITE);
That being an example.
But when i have a massive grid out of jbuttons (1000+), just running a for loop to change every buttons background is very, very slow. You can see the grid slowly turning white, box by box. I really don't want this
Is there a better way of changing every JButton on the grid to the same color at the same time?
This is how i am making the grid, the numbers used are only for example...
grid = new JPanel(new GridLayout(64, 64, 0, 0));
That's 4096 buttons, takes about 30+ seconds to change every button to the same color.
Edit 1: I need the buttons to be clickable, like when i click a button it turns blue for example. when all of the buttons are clicked, change the color of every button to white. Right now i have that working fine, but it is just slow to change the color of every button.
Edit 2: this is how i am changing the buttons:
new javax.swing.Timer(300, new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent e) {
if (counter >= counterMax) {
((Timer) e.getSource()).stop();
}
Color bckgrndColor = (counter % 2 == 0) ? flashColor : Color.white;
for (JButton button : gridButton) {
button.setBackground(bckgrndColor);
}
counter++;
}
}).start();
The fact that you see the boxes being repainted individually indicates that either double buffering is turned off, or that the paint code in the button UI makes use of paintImmediately().
I tested your setup with 64x64 JButtons, an made sure that all UI operations were executed in the EDT (Event Dispatch Thread). I can confirm the effect you saw, changing the background of all buttons took about 1200 ms, with every box repainted immediately.
You can bypass the immediate repaints by setting the grid to non-visible before, and to visible after you changed the backgrounds:
grid.setVisible(false);
for (Component comp : grid.getComponents()) {
comp.setBackground(color);
}
grid.setVisible(true);
This caused the grid to do only one repaint, and reduced the time to ~300ms (factor 4).
This is still too slow for frequent updates, so you're better off with a custom component which draws the grid, or a flyweight container (what trashgod suggested in the comment to your question) if you want allow the grid cells to be arbitrary components.
You can get a considerable benefit if only visible buttons need to be repainted. In the MVC approach shown below, each button listens to a model that defines it's current state. Updating the model is quite fast compared to repainting. Although startup takes a few seconds, I see updates taking < 10 ms. in the steady-state. It's not as scalable as the flyweight pattern used by JTable, illustrated here, but it may serve.
import java.awt.*;
import java.awt.event.*;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import javax.swing.*;
/** #see https://stackoverflow.com/questions/6117908 */
public class UpdateTest {
private static final int ROW = 64;
private static final int COL = 64;
private static final int MAX = COL * ROW;
private final DataModel model = new DataModel(MAX);
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new UpdateTest().create();
}
});
}
void create() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(ROW, COL));
for (int i = 0; i < MAX; i++) {
panel.add(new ViewPanel(model, i));
}
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long start = System.nanoTime();
model.update();
System.out.println(
(System.nanoTime() - start) / (1000 * 1000));
}
});
JFrame f = new JFrame("JTextTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(panel), BorderLayout.CENTER);
f.setPreferredSize(new Dimension(800, 600));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
timer.start();
}
private static class ViewPanel extends JPanel implements Observer {
private final JButton item = new JButton();
private DataModel model;
private int index;
public ViewPanel(DataModel model, int i) {
this.model = model;
this.index = i;
this.add(item);
item.setText(String.valueOf(i));
item.setOpaque(true);
item.setBackground(new Color(model.get(index)));
model.addObserver(this);
}
#Override
public void update(Observable o, Object arg) {
int value = model.get(index);
item.setBackground(new Color(value));
}
}
private static class DataModel extends Observable {
private final Random rnd = new Random();
private final int[] data;
public DataModel(int n) {
data = new int[n];
fillData();
}
public void update() {
fillData();
this.setChanged();
this.notifyObservers();
}
public int get(int i) {
return data[i];
}
private void fillData() {
for (int i = 0; i < data.length; i++) {
data[i] = rnd.nextInt();
}
}
}
}
I am trying to flash the background colour in a textfield. My timer setup is as follows:
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
tmr.setInitialDelay(0);
tmr.setRepeats(true);
tmr.start();
My actionListener is as follows:
static class Flash implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.white);
}
else
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
Now, when I put this in debug and follow the action, the program does repeatedly step through flash and toggle between the two alternatives. But onscreen, only the first toggle
occurs. After that, no action, although flash is still functioning.
What is wrong here?
Thanks in advance for any help.
This example continually varies the saturation of a panel's background color:
import java.awt.*;
import java.awt.event.*;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.*;
public class FlashTest extends JPanel {
private static final Font font = new Font("Serif", Font.PLAIN, 32);
private static final String s = "Godzilla alert!";
FlashTest() {
this.setPreferredSize(new Dimension(256, 96));
this.setBackground(Color.red);
Timer t = new Timer(50, new Flash(this));
t.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
int xx = this.getWidth();
int yy = this.getHeight();
int w2 = g.getFontMetrics().stringWidth(s) / 2;
int h2 = g.getFontMetrics().getDescent();
g.setColor(Color.black);
g.drawString(s, xx / 2 - w2, yy / 2 + h2);
}
private static class Flash implements ActionListener {
private final float N = 32;
private final JComponent component;
private final Queue<Color> clut = new LinkedList<Color>();
public Flash(JComponent component) {
this.component = component;
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, 1 - (i / N), 1));
}
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, i / N, 1));
}
}
#Override
public void actionPerformed(ActionEvent e) {
component.setBackground(clut.peek());
clut.add(clut.remove());
}
}
static public void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new FlashTest());
f.pack();
f.setVisible(true);
}
});
}
}
There are a couple of problems here.
The first obvious thing is that you appear to be using mutable statics. This is a really bad idea and indicates (and causes!) confusion. In this particular case, one of the problems caused is that the flasher static is shared.
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
We are adding two Flash actions. Ordinarily this would be bad, but just produce an undetectable "bug". The colour would be set twice.
Bring these two things together, and we have two actions without a break that perform the same toggle. Two toggles. The state does not change (although there are repaint, property change events, etc.).
So, don't use mutable statics, and keep the code clean.
tmr = new javax.swing.Timer(1000, flash);
I tried your code and it works fine.
Why do you use a static context for SpreademPanel.historyPnl.NameTxt?
EDIT
You might want to redesign your class to pass the component in the constructor.
private class Flash implements ActionListener
{
private boolean flasher = false;
private JComponent component;
public Flash(JComponent component) {
this.component = component;
}
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
component.setBackground(Color.white);
}
else
{
component.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
and then init it with
Flash flash = new Flash(SpreademPanel.historyPnl.NameTxt);
Timer tmr = new javax.swing.Timer(1000, flash);
tmr.start();