JMenuBar visibility confusion - java

I'm learning about GUI in Java.
I'm slightly confused here. When I place window.setVisible(true); like this, I only see JMenuBar if I resize it (it doesn't show without some sort of interaction).
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
public class Main {
public static void main(String[] args) {
JFrame window = new JFrame("My App");
window.setSize(500, 500);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
JMenuBar bar = new JMenuBar();
window.setJMenuBar(bar);
JMenu menu = new JMenu("File");
bar.add(menu);
}
}
But when I place it at the very bottom, it shows as expected. Why is this?
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
public class Main {
public static void main(String[] args) {
JFrame window = new JFrame("My App");
window.setSize(500, 500);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMenuBar bar = new JMenuBar();
window.setJMenuBar(bar);
JMenu menu = new JMenu("File");
bar.add(menu);
window.setVisible(true);
}
}
Here it is explained that it must be called at the end, but what is the reasoning behind this?
java JMenuBar not visible?Why?

After adding a component you would have to repaint the container.
So if you add menubar after window is visible, it will popup after next repaint, in your example, after resize. If menubar is added prior setting window to be visible, it will be drawn at first drawing.
This is common behaviour for Swing components.
See Component javadoc
If you add or remove component:
If the container has already been displayed, the hierarchy must be
validated thereafter in order to display the added component.

Related

Java GUI JMenuBar

Ok I have a GUI with and JMenuBar and when I load it sometimes it won't show, but if I minimize it and click back on it, the JMenuBar Shows. Where is my problem? and how can I fix it? oh and if I re size it to, the JMenuBar appears.
Here is my code
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class Main {
public static void main(String[] args){
JFrame frame = new JFrame("TwitchBot");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(700, 500));
frame.setLocationRelativeTo(null);
frame.setResizable(true);
frame.pack();
frame.setVisible(true);
KeyGetter.LoadKeys();
try {
Config.loadConfig();
} catch (Exception e) {
e.printStackTrace();
}
JMenuBar mb = new JMenuBar();
JMenu file = new JMenu("File");
mb.add(file);
JMenu edit = new JMenu("Edit");
mb.add(edit);
JMenuItem options = new JMenuItem("Options");
options.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
Config.openConfig(frame);
}
});
frame.setJMenuBar(mb);
edit.add(options);
}
}
Make sure you're creating/modifying your UI only from within the context of the Event Dispatching Thread. See Initial Threads for more details
Call pack and setVisible only after you have established the base UI.
NEVER USE pack() ..it has conner cases.. some times it fails. From experimentation I have learnt to always call repaint() right before calling setVisible(true) this never fails. Call repaint() right before you call setVisible(true).--THIS NEVER FAILS.
repaint();
setVisible(true); // assured NEVER FAILS.

Redrawing a new JFrame in an existing one?

I'm trying to display a different JFrame after the user does something in the same window they are using, similar to a login feature. Haven't been able to figure out how to do that.
The workaround I have now is to just hide the current JFrame and then open a new one, which simulates a similar effect. But ideally I want it to just display the next JFrame in the same existing window.
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public class Login extends JFrame {
private static int x = 0;
static JTextField txtInput = new JTextField(10);
static JButton btnSwitch = new JButton("Log on");
public Login(){
setLayout(new FlowLayout());
//add button and register
add(new JLabel("Enter password:"));
add(txtInput);
add(btnSwitch);
}
public static void main(String[] args) {
final JFrame frame = new Login();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 150);
frame.setVisible(true);
btnSwitch.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(txtInput.getText().equals("123")){
//frame = new GUIHelloWorld(); this doesn't work because "The final local variable frame cannot be assigned, since it is defined in an enclosing type"
//so I went with the below workaround
GUIHelloWorld frame = new GUIHelloWorld();
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 100);
frame.setVisible(true);
}
}
});
}
}
Once the user get pass the first part of the GUI, I want to show em something else like this:
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GUIHelloWorld extends JFrame {
public GUIHelloWorld(){
setLayout(new GridLayout(0,1));
add(new JLabel("Hello World"));
add(new JLabel("Welcome to the 2nd part of the GUI"));
}
public static void main(String[] args) {
JFrame frame = new GUIHelloWorld();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 100);
frame.setVisible(true);
}
}
Can someone give me an idea of how to display a new JFrame into the existing window that the user is using?
Don't extend from JFrame, especially in the case, frame's can't be added to other frames. Instead, based you individual UI views on something like JPanel
Create a single instance of a JFrame, set it's layout manager to use a CardLayout.
Add each of your view's to the frame, naming each view appropriately
Use CardLayout to switch between the view as needed
You could also consider using a JDialog for the login window, but the basic advice remains; create windows, extend components...

Adding panels to frame, but not showing when app is run [duplicate]

This question already has answers here:
Why shouldn't I call setVisible(true) before adding components?
(2 answers)
Closed 9 years ago.
I am crating an app with two panels in the frame along with a menu bar along the top. The menu bar shows up just fine, and any actions set so far work, but the other two panels never appear.
I have tried retracing all the panels and lines that add them on to the frame and cannot find any errors.
The first of the two panes that do not show, form in the drawForm() method, did show before I added some components, but since have not shown even when I remove the components again.
Here is the class Frame:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Frame {
public static void drawFrame(){
// Create frame
JFrame frame = new JFrame("Frame");
// Set default close operation
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Set frame attributes
frame.setVisible(true);
frame.setSize(400, 300);
frame.setResizable(false);
// Set Layout
frame.setLayout(new BorderLayout());
// Add Components
frame.add(drawMenuBar(), BorderLayout.NORTH);
JPanel twinPane = new JPanel();
frame.add(twinPane, BorderLayout.CENTER);
twinPane.setLayout(new GridLayout(1, 2));
twinPane.add(drawForm());
twinPane.add(drawInfo());
} // Ends method drawFrame
public static JMenuBar drawMenuBar(){
//Create menu structure
JMenuBar menu = new JMenuBar();
JMenu file = new JMenu("File");
JMenuItem clear = new JMenuItem("Clear");
JMenuItem calculate = new JMenuItem("calculate");
JMenuItem exit = new JMenuItem("Exit");
JMenu help = new JMenu("Help");
JMenuItem about = new JMenuItem("About");
JMenuItem instructions = new JMenuItem("Instructions");
//Add menu items to repective area of menu tree
menu.add(file);
file.add(clear);
file.add(calculate);
file.add(exit);
menu.add(help);
help.add(about);
help.add(instructions);
//Add ActionListener
exit.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
MainApp app = new MainApp();
app.exit();
}
});
//Set Return
return menu;
} // Ends method drawMenuBar
public static JPanel drawForm(){
//Set panel with layout and border
JPanel form = new JPanel();
form.setBorder(BorderFactory.createTitledBorder("Form"));
form.setLayout(new BoxLayout(form, BoxLayout.PAGE_AXIS));
//Create field and labels for form panel and add to form panel
JLabel label1 = new JLabel("text1");
form.add(label1);
JTextField textfield1 = new JTextField(5);
form.add(textfield1);
JLabel label2 = new JLabel("text2");
form.add(label2);
JTextField textfield2 = new JTextField(5);
form.add(textfield2);
JButton calc = new JButton("calculate");
form.add(calc);
JTextField result = new JTextField(5);
form.add(result);
//Set return
return form;
} // Ends method drawForm
public static JPanel drawInfo(){
//Set Panel with layout and border
JPanel info = new JPanel();
info.setBorder(BorderFactory.createTitledBorder("Information"));
//Set Return
return info;
} // Ends method drawInfo
} // Ends class Frame
The main method is in another class, but the class Frame creates the GUI. The frame along with the menu bar work perfectly, but everything after that does nothing.
I appreciate any help, thank you
Josh
When you add components to a container you may need to invalidate the container hierarchy in order to get them to become visible...
The problems is as highlight when you set the frame visible BEFORE you've added anything to it...
public static void drawFrame(){
// Create frame
JFrame frame = new JFrame("Frame");
// Set default close operation
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Set frame attributes
// !! Don't do this here...
//frame.setVisible(true);
// ... IMHO, better to use pack...
//frame.setSize(400, 300);
frame.setResizable(false);
// Set Layout
frame.setLayout(new BorderLayout());
// Add Components
frame.add(drawMenuBar(), BorderLayout.NORTH);
JPanel twinPane = new JPanel();
frame.add(twinPane, BorderLayout.CENTER);
twinPane.setLayout(new GridLayout(1, 2));
twinPane.add(drawForm());
twinPane.add(drawInfo());
// !! Do it here instead...
frame.pack();
frame.setVisible(true);
} // Ends method drawFrame
You're setting the JFrame visible before you've added the components. Don't do that. Add the components, pack() your JFrame, and only then set it visible.
But regardless of any answers you might receive, your question has been asked many times before, including: Why shouldn't I call setVisible(true) before adding components?. Voting to close as a duplicate since the question is unlikely to help future visitors.
Agree with #MadProgrammer. You should put the codes--frame.pack(); frame.setVisible(true)-- on bottom.

JDesktopPane and BorderLayout

I am writing a program that attempts to simulate the evolution of a species, and it has a window that looks like this:
Originally the empty area in the bottom right was a Panel, and it is intended to draw a visual representation of the specimens, locations, and travel paths(doesn't really matter). However, you will be able to open some sort of window that allows you to create/edit different items(like species, locations, and travel paths). Originally I planned for those to simply be popup windows. But, I was thinking I would perhaps use JInternal panes for the popups, and the visual representation screen.
So in my JFrames constructor:
JDesktopPane pane = new JDesktopPane();
this.setContentPane(pane);
setLayout(new BorderLayout());//To layout the menubar, and the items on the left
panel = new GraphicsPanel(manager);
panel.setVisible(true);
And in Graphics Panel constructor:super("Graphic Project View",true,false,true,true);
This locks the Panel to BorderLayout.CENTER, and it fills up the entire space, not allowing for anything else. My guess this is because JDesktopPanes use an OverlayLayout, and when I set the layout to BorderLayout that overrides the OverlayLayout, and so my InternalFrame just gets added to the center.
So the question is:
How do I layout the things like the JMenuBar, and the left ward Panel as they are now, whilst still maintaining the capability to have JInternalFrames?
For now I am going to add the JMenuBar via JFrame.setJMenuBar(JMenuBar) instead of JFrame.add(menuBar,BorderLayout.NORTH), and then change the panel on the left into a JInternal frame, but if possible I'd rather have it as is. I would like it if I could just have the DesktopPane be added to the JFrame at BorderLayout.CENTER, and then just add the frame to the Desktop pane. If the InternalFrame were limited to that region I wouldn't care, as long as it's still mobile, ect.
EDIT: How I add JInternalFrame(Sorry it still says panel, but it has been converted to a JInternalFrame):
panel = new GraphicsPanel(manager);
panel.setSize(desktop.getSize());
panel.setLocation(0,0);
panel.setVisible(true);
desktop.add(panel);
I would start with a single JPanel (lets all it the base pane), which will house the other containers.
Using a border layout, I would add a "controls" panel to the WEST position of the base pane. Onto the CENTER position I would add the JDesktopPane.
I would set the main windows layout to BorderLayout and add the base pane to it. This will allow you to use JFrame#setJMenuBar to manage the menu bar while maintaining the result of the layout.
This will allow you to contain to use the JInternalFrames on the desktop without effecting the rest of the layout...
Simple Example
This is an overly simplified example used to demonstrate the basic concept described above...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimpleLayout {
public static void main(String[] args) {
new SimpleLayout();
}
public SimpleLayout() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JMenuBar mb = new JMenuBar();
mb.add(new JMenu("File"));
mb.add(new JMenu("Add"));
mb.add(new JMenu("Edit"));
mb.add(new JMenu("Analize"));
mb.add(new JMenu("About"));
JFrame frame = new JFrame("Testing");
frame.setJMenuBar(mb);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BasePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BasePane extends JPanel {
private JTextArea species;
private JTextArea locations;
private JTextArea travelPaths;
private JDesktopPane desktopPane;
public BasePane() {
setLayout(new BorderLayout());
desktopPane = new JDesktopPane();
species = new JTextArea("Species");
locations = new JTextArea("Locations");
travelPaths = new JTextArea("TravelPaths");
JPanel controls = new JPanel(new GridLayout(3, 0));
controls.add(new JScrollPane(species));
controls.add(new JScrollPane(locations));
controls.add(new JScrollPane(travelPaths));
add(controls, BorderLayout.WEST);
add(desktopPane);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Your requirements might be slightly difference, but the basic concept should get you moving.
Depending on the structure of your application, I might be tempted to separate the Controls pane into a separate class as well.

How to add support for resizing when using an undecorated JFrame?

I would like to customize my titlebar, minimize-, maximize- and the close-button. So I used setUndecorated(true); on my JFrame, but I still want to be able to resize the window. What is the best way to implement that?
I have a border on the RootPane, and I could use MouseListeners on the Border or the RootPane. Any recommendations?
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.border.LineBorder;
public class UndecoratedFrame extends JFrame {
private LineBorder border = new LineBorder(Color.BLUE,2);
private JMenuBar menuBar = new JMenuBar();
private JMenu menu = new JMenu("File");
private JMenuItem item = new JMenuItem("Nothing");
public UndecoratedFrame() {
menu.add(item);
menuBar.add(menu);
this.setJMenuBar(menuBar);
this.setUndecorated(true);
this.getRootPane().setBorder(border);
this.setSize(400,340);
this.setVisible(true);
}
public static void main(String[] args) {
new UndecoratedFrame();
}
}
As you said, you have a border on your root pane. As a consequence, there is at least one location (below the palce where your border is drawn) where your root pane is the upmost component. As a consequence, you can add it a mouse listener and a mouse motion listener.
When your root pane is clicked (and the mouse button is pressed), your mouse and motion listeners will inform you of the initial and actual mouse position. As a consequence, you can update your frame size of the offset between both values, making your frame resizable.
Resizing Components shows one way.
I found a nice method in RootPane that gave me this functionality, so now I only have to find out how to customize the titlebar and the buttons on it.
I added this.getRootPane().setWindowDecorationStyle(JRootPane.FRAME); in my constructor for UndecoratedFrame.
There is some more reading about this on Sexy Swing App – The Unified Toolbar and How to control window decorations

Categories

Resources