Java Swing Revalidate() VS Repaint()

Java Swing revalidate() vs repaint()

You need to call repaint() and revalidate(). The former tells Swing that an area of the window is dirty (which is necessary to erase the image of the old children removed by removeAll()); the latter tells the layout manager to recalculate the layout (which is necessary when adding components). This should cause children of the panel to repaint, but may not cause the panel itself to do so (see this for the list of repaint triggers).

On a more general note: rather than reusing the original panel, I'd recommend building a new panel and swapping them at the parent.

Calling repaint() and/or revalidate() through an EventListener - Swing

Your problem is one of confusing object with reference variable, thinking that changing String1's text will magically cause a change in the text that is displayed by the JButton, but that is not how Java's OOP model works. Understand that the JButton is displaying a String object, the same object that String1 is initially referring to, but then when you change the String that String1 refers to, this has no effect on the original String object. In order to change the displayed String, you have to change the String object displayed by the JButton by calling the JButton's setText(...) method and pass the new String into it. That's the only way this will work.

public void actionPerformed(ActionEvent e) {
Beans.setText("Surprise!");
}

See comments:

// here are several reference variables
// all without assigned objects, and thus
// all holding "null" values:
JButton Beans;
String String1;
JPanel things;


public Actions() {
//.....

// here you assign the String object, "Beans" to the String1 variable
String1 = "Beans";

// .....

// here you create a JButton and pass in String1's current object, "Beans"
// into the constructor (note the "" + is NOT needed for Strings, only for numberrs)
Beans = new JButton("" + String1);

//.....
}

public void actionPerformed(ActionEvent e) {
// here you change the object that String1 refers to
String1 = "Surprise!";

// but this has no effect on the original String object, "Beans" displayed in the
// JButton, but rather all it does is change the state of String1.
// To change the state of the JButton, you must explicitly do this
// by calling setText on it

//....

Sample Image

As an aside, you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.

Note as an aside #2: If you actually draw the String, then your original code would work. Note in the code below I have a String variable, currentString that originally refers to the first item in a String array, TEXTS, the String "One". In a JButton's ActionListener, I update the array index variable aptly named index, and set the currentString variable to the next String item in the array, and then call repaint(). The reason this code works is because I am drawing the text held by currentString in the JPanel's painting method, paintComponent(...):

import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.*;

public class DrawStringPanel extends JPanel {
private static final String[] TEXTS = {
"One", "Two", "Three", "Four", "Five",
"Six", "Seven", "Eight", "Nine", "Ten"
};
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final Font TEXT_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 40);
private static final int TEXT_X = 150;
private static final int TEXT_Y = 200;
private int index = 0;

// Note that this String variable holds the first item in the TEXTS array
private String currentString = TEXTS[index];

public DrawStringPanel() {
setPreferredSize(new Dimension(PREF_W, PREF_H));
JButton nextBtn = new JButton("Next");
add(nextBtn);
nextBtn.addActionListener(e -> {
// update the array index
index++; // get next index
index %= TEXTS.length; // but don't let get bigger then array length

// and in the ActionListener here I'm changing the variable and calling repaint
// this works because this variable is actually painted within this JPanel's
// paintComponent method....
currentString = TEXTS[index];
repaint();
});
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setFont(TEXT_FONT);

// ****** HERE ****** I draw the contents of the currentString variable
g2.drawString(currentString, TEXT_X, TEXT_Y);
}

private static void createAndShowGui() {
DrawStringPanel mainPanel = new DrawStringPanel();

JFrame frame = new JFrame("DrawStringPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

how to repaint and or revalidate a JPanel

However I can not get my panel to redraw.

Yes, the panel will redraw when you invoke repaint() on the panel.

However, nothing changes because you haven't changed anything on the panel.

You create all the buttons and add them to the panel when you create an instance of the class.

successfully set my model dimensions.

Yes but you never tell the panel that the dimensions have changed. And you have no logic in the panel to change anything when the memory model changes.

So in your MemoryPanel class what you need is a reset(...) method. This method would remove all the buttons and then recreate them based on the new values in the MemoryModel. Note this reset() method would also be invoked in your constructor when the MemoryPanel instance is created.

Java Swing: repaint() vs invalidate

Q1: which should I use when my
interface has changed: repaint or
invalidate?

If the layout is not up to date because of resizing , font change etc then you should call invalidate. Invalidating a component, invalidates the component and all parents above it are marked as needing to be laid out. Prior to painting, in the validation step if no change is found then the paint step is left out.

If there is some part of component which is being updated (defined by the graphic's clip rectangle, called "damaged" region) then you should consider calling repaint. One of the reason a damaged regions may occur is from the overlapping of a part of your component because of some other component or application.
As per my experience the repaint() is more effective if you call it on the innermost enclosing component (i.e. using public void repaint(int x, int y, int width, int height) rather than using public void repaint()).

Q2: when should they be called?

Invalidate(): marks a component as not valid -- that means, it's layout is or may not be "up to date" anymore: i.e. the component is resized, a border is added, it's font changes, etc. you should never need to call invalidate() by hand, as swing does that for you on pretty much for every property change.

When more than one region within the control needs repainting, Invalidate will cause the entire window to be repainted in a single pass, avoiding flicker caused by redundant repaints. There is no performance penalty for calling Invalidate multiple times before the control is actually repainted.

Repaint() : If the component is a lightweight component, this method causes a call to this component's paint method as soon as possible. Otherwise, this method causes a call to this component's update method as soon as possible.

Also have look at Update method.

NOTE: Swing processes "repaint" requests in a slightly different way from the AWT, although the final result for the application programmer is essentially the same -- paint() is invoked.

Refer to the link below for an excellent link on how painting is done in AWT and Swing:

http://www.oracle.com/technetwork/java/painting-140037.html

Hope this will help.

Java usage of revalidate() and repaint() not properly working

Start by taking a look at Concurrency in Swing.

Swing, like most GUI frameworks, is single threaded AND not thread safe.

This means that any long running or blocking operation executed from within the Event Dispatching Thread will prevent the EDT from processing the Event Queue and update the UI in anyway.

While you could use a Thread to offload the wait time to a second thread, Swing is NOT thread safe, meaning you should never update/modify the UI directly or indirectly from outside the context of the EDT.

The simplest solution in your case is to simply use a Swing Timer. This allows you to specify a delay between updates (and if it's repeating or not), which is executed off the EDT, but when triggered, is notified within the context of the EDT, making easy and safe to use with Swing.

The Timer acts as a pseudo loop, each trigger of the Timer representing the next iteration

See How to use Swing Timers for more details

Validate or repaint for this case?

You create a new JPanel in the ActionListener's actionPerformed method but add it to nothing, in particular you add it to no container whose container hierarchy leads to a top-level window (here your JFrame), so changes to it will not be reflected in the GUI.

I suspect that you are under the fallacy that if you have a variable refer to a new object and change the new object's state, here panConf, that you will somehow change the state of the original object that the variable previously referred to, but that's not how Java works. The original JPanel that panConf refered to at the program start still exists and still sits in the GUI unchanged. The key to this is that you must understand the difference between a reference variable and an object (or reference). This is a key Java concept that might take some effort to fully get, but is worth the effort.

Instead simply remove the components held by the original panConf JPanel:

  btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do not create a new JPanel here
panConf.removeAll();
panConf.revalidate();
panConf.repaint();
}
});

Or better still, use a CardLayout to swap views.

Also, there's no need to use SwingUtilities.invokeLater and a Runnable queue this on the EDT. The code is already being called on the event thread.



Related Topics



Leave a reply



Submit