I have a screen in which one of its components is made invisible depending on a boolean value. If the boolean changes after the screen has been created, how do I refresh the screen to take this into account?
I think revalidate() is more appropriate here if you are dealing with JComponents.
From the JavaDoc:
Supports deferred automatic layout.
Calls invalidate and then adds this component's validateRoot to a list of components that need to be validated. Validation will occur after all currently pending events have been dispatched. In other words after this method is called, the first validateRoot (if any) found when walking up the containment hierarchy of this component will be validated. By default, JRootPane, JScrollPane, and JTextField return true from isValidateRoot.
This method will automatically be called on this component when a property value changes such that size, location, or internal layout of this component has been affected. This automatic updating differs from the AWT because programs generally no longer need to invoke validate to get the contents of the GUI to update.
Call the validate() method on the container that needs to be laid out -- probably your window's content pane.
Try calling repaint() which in turn will call paintComponent().
I thought that (with Java 6?) you need not do anything... This should happen automatically - no?
With the following example, it does happen automatically...
public class TT extends JFrame
{
public TT()
{
setLayout(new FlowLayout());
JLabel label = new JLabel();
label.setText("Label:");
add(label);
final JTextField textField = new JTextField();
add(textField);
JButton button = new JButton();
button.setText("Button");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (textField.isVisible())
{
textField.setVisible(false);
}
else
{
textField.setVisible(true);
}
}
});
add(button);
setSize(100,100);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
TT frame = new TT();
frame.setDefaultCloseOperation(TT.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
[Add] And using a layout manager like GridBagLayout would also solve the problem of "Re-Laying out" the page.
Related
I'm working on an RCP application that's in a transition from a Swing version. So we have a lot of UI components that still need to live in the Swing world during this transition. I'm able to properly place the existing Swing components in AWT-SWT bridge frames.
I've wrapped these Swing components in a JScrollable pane before adding them to the bridge so that I don't have to resize the containing-part when the size of the Swing UI elements change. The code where I place an old Swing component in a part looks like this:
#PostConstruct
public void postConstruct(final Composite parent) {
/* Create embedding composite */
final Frame bridgeFrame;
final Composite embed;
embed = new Composite(parent, SWT.EMBEDDED);
embed.setLayout(new FillLayout());
bridgeFrame = SWT_AWT.new_Frame(embed);
bridgeFrame.setLayout(new BorderLayout());
bridgeFrame.add(
new JScrollPane(getTestPanel()),
BorderLayout.CENTER);
}
My Swing component has a behavior where when the user clicks a button, things that were hidden in the component are made visible, or new UI elements are added to the Swing component. For example:
private JPanel getTestPanel() {
final JPanel output;
final JButton eastBttn, westBttn;
output = new JPanel();
eastBttn = new JButton("East Button");
westBttn = new JButton("West Button");
output.setLayout(new BorderLayout());
output.add(eastBttn, BorderLayout.EAST);
output.add(westBttn, BorderLayout.WEST);
eastBttn.addActionListener(evt -> {
System.out.println("East Button Clicked");
output.add(new JLabel("East Button Clicked"), BorderLayout.CENTER);
});
return output;
}
My problem is, when the elements in the Swing-component change, the parent bridge-frame doesn't properly get rendered.
When the parts are first created, my application looks like this:
After I click on the EastButton it's supposed to add a text label in the center of that bridge frame. However, nothing changes in the application view.
But, when I even begin to resize the containing part-sash a little, the part containing the bridge-frame updates correctly:
What can I do to make the bridge-frame update containing part update automatically?
To test whether this was a repainting issue on the bridge-frame, I had a menu item which would trigger a repaint / revalidate / pack of the bridge-frame, but that didn't solve the issue. I suspect it has something to do with the renderer of the containing part, but have no idea how to go about addressing it.
The same a problem exists in a pure Swing solution:
public static void main(String[] args) {
JFrame bridgeFrame = new JFrame("Test");
bridgeFrame.setSize(400, 400);
bridgeFrame.setLayout(new BorderLayout());
bridgeFrame.add(new JScrollPane(getTestPanel()), BorderLayout.CENTER);
bridgeFrame.setVisible(true);
}
You need to add an output.doLayout() in your event handler.
I eventually got around the problem by attaching a custom ControlListener / ComponentListener to the part containing the bridge. If any changes within the workings of the bridge-frame caused it to resize to beyond the parent, I'd have the listener resize it to fit within the parent thus forcing the scroll-pane to take over.
Here's my listener:
public class BridgeComponetAdapter
extends ComponentAdapter
implements ControlListener {
private final Composite parent;
private final Frame bridgeFrame;
private Point parentSize;
public BridgeComponetAdapter(
final Composite parent,
final Frame bridgeFrame) {
this.parent = parent;
this.bridgeFrame = bridgeFrame;
bridgeFrame.addComponentListener(this);
parent.addControlListener(this);
}
#Override
public void componentResized(final ComponentEvent e) {
System.out.println(e);
if (e.getSource() != bridgeFrame)
return;
final Dimension currentBridgeSize;
currentBridgeSize = bridgeFrame.getSize();
if (currentBridgeSize.getWidth() > parentSize.x
|| currentBridgeSize.getHeight() > parentSize.y) {
bridgeFrame.setSize(parentSize.x, parentSize.y);
}
}
#Override
public void controlMoved(final ControlEvent e) {}
#Override
public void controlResized(final ControlEvent e) {
System.out.println(e);
if (e.getSource() == parent)
parentSize = parent.getSize();
}
}
It's not an elegant solution; I'm still open to other ideas to solve the problem.
I'm just new to Java GUI Programming and I'm having a problem that the components inside my panel is missing when I place the setVisible()function at the beginning of the function called by the constructor but it works fine when it is at the end. See code below:
public static void main(String[] args)
{
new MainClass();
}
public MainClass()
{
setFrame();
}
private void setFrame()
{
JFrame frame = new JFrame();
frame.setSize(400,400);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Some area where the object of my components inside the panel is created and initialized.
// If I just place a label and a button, it will appear on the panel. However if I add the JTextArea, all the components in my panel is gone. Just like the code below.
textArea1 = new JTextArea(20,34);
textArea1.setWrapStyleWord(true);
textArea1.setLineWrap(true);
JScrollPane scroll =
new JScrollPane(textArea1,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
panel.add(scroll);
frame.add(panel);
// Works fine when setVisible(true); it placed here.
}
What could the problem possibly be with regard to placing the setVisible() function to the beginning or to the end of the method.
As already pointed out in the comments and the other answer:
You should call setVisible(true) at the end, after all components have been added.
This does not directly answer your question. The answer to your question is: Yes, it makes a difference. If you call setVisible before all compoents have been added, it may work, in some cases, with some programs, on some PCs, with some Java versions, with some operating systems - but you always have to expect that it may not work as expected in some cases.
You will find dozens of related questions here on stackoverflow and elsewhere. The usual symptoms of these problems are that some components are not displayed properly, and then suddenly appear when the window is resized. (Resizing a window basically triggers a layout and a repaint).
The likeliness of unexpected behavior is increased when you violate the threading rules of Swing. And, in some sense, you did violate the threading rules of Swing: You should always create the GUI on the Event Dispatch Thread!
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SomeSwingGUI
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
// This method may (and will) only be called
// on the Event Dispatch Thread
private static void createAndShowGUI()
{
JFrame f = new JFrame();
// Add your components here
f.setVisible(true); // Do this last
}
}
And by the way: Timothy Truckle pointed out in a comment that you should not invoke setVisible from the constructor. This is true. More importantly: You should usually not directly create a class that extends JFrame. (In some (rare!) cases, this is appropriate, but the general guideline should be to not extend JFrame)
The components cannot be shown, because you add them after you call the setVisible() method of the Frame.
The Componet's add() method changes layout-related information, and invalidates the component hierarchy. If the container has already been displayed, the hierarchy must be validated thereafter in order to display the added component, as pointed here
.
So in order to show the items, you should either call the revalidate() method of the frame or call setVisible() after all your components are added.
Unless there is no special need, you should call setVisible() after you have added every other component.
public class TestMain extends JFrame {
public static void main(String[] args) {
JFrame test = new TestMain();
//if the setVisible() is called too early, you have to revalidate
test.revalidate();
}
public TestMain() {
setFrame();
}
private void setFrame() {
setSize(400,400);
setResizable(false);
setLayout(new FlowLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new FlowLayout());
setVisible(true);
JTextArea textArea1 = new JTextArea(25,15);
textArea1.setWrapStyleWord(true);
textArea1.setLineWrap(true);
panel.add(textArea1);
JScrollPane scroll =
new JScrollPane(panel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
// this method invalidates the component hierarchy.
getContentPane().add(scroll);
}
}
I wrote the following code to have a JPopupMenu that allows multiple selection of different items.
The problem is that, as soon as the mouse enters one of the displayed JCheckboxMenuItems, the JPopupMenu gets closed. This issue doesn't occur if I replace JCheckboxMenuItem with, for example, JLabel but, for sure, JLabel doesn't work for my purpose.
Any idea of what could trigger this issue? Any idea of how this problem can be resolved in a better way? I apologize for the newbie question but I'm not a java developer. Thanks in advance for any help.
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedborder(),"Select Layers");
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
for (MyAction layer : layers) {
JCheckBoxMenuItem box = new JCheckBoxMenuItem(layer);
box.setIcon(new SquareIcon(myColor));
panel.add(box);
}
JPopup popup = new JidePopup();
popup.add(panel)
JButton button = new JButton("Layers");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
popup.show(button,0,button.getHeight())
}
});
Thats in the nature of JPopupMenus. They disappear when the invoker component loses the focus. But I found a little trick here.
Create your own class and extend it from JPopupMenu. Then override the setVisible method that it will only forward true to the super class and create an own method that will setVisible of the super class to false.
public class StayOpenPopup extends JPopupMenu{
public void setVisible(boolean visible){
if(visible == true)
super.setVisible(visible);
}
public void disappear() {
super.setVisible(false);
}
}
Then use it like this in your code
[...]
StayOpenPopup popup = new StayOpenPopup();
popup.add(panel);
[...]
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(popup.isVisible())
popup.disappear();
else popup.show(button,0,button.getHeight());
}
});
Now one click on button will show it. And it will stay visible until next click on Button.
I am trying to make a refresh button that will essentially restart the program when ever I click the button. I don't know how I should go about doing this.
I've place the Graphical User Interface i decided to use do complete this action. Any and all help would be greatly appreciated.
package pdfView;
import javax.swing.*;
import java.awt.*;
public class View extends JFrame {
public View() {
super("PDF Viewer");
setLookAndFeel();
setSize(500, 125);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout flo = new FlowLayout();
setLayout(flo);
JTextField Search = new JTextField ("Search", 29);
JButton Search1 = new JButton("Search");
//this is where i have the button
JButton ReFresh = new JButton("ReFresh");
add(Search);
add(Search1);
add(ReFresh);
setVisible(true);
}
private void setLookAndFeel() {
try {
UIManager.setLookAndFeel(
"com.sun.java.squing.plaf.nimbus.NimbusLookAndFeel"
);
} catch (Exception exc){
}
}
public static void main(String[] args) {
View pdf = new View();
}
}
What do you mean by refresh or restart?
Do you mean:
Let the application be as it is, just update what it's showing?
Really restart the application?
Updating what the application is showing
You first need to decide what actually should cause your application to refresh. You already talked about a Button. The mechanism for activating something like a button is called Action. You can do that stuff manually, using an ActionListener, or you could extend AbstractAction, which is what I recommend. Extending AbstractAction allows you to use the same logical action something in more than one location on the UI. Look at typical applications, they offer Cut/Copy/Paste through menu, toolbar, popupmenu and keyboard shortcuts. The simplest way to achieve this in Java is using Action by extending AbstractAction.
The methods you need to call to update your application are invalidate(), validate() or repaint().
Restarting an application
So you want to run through main() again? That should actually not be required, unless you have an application that supports updating itself. Even then it can sometimes be avoided by smart usage of a ClassLoader.
Some more notes on your code
Usage by extension anti-pattern
I wouldn't extend JFrame just to display a window on the screen. Usage by extension is an anti-pattern. You don't need to extend JFrame to get a JFrame displayed on the screen and do what you want.
Referring static members
I would refer to constants via their original declaration. I.e. I'd refer to EXIT_ON_CLOSE via WindowConstants.EXIT_ON_CLOSE, not JFrame.EXIT_ON_CLOSE.
Typo
You have a typo in your UIManager.setLookAndFeel() code. Search for swing and you will see the typo.
Exception information
You might actually want to print the exception to stderr using exc.printStackTrace() instead of ignoring it completely, because when you have a typo in the LaF class name, as you do, and you don't print the exception, you might actually not come to know what's going wrong.
Sequence of widget construction and UIManager.setLookAndFeel()
The sequence of UIManager.setLookAndFeel() and the effective new JFrame() via super(...) does not guarantee you that the whole UI will be in Nimbus, parts of it might still be in Metal. I recommend to set the LaF before even constructing the first widget, to be on the safe side. As far as I remember, it's not guaranteed that changing the LaF after component construction has an effect, unless you tell the UIManager to update the LaF. See also this quote from the documentation of UIManager:
Once the look and feel has been changed it is imperative to invoke updateUI on all JComponents. The method SwingUtilities.updateComponentTreeUI(java.awt.Component) makes it easy to apply updateUI to a containment hierarchy. Refer to it for details. The exact behavior of not invoking updateUI after changing the look and feel is unspecified. It is very possible to receive unexpected exceptions, painting problems, or worse.
http://docs.oracle.com/javase/8/docs/api/javax/swing/UIManager.html
setSize() vs. pack() with a little help of Insets and Border
Instead of setting the size manually, you might want to play with Insets or Border and JFrame.pack() in order to get a decent layout of your window. Setting the size manually assumes that you know a lot about the screen resolution and the font size of the user.
The pack() method performs automatic size calculation based on the contents. Insets and Border allow you to create some space and borders, even with some designs or labels, around components so they wouldn't be cramped tightly in a window but be nicely spaced.
First you have to assign an actionListener to the ReFresh Jbutton.
You can either implement the interface ActionListener to the class, and override the actionPerformed() method like this
public class View extends JFrame implements ActionListener{
private JButton ReFresh;
public View() {
super("PDF Viewer");
setLookAndFeel();
setSize(500, 125);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout flo = new FlowLayout();
setLayout(flo);
JTextField Search = new JTextField ("Search", 29);
JButton Search1 = new JButton("Search");
//this is where i have the button
ReFresh = new JButton("ReFresh");
ReFresh.addActionListener(this);
add(Search);
add(Search1);
add(ReFresh);
setVisible(true);
}
private void setLookAndFeel() { //right way for nimbus: http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/nimbus.html
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e)
{
if(e.equals(ReFresh))
{
super.repaint();
}
}}
public static void main(String[] args) {
View pdf = new View();
}
Or you can do inline assignment to addActionListener, like this
ReFresh.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
super.repaint();
}
});
You can try these methods to refresh/reload the JFrame,
invalidate();
validate();
repaint();
you can also use dispose(); and then new View(); to create the new JFrame, but in this sequence it will close the window and create new one.
or you can even try setVisible(false); then setVisible(true);
I recommend the first 3.
Here is my program.This program simply creates a frame using swings in java and create a grid first and adds button to that frame:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class one {
private static void createAndShowGUI() {
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.getContentPane().setPreferredSize(new Dimension(500,500));
frame.pack();
frame.setVisible(true);
Container pane=frame.getContentPane();
pane.setLayout(new GridLayout(5,6));
JButton[] buttons = new JButton[26];
String b[]={"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
for(int i = 0; i<buttons.length; i++) {
buttons[i] = new JButton(b[i]);
buttons[i].setSize(80, 80);
buttons[i].setActionCommand(b[i]);
buttons[i].addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String choice = e.getActionCommand();
JOptionPane.showMessageDialog(null, "You have clicked: "+choice);
}
});
System.out.println("adding button\n");
pane.add(buttons[i]);
}
}
public static void main(final String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
The program compiles fine.But the button creation i can see only one button that is "A" and rest of the buttons i am not able to see in the pane.
It turns out that this seems to be a race condition and/or system dependent type issue, since it works for others. In any case, the Javadoc for java.awt.Container.add() states:
This method changes layout-related information, and therefore,
invalidates the component hierarchy. If the container has already been
displayed, the hierarchy must be validated thereafter in order to
display the added component.
You should therefore call pane.validate() after you have added all components (or, as MadProgrammer says, execute the setVisible() call after you have added all your components).
There are, essentially two ways to fix this problem.
The first, is call frame.setVisible(true) AFTER you have completed adding the buttons to the pane.
The second is to call
pane.invalidate();
pane.repaint();
After you have added all the buttons to the pane
I can see buttons(A-Z). I could not reproduce your problem.