Jbutton Actionlistener - Gui Updates Only After Jbutton Is Clicked

JButton ActionListener - GUI updates only after JButton is clicked

Do not perform any intensive operations within EDT, otherwise the GUI will be unresponsive and you might not see the GUI updates. Best choice you can use is SwingWorker:

  • Override doInBackground(), and put any long operations inside this method so that it will be run on a separate thread rather than the EDT.

  • For any GUI creation or changing states of GUI components within doInBackground(), use publish(V... chunks) to send data to process(List<V> chunks). You need to override process(List<V> chunks). Also note that process(List<V> chunks) is executed on EDT.

  • After doInBackground() returns, done() executes on EDT and you can override it to use it for any GUI updates. You can also retrieve the value returned from doInBackground() by using get().

  • Note that SwingWorker<T,V> is generic, and you need to specify the types. T is the type of object returned from doInBackground() and get(), while V is the type of elements you passed to process(List<V> chunks) via publish(V... chunks).

  • execute() method starts the swing worker by invoking doInBackground() first.

For more on this, please read Concurrency in Swing.

GUI not updating visually before running ActionEvent

Every time you execute logic from the GUI you should be using the SwingWorker in the following way:

SwingWorker myWorker= new SwingWorker<String, Void>() {
@Override
protected String doInBackground() throws Exception {
//Execute your logic
return null;
}
};
myWorker.execute();

If you want to update the GUI from inside this logic use InvokeLater:

SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//To update your GUI
}
});

With this you can be sure that both your logic and your GUI stay responsive.

Edit:

You could also use invokeAndWait if this suits your needs more. Link to related answer

Clicking on one button calls the actionListener of another button in JAVA Swing

This happens because setText eventually calls paintComponent, which changes the color to a random color. This happens because the text cannot be changed without redrawing the graphics, which is going to include calling paintComponent as that is the method used to draw the graphics.

But his only happens when the text is actually changed, so the second time you click the button nothing will happen, since you are not actually changing the text - it will still be "Good Job!".

So, basically the button does not call the action listener of the other button, but both action listeners eventually call paintComponent (at least on the first click, when the text of the label is actually changed).

If you want to fix this, move the part where color is randomized into the action listener and out of the paintComponent method.

JButtons won't update on button click

Building on to what this answer points out (using layout managers instead of setting size, as you should be doing) you could reset the the images just by looping through the components (JLabels) of the JPanel and changing their icons.

This particular example uses JLabels with MouseListeners but it could easily be switched to JButtons with ActionListeners

newGame.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
reset(iconPanel, icons); <--- call reset method from below
score = 0;
scoreField.setText(String.valueOf(score));
}
});
....

private void reset(JPanel panel, ImageIcon[] icons) {
Component[] comps = panel.getComponents();
Random random = new Random();
for(Component c : comps) {
if (c instanceof JLabel) {
JLabel button = (JLabel)c;
int index = random.nextInt(icons.length);
button.setIcon(icons[index]);
}
}
}

Sample Image

Here's the complete running code. You just need to replace the image paths.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class CircleImages {

private int score = 0;
private JTextField scoreField = new JTextField(10);

public CircleImages() {
scoreField.setEditable(false);

final ImageIcon[] icons = createImageIcons();
final JPanel iconPanel = createPanel(icons, 8);

JPanel bottomLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
bottomLeftPanel.add(new JLabel("Score: "));
bottomLeftPanel.add(scoreField);

JPanel bottomRightPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
JButton newGame = new JButton("New Game");
bottomRightPanel.add(newGame);
JButton quit = new JButton("Quit");
bottomRightPanel.add(quit);

JPanel bottomPanel = new JPanel(new GridLayout(1, 2));
bottomPanel.add(bottomLeftPanel);
bottomPanel.add(bottomRightPanel);

newGame.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
reset(iconPanel, icons);
score = 0;
scoreField.setText(String.valueOf(score));
}
});

JFrame frame = new JFrame();
frame.add(iconPanel);
frame.add(bottomPanel, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

private void reset(JPanel panel, ImageIcon[] icons) {
Component[] comps = panel.getComponents();
Random random = new Random();
for(Component c : comps) {
if (c instanceof JLabel) {
JLabel button = (JLabel)c;
int index = random.nextInt(icons.length);
button.setIcon(icons[index]);
}
}
}

private JPanel createPanel(ImageIcon[] icons, int gridSize) {
Random random = new Random();
JPanel panel = new JPanel(new GridLayout(gridSize, gridSize));
for (int i = 0; i < gridSize * gridSize; i++) {
int index = random.nextInt(icons.length);
JLabel label = new JLabel(icons[index]);
label.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e) {
score += 1;
scoreField.setText(String.valueOf(score));
}
});
label.setBorder(new LineBorder(Color.GRAY, 2));
panel.add(label);
}
return panel;
}

private ImageIcon[] createImageIcons() {
String[] files = {"blackcircle.png",
"bluecircle.png",
"greencircle.png",
"greycircle.png",
"orangecircle.png",
"redcircle.png",
"yellowcircle.png"
};
ImageIcon[] icons = new ImageIcon[files.length];
for (int i = 0; i < files.length; i++) {
icons[i] = new ImageIcon(getClass().getResource("/circles/" + files[i]));
}
return icons;
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new CircleImages();
}
});
}
}

How to create a JLabel that updates on a click?

You have two ActionListeners:

  1. one added in the constructor of your button class.
  2. the second added in your main method.

Swing invokes ActonListeners in the reverse order the listener is added to the component.

So you update the text on the label before the counter variable has been incremented.

A better design is to create a JPanel class. The you create an instance of the panel and add it to the frame. This class would contain:

  1. the button
  2. the label
  3. the counter.

You create the components and add them to the panel and add a single listener to the button.

With this design all the variable are defined in the same class. So all the components can work together with one another. Don't define Swing components in a main() method.

For example see the section from the Swing tutorial on How to Use Buttons for a better design of your code showing how to create a panel with multiple related components.

How to update components listening a button in java

You're removing all components from this (which in this case is the JFrame (as you're extending it, which isn't needed, and instead you should create an instance from it rather than inherit from it, as you're not changing the behavior of the JFrame so it's better to just create an instance of it). See: Extends JFrame vs. creating it inside the program

In this case you're adding your components in this way:

JFrame > buttons (JPanel) > JButtons

And you're trying to remove

JFrame > everything

That includes the contentPane, instead you should call.

buttons.removeAll()

Inside the update() method.

And also call this.repaint() so your update() method should become:

public void update() {
buttons.removeAll();
buttons.add(button3);
this.revalidate();
this.repaint();
}

Or the best approach is to use CardLayout as recommended by @AndrewThompson in the comment below. This way you don't have to handle removing / repainting for each component, as CardLayout will do it for you. For example

Actions performed on jButton after disabling

I got this working by running task to be performed on click of button on new thread.

How do I update the appearance of my JButton when it's clicked?

The problem is that when I add the code to modify the JButton, none of the changes happen until after the validation has completed.

Your code is executing on the EDT, so you long running code prevents the GUI from repainting itself until the task is finished executing. You need to use a separate Thread for the long running task, maybe a SwingWorker. Read the section from the Swing tutorial on Concurrency for more information.



Related Topics



Leave a reply



Submit