How to execute several methods one after another in EDT (Swing) - java

I have 100 same JPanels, each contains JLabel with an icon and JLabel with text. When certain event occurs, I want to change icon and border of panel for 2.5 seconds, and then change them back. The problem is that 1st they are changed together, but when I try to change them back - first icon is changed, and then in 2 or 3 seconds border is changed. Here is the method of a JPanel to perform this:
public void showPacketCame() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
img.setIcon(blue);
setBorder(BorderFactory.createLineBorder(new Color(54, 208, 243)));
javax.swing.Timer tim = new javax.swing.Timer(2500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
img.setIcon(onDark);
setBorder(null);
}
});
tim.setRepeats(false);
tim.setDelay(2500);
tim.start();
}
});
}

This is not the apt way of coding animation task. Please make use of SwingWorker
for this purpose.

Related

What exactly a constructor does and why this code is not working in constructor?

I read about the constructer that a constructor is something that is used to set up the components of the container. So i wrote the following program which works well but only problem is that the label is not at the specified location i.e. 125,300. When I wrote the line label.setLocation(125,300) in the public run() then the code works well. Why it is not working when I write the line in the constructor? Basically it should work as the constructor is used to set up the components of the JFrame.
If not then What exactly is a constructor?
The Code:
public class RealGame extends JFrame implements Runnable, KeyListener{
JLabel label = new JLabel("I am a JLabel");
RealGame(){
setVisible(true);
requestFocus();
setContentPane(new JPanel());
getContentPane().setSize(640, 480);
setSize(640, 480);
label.setSize(50,50);
label.setOpaque(true);
label.setVisible(true);
label.setFont(new Font("Arial", Font.PLAIN, 18));
getContentPane().add(label);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new RealGame());
}
#Override
public void run() {
label.setLocation(125,300); //THIS IS IMPORTANT
JOptionPane.showMessageDialog(null, "Hello");
System.out.println("Done");
System.exit(0);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}
And also is the structure of the program correct? i.e. in many programs I saw that people use
SwingUtilities.invokeLater(new Runnable(){
public run(){
}
});
But instead I instantiated the main class. Will it give any problems in future?
but only problem is that the label is not at the specified location i.e. 125,300.
What is special about (125, 300). Don't use magic numbers in your program.
Swing uses layout managers to position components in a panel. The layout manager will determine the size and location of your component.
Read the section from the Swingtutorial on Layout Managers for more information and examples.
in many programs I saw that people use
Yes all GUI components should be updated on the Event Dispatch Thread(EDT). All the tutorial examples show you how to do this. You can also read the section in the tutorial on Concurrency in Swing for reasons why this is important.
When I wrote the line label.setLocation(125,300) in the public run() then the code works well
The code is added to the end of the EDT, which means it executes AFTER the layout manager. However this is only temporary. Try resizing the frame and the label will go back to the position determined by the layout manager. Don't try to set the location manually!

how do you change the color of a jbutton after it is clicked?

I am making a jFrame that represents a go board. I want a click of a given button to change the color to represent placing a piece on the board. In my code below, I show a method that should be able to change the color of a button (it only changes the background of the whole frame). First question: Why is the button color not changing (this is not my bigger problem about changing color after click occurs, my preliminary issue is that the button color will not change). I do not get any errors, the button color just never changes.
public static void showBoard()
{
JFrame frame2 = new JFrame("Go Board");
frame2.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
for(int i = 19*19; i > 0; i--)
{
JButton firstButton = new JButton("");
firstButton.setBackground(Color.blue);
firstButton.setVisible(true);
firstButton.setContentAreaFilled(true);
firstButton.setOpaque(true);
firstButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run()
{
System.out.println("ddsd");
//int[] arr = findMove(0);
}
});
}
});
frame2.getContentPane().add(firstButton);
}
frame2.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
frame2.setLayout(new GridLayout(19,19));
frame2.pack();
frame2.setVisible(true);
}
My second problem, getting the button to change color after being clicked, is presumably affected by the fact that I am not able to even change the button color. To get the button to change color after a click, I plan to put the button color change code inside the action listener.
So in summation, how can I change the color of the button after a click?
ANSWER:
The problem was the look and feel of my mac. Look to the checked answer for how to fix this if you have similar problem on your mac.
You don't need to call SwingUtilities.invokeLater inside your ActionListener, as the actionPerformed(ActionEvent) method will be invoked on the Event Thread already.
The following example demonstrates how to change a Button's background color when it's clicked on:
public class ChangeButtonColor implements Runnable {
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel());
} catch (UnsupportedLookAndFeelException e) {
System.err.println("Cannot set LookAndFeel");
}
SwingUtilities.invokeLater(new ChangeButtonColor());
}
#Override
public void run() {
JFrame frame = new JFrame();
frame.setLayout(new FlowLayout());
JButton button1 = new JButton("click me");
JButton button2 = new JButton("click me too");
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source instanceof Component) {
((Component)source).setBackground(Color.RED);
}
}
};
button1.addActionListener(listener);
button2.addActionListener(listener);
frame.add(button1);
frame.add(button2);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Please note that the ActionListener used here can be used for all buttons. There is no need to create a new instance of it for every button.

windowbuilder in eclipse, unsure how to display window

I'm fairly new to Swing, so I've been using windowbuilder to try and put together a basic GUI. The design screen works fine, but when I return to the code, it's written it in a way I'm unfamiliar with and I'm struggling to actually get it to run.
The code it generates is:
public class GUIControls extends JFrame{
public GUIControls() {
getContentPane().setLayout(new CardLayout(0, 0));
JPanel panel = new JPanel();
getContentPane().add(panel, "name_36737116256884");
panel.setLayout(null);
JButton InsertionSortButton = new JButton("Insertion Sort");
InsertionSortButton.setBounds(32, 16, 101, 56);
panel.add(InsertionSortButton);
JPanel panel_1 = new JPanel();
getContentPane().add(panel_1, "name_36737137352442");
InsertionSortButton.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
CardLayout cardLayout = (CardLayout) getContentPane().getLayout();
cardLayout.show(getContentPane(), "name_36737137352442");
}
});
}
(With the action taken when the button is mouseclicked being written by me, I haven't tested it because I can't run the thing)
Normally I'd do:
public void runGUI(){
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
}
});
}
With createGUI being the method I used to create a (completely horrible) GUI without windowbuilder, but I can't use GUIControls in this because it doesn't work with runnable (in fact, I'm not even sure what it is when something doesn't a return value, is it still a method?).
Does anyone know how I go about running it?
Thanks
You need to instantiate an instance of GUIControls and make it visible, for example...
public void runGUI(){
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUIControls guiControls = new GUIControls();
guiControls.pack();
guiControls.setLocationRelativeTo(null);
guiControls.setVisible(true);
}
});
}
ps- I know Window Builder likes to make use of null layouts, but I would avoid them wherever possible - IMHO

Update JTextFields text every 3 seconds after pressing button

So what I want to do is that when I press button JTextField text starts to update to new value every 3 seconds. I have tried Thread sleep metod, but it freezes whole program for the sleep time and after it is over textfields gets the latest input. So here is better explained example of what i am trying to do.
I press the JButton which puts the numbers in JTextFiel every 3 seconds as long as there is available values. I dont want it to append new text, just replace old with new. Anyone got ideas how I can do that? Thanks in advance.
You should use a javax.swing.Timer.
final Timer updater = new Timer(3000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// update JTextField
}
});
JButton button = new JButton("Click me!");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updater.start();
}
});
The Timer will not block the Event Dispatch Thread (like Thread.sleep does) so it won't cause your program to become unresponsive.
You can't sleep in the EDT. You can either use a swingworker (better solution) or do something like this:
//sleep in new thread
new Thread (new Runnable() {
public void run() {
Thread.sleep(3000);
//update UI in EDT
SwingUtilities.invokelater(new Runnable() {
public void run() {updateYourTextHere();}
});
}
}).start();
You need to get 'the work' done on a separate thread. See some of the answers here: Java GUI Not changing

Components in JDialog not showing

First of all: I know this question seems to have been asked a few million times, but none of the answers given to other questions seem to work with me.
Sometimes, when I run Message.popup(String, int) in the code below, the text displays correctly, but sometimes the JDialog is empty, like if the component wasn't added at all.
public class Message extends JDialog {
private int width;
private int height;
private JLabel content;
public Message(String _content, int _margin) {
super();
this.content = new JLabel(_content);
content.setFont(new Font("Monospaced", Font.BOLD, 20));
this.margin = _margin;
this.width = content.getPreferredSize().width + _margin;
this.height = content.getPreferredSize().height + _margin;
createComponents();
setProperties();
}
public static void popup(String _content, int _time) {
if (SwingUtilities.isEventDispatchThread()) {
runPopup(_content, _time);
}
else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
runPopup(_content, _time);
}
});
}
}
private static void runPopup(String _content, int _time) {
final Message message = new Message(_content);
new Timer(_time, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
message.dispose();
}
}).start();
}
private void createComponents() {
setLayout(new BorderLayout());
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
box.add(content, BorderLayout.CENTER);
box.add(Box.createHorizontalGlue());
add(box);
}
private void setProperties() {
setSize(width, height);
setLocation(Coordinator.calculateCenteredWindowLocation(width, height));
setUndecorated(true);
setResizable(false);
setTitle(content.getText());
setVisible(true);
update(getGraphics());
}
}
Without the update(getGraphics());, the frame is always empty, but with it, it depends of what direction the wind is blowing... (go figure!)
As mentioned by #Riduidel, it is important that anything Swing-related occur on the Event Dispatch Thread, or EDT. This is because Swing is not thread-safe. When invoking popup(), you ought to do the following
if(SwingUtilities.isEventDispatchThread()){
Message.popup(...);
}
else{
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
Message.popup(...);
}
});
}
This will ensure that the JFrame is created on the EDT. Also, from the code snippet you've posted, it would seem likely that Message should have a private constructor. In addition, since you're not doing any custom rendering, why not just make a JFrame member variable instead of extending the class? -- seems a bit superfluous to me.
Regardless, you should also never sleep in the EDT either, since this will make the GUI appear to "freeze" and blocks execution of other queued events. When performing long-running tasks, use either SwingWorker, or as #Riduidel mentioned, javax.swing.Timer. But if you prefer to use the java.util.Timer, use the SwingUtilities utility class as shown above to post the Runnable task on the EventQueue to be executed in the EDT.
EDIT
Here is what I'd do (and yes, it works)
public class Message {
// Private constructor to prevent external instantiation
private Message(){
}
public static void createAndShowDialog(final String content, final int time){
final JDialog dialog = new JDialog();
dialog.setLayout(new BorderLayout());
dialog.setUndecorated(true);
JLabel label = new JLabel(content);
label.setFont(new Font("Monospaced", Font.BOLD, 20));
Box b = Box.createHorizontalBox();
b.add(Box.createHorizontalGlue());
b.add(label, BorderLayout.CENTER);
b.add(Box.createHorizontalGlue());
dialog.add(b);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
// kick-off timer
Timer t = new Timer(time, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
dialog.dispose();
}
});
t.setRepeats(false);
t.start();
}
}
And wherever you invoke createAndShowDialog(...), do the following
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
Message.createAndShowDialog("Content", 5000); // wait 5 seconds before disposing dialog
}
});
Are you sure your code is executing in the EDT ? indeed, if not (which is what I expect to be, since you sleep the current thread, what Swing would typically don't like), your frame will have trouble rendering.
To avoid those typical Swing threading issues, please take a look at the SwingUtilities class, which provide you methods to ensure you're running in EDT. Additionnaly, instead of directly sleeping your thread, you could repalce it with a Swing javax.swing.Timer (beware not to confuse it with the java.util.Timer).
update(getGraphics());
Never use the update() method or the getGraphics() method.
Invoking update() is used for AWT NOT Swing.
If you need to do custom painting then you override the paintComponent() method of your component which already has access to the graphics object.

Categories

Resources