Java swing graphics not updating - java

I have a java application that uses Swing graphics which has a 2D array of textfields, each of which are updating their text properly throughout the application. On each iteration of change, I change the text of the textfield then I make it's background green for half a second and turn it back to white. The issue is that, after the first iteration of the change, the textfields no longer flash green. When I comment out the portion that converts the background back to white, the rest works and the cells progressively turn green one by one (correctly), which indicates that it is working and executing properly. I tried to address this by repainting and revalidating the UI but it's not working. What is going on?
Below is my code that updates the UI.
try {
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
textfieldArray[row][col].setBackground(Color.green);
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
Thread.sleep(300);
textfieldArray[row][col].setBackground(Color.white);
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Swing is a single threaded framework, it also NOT thread safe. This means two things.
First, you should never perform any long running or blocking operations within the context of the Event Dispatching Thread and...
Secondly, you should never update/modify the UI, or anything the UI depends on, from outside the context of the Event Dispatching Thread.
This means, your Thread.sleep is blocking the EDT, preventing from process paint requests.
Instead, you need some way you can trigger an update to occur after a specified delay. If that doesn't scream Swing Timer, I don't know what does
I would highly recommend taking the time to read through Concurrency in Swing for the background of why your code isn't working and possibly How to Use Swing Timers for a solution
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextField field;
public TestPane() {
setLayout(new GridBagLayout());
field = new JTextField("All your bases beloging to us", 20);
JButton blink = new JButton("Blink");
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
field.setForeground(null);
field.setBackground(null);
}
});
timer.setRepeats(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(field, gbc);
blink.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
field.setForeground(Color.WHITE);
field.setBackground(Color.GREEN);
timer.start();
}
});
add(blink, gbc);
}
}
}

Related

Loop Instead of Button for displaying images in JLabel? [duplicate]

in a java aplication I have a Jlabel which i want to assign a new image to every time i click a button, using a for loop i can get it to just display the last image skipping all in between images, i know there is a error in my logic here maybe i should not be using a for loop?? any advice
private String imageList[];
ImageIcon image;
imageList = new String[] {"src\\Tour_Eiffel_Wikimedia_Commons.jpg","src\\Ben.jpg", "src\\Rio.jpg", "src\\Liberty.jpg", "src\\Pyramid.jpg"};
//constructor setting first image to display on load
public GeographyGameGUI() {
image = new ImageIcon(imageList[0]);
imageLbl.setIcon(image);
}
//button method
private void nextBtnActionPerformed(java.awt.event.ActionEvent evt) {
for (imgCount = 1; imgCount < imageList.length; imgCount++) {
image = new ImageIcon(imageList[imgCount]);
imageLbl.setIcon(image);
}
if i dont use a for loop and simply use a counter (displayed below) which i declare outside of the button method it loops correctly displaying the images but runs into a ArrayIndexOutOfBoundsException. what is the best practice here? thanks
image = new ImageIcon(imageList[imgCount]);
imageLbl.setIcon(image);
imgCount++;
You're, essentially, blocking the Event Dispatching Thread, prevent it from updating the UI. See Concurrency in Swing for more details
Instead, you should use a javax.swing.Timer to loop over the images, allowing the UI to update before changing to the next one...
See How to use Swing Timers for more details.
Java arrays are zero indexed, this means that the first element in the array is a position 0, not 1
Don't reference src directly within your code, the src directory will not exist once the application is built and packaged
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private String[] imageList = new String[] {"/Tour_Eiffel_Wikimedia_Commons.jpg","/Ben.jpg", "/Rio.jpg", "/Liberty.jpg", "/Pyramid.jpg"};
public TestPane() {
setLayout(new BorderLayout());
label = new JLabel();
add(label);
JButton btn = new JButton("Play");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setEnabled(false);
Timer timer = new Timer(1000, new ActionListener() {
private int count;
#Override
public void actionPerformed(ActionEvent e) {
if (count < imageList.length) {
try {
label.setIcon(
new ImageIcon(
ImageIO.read(
TestPane.this.getClass().getResource(imageList[count]))));
} catch (IOException exp) {
exp.printStackTrace();
}
count++;
} else {
((Timer)e.getSource()).stop();
}
}
});
timer.stop();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Your counter reaches the end of the array so you get out of bounds exception. After each increment you should check whether the end of array has been reached, and if so, set the counter to 0.
If you want to iterate over a few images with a delay on single click you need to use SwingWorker. Using delays in your action listener will suspend event dispatch thread, which means that no other updates or interactions with swing components will be available (it is likely that refreshes will not be done correctly too).
If you do a few updates (setIcon) in a very short time, Swing usually refreshes the component after the last of them, which means that only last image will be visible.
Have a look here: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html

multiple dispatchEvent calls on a JButton

I have a customized JButton in a Java Swing application.
it changes it appearances according to mouse event.
MouseEvent.MOUSE_ENTERED - will trigger the hover image for the button. MouseEvent.MOUSE_PRESSED - will trigger the pressed image.
MouseEvent.MOUSE_RELEASED - will change the foreground to gray and render the button disabled.
This is working fine with actual mouse clicks.
I want to add a support for pressing the ENTER key.
simply calling button.doClick() did not go through the hover-press-release cycle, but simply jumped to the release event.
So I have this short and effective code for doing this.
InputMap im = workspacePnl.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = workspacePnl.getActionMap();
im.put(KeyStroke.getKeyStroke("ENTER"), "connect");
am.put("connect", new ConectAction());
private class ConectAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent ev) {
simulateClick();
}
and the problematic code:
public void simulateClick() {
MouseEvent evt;
evt = new MouseEvent(connectBtn,
MouseEvent.MOUSE_ENTERED, 1, 0, 0, 0, 1, false);
connectBtn.dispatchEvent((AWTEvent) evt);
//CommonUtil.sleep(300);
evt = new MouseEvent(connectBtn,
MouseEvent.MOUSE_PRESSED, 8, 0, 0, 0, 1, false);
connectBtn.dispatchEvent((AWTEvent) evt);
//CommonUtil.sleep(300);
evt = new MouseEvent(connectBtn,
MouseEvent.MOUSE_RELEASED, 20, 0, 0, 0, 1, false);
connectBtn.dispatchEvent((AWTEvent) evt);
}
I am trying to make the ENTER press go through the same route: trigger a MOUSE_ENTERED event which will alter the button's appearance for hover, followed by MOUSE_PRESSED and MOUSE_RELEASED.
But I only see the last event effect. it is as if i'm only firing the last event alone which lacks the liveliness for an interactive software.
I tried (as can be seen commented out) to have the thread go to sleep after each event firing, but it has no effect.
If I try to fire each of the other two events they are noticeable on the screen by themselves. it's the batching together that messes things up.
How can I fire a series of dispatchEvents one by one which will all be noticed by the user? how can i make the program wait for the current dispatchEvent to work it's magic before striding on to the next on?
Any help or insights would be greatly appreciated.
How can I fire a series of dispatchEvents one by one which will all be
noticed by the user? how can i make the program wait for the current
dispatchEvent to work it's magic before striding on to the next on?
Mouse and Key Event are correctly implemented in ButtonComponents, don't use MouseListener, to use events from ButtonModel, by using ChangeListener, for example
MouseEvent.MOUSE_ENTERED - will trigger the hover image for the button.
Make use of the roll over support supplied by the button, see JButton#setRolloverEnabled & JButton#setRolloverIcon
MouseEvent.MOUSE_PRESSED - will trigger the pressed image.
Is a little more difficult, but you can use listener to the ButtonModel for changes and update the icon based on you requirements
MouseEvent.MOUSE_RELEASED - will change the foreground to gray and render the button disabled.
Should probably be achieved through the use a ActionListener
I am trying to make the ENTER press go through the same route:
JButton#doClick will go through the isArmed and isPressed states of the model automatically, which will trigger the state changes provided by the previous comments...
With the mouse...
With the keyboard...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
JButton btn = new JButton();
try {
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash01.png"))));
btn.setRolloverIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash02.png"))));
btn.setRolloverEnabled(true);
// btn.setSelectedIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash03.png"))));
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (btn.getModel().isPressed()) {
try {
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash03.png"))));
} catch (IOException ex) {
ex.printStackTrace();
}
} else {
try {
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash01.png"))));
} catch (IOException ex) {
ex.printStackTrace();
}
}
System.out.println("Armed: " + btn.getModel().isArmed());
System.out.println("Enabled: " + btn.getModel().isEnabled());
System.out.println("Pressed: " + btn.getModel().isPressed());
System.out.println("Rollover: " + btn.getModel().isRollover());
System.out.println("Selected: " + btn.getModel().isSelected());
}
});
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(btn, gbc);
add(new JTextField("Stealer of focus"), gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setEnabled(false);
}
});
InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke("ENTER"), "connect");
am.put("connect", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ev) {
System.out.println("click");
btn.doClick();
}
});
}
}
}
It's batching together because your whole code is running on the EventDispatchingThread (EDT).
When you call simulateClick() from actionPerformed() call it on a new thread instead of the same thread (which will be EDT).

JFrame Start Up Display Delay

I am having difficulties displaying a JFrame with all of the contents showing immediately. I would like to show the JFrame with all Components already added, but the window appears to become visible out-of-focus, wait a second, then come into focus with the Component(s) added. The code for my SSCCE is extremely short/simple, so I don't know why this effect is happening.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DelayTest
{
public static void main(String[] args)
{
JLabel label = new JLabel("DELAY TEST");
label.setHorizontalAlignment(JLabel.CENTER);
label.setPreferredSize(new Dimension(400, 60));
JFrame frame = new JFrame("Delay Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I know that this effect does not happen if I comment out label.setPreferredSize(new Dimension(400,60));, so though it may simply be a correlation & not a causation, I'm guessing somehow PreferredSizes have to do with it. But, why?
ALSO, this effect occurs even if I create the GUI in a separate Runnable thread apart from the main thread. Although clearly the effect is related to the order of thread execution, I do not believe that specific factor is the cause.
Upon startup:
About 1 second later:
The likely delay is caused by the need for the system to initialize the synchronize the Event Dispatching Thread with the native system before it can render content.
You should make sure that your UI is initialised and shown from within the context of the Event Dispatching Thread, which should make the initial startup more synchronised...
Take a look at Initial Threads for more details
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestDelay {
public static void main(String[] args) {
new TestDelay();
}
public TestDelay() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JLabel label = new JLabel("Delay Test");
Font font = label.getFont();
label.setFont(font.deriveFont(Font.BOLD, 24f));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Change JTextField enabled background color

i've got a question about JTextField background color. How can I change it in enabled text field (while editing) ? setBackground works only for disabled text field. UIManager.put can change this background for all of my text field in window but i want to do that only for one of them.
There's a number of ways you might achieve this, but the basic idea is, when the field gets focus, you want to set the fields background color to something else and when it loses focus, you want to reset it...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FocusedField {
public static void main(String[] args) {
new FocusedField();
}
public FocusedField() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTextField field1 = new JTextField(20);
JTextField field2 = new JTextField(20);
FocusListener highlighter = new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
e.getComponent().setBackground(Color.GREEN);
}
#Override
public void focusLost(FocusEvent e) {
e.getComponent().setBackground(UIManager.getColor("TextField.background"));
}
};
field1.addFocusListener(highlighter);
field2.addFocusListener(highlighter);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.gridwidth = gbc.REMAINDER;
frame.add(field1, gbc);
frame.add(field2, gbc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
I would be tempted to write a simple singleton "manager" which allowed you to register and unregister fields as you needed.
You might also be able to achieve something similar by attaching a PropertyChangeListener to the KeyboardFocusManager, this would allow you to basic apply this highlighting concept to all fields within in any program without the need to change any of the code, but that would depend on your requirements
Just add an ActionListener on your textField, and then set the Background in the Listener.
Ok, there is what i needed:
Properties props = new Properties(); props.put("showFocusFrame", "off");
((AbstractLookAndFeel)UIManager.getLookAndFeel()).getTheme().setProperties(prop‌​s);
I think it works with textField.setForeground(Color.RED) :)

Displaying a String letter by letter "animation" in a JLabel?

Is there any way to show a phrase, per se, "Welcome!", letter by letter with a very small delay between them? I would provide what I've tried but I haven't even come close to barely working, nothing worth mentioning. I suppose I would have to use a loop containing a scanner, yes? Any help appreciated, thanks:)
Caveats
Swing is a single threaded framework, that is, all updates and modifications to the UI are expected to be executed from within the context of the Event Dispatching Thread.
Equally, any operation which blocks the EDT will prevent it from processing (amongst other things), paint updates, meaning that the UI won't be updated until the block is removed.
Example
There are a few ways you could achieve this. You could use a SwingWorker and while it would be a good learning exercise, it would probably be a little over kill for this problem.
Instead, you can use a javax.swing.Timer. This allows you to schedule callbacks at regular intervals, these callbacks are executed within the context of the EDT which will you allow you to update the UI safely.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AnimatedLabel {
public static void main(String[] args) {
new AnimatedLabel();
}
public AnimatedLabel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.setSize(100, 100);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private String text = "Hello";
private JLabel label;
private int charIndex = 0;
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel();
add(label);
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String labelText = label.getText();
labelText += text.charAt(charIndex);
label.setText(labelText);
charIndex++;
if (charIndex >= text.length()) {
((Timer)e.getSource()).stop();
}
}
});
timer.start();
}
}
}
Take a look at Concurrency in Swing for more details
Update from comments
The main problem is your text value is wrapped in <html>
static String text = "<html>Welcome! I will ask simple, two-answer questions, and you will answer them. Simple as that. </html>";
And then you apply it to your label...
final JLabel centerText = new JLabel(text);
So when the timer runs, it ends up append the text again...
"<html>Welcome! I will ask simple, two-answer questions, and you will answer them. Simple as that. </html><html>Welcome! I will ask simple, two-answer questions, and you will answer them. Simple as that. </html>"
Which is invalid, because everything after the </html> will be ignored.
Instead, remove the <html> tags from the text
static String text = "Welcome! I will ask simple, two-answer questions, and you will answer them. Simple as that.";
And set the initial text of the label with <html>
final JLabel centerText = new JLabel("<html>);
Don't worry, Swing will take care of it...

Categories