I have a program with a static header (BorderLayout.NORTH) and footer (BorderLayout.SOUTH) and was attempting to cycle different JPanel's for the body (BorderLayout.CENTER) using action listeners on a JComboBox in my header. As I understood BorderLayout, each time I added a JPanel (or any component) to the CENTER (or anywhere else in the layout) it would overwrite the old content. That mostly happens, but the new CENTER retains the outline of the previous JPanel over the top of it (just the outline as I removeAll() from the panel).
I've been googling this expecting that I've done a common mistake, but all I'm seeing is people trying to negate the overwrite ability which I can't seem to trigger. Tried several different fixes (a single Jpanel declared as an instance variable and then changing it's content using removeAll/revalidate/repaint, creating separate panel's and adding them, etc...).
I'll include the majority of the code below but here is my constructor:
public UserInterface(Operator o, LocalDate d) {
DefaultDateModel model = new DefaultDateModel(d);
user = o;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//setup Menus
if (user.getAccessPrivs().equalsIgnoreCase("OPRTR")) {
OperatorMenus menu = new OperatorMenus();
setJMenuBar(menu); }
else if (user.getAccessPrivs().equalsIgnoreCase("ADMIN")) {
AdminMenus menu = new AdminMenus();
setJMenuBar(menu); }
else {
GalileoMenus menu = new GalileoMenus();
setJMenuBar(menu); }
//creates header with navigation buttons
add(new NavButtons(model), BorderLayout.NORTH);
//loads content
viewConstructor(model);
//creates footer
JPanel footer = new JPanel();
footer.setLayout(new BorderLayout());
footer.setBackground(Color.BLACK);
focusPoint = new JLabel(DateTimeFormatter.ofPattern("E, dd MMM yyyy").format(model.getDate()));
focusPoint.setForeground(Color.RED);
footer.add(focusPoint, BorderLayout.WEST);
JLabel loggedIn = new JLabel(user.getName(), JLabel.RIGHT);
loggedIn.setForeground(Color.CYAN);
footer.add(loggedIn, BorderLayout.EAST);
add(footer, BorderLayout.SOUTH); }
...and the method I use to modify the body:
public void viewConstructor(DefaultDateModel m) {
DefaultDateModel model = m;
//removeAll();
body.removeAll();
body.revalidate();
body.repaint();
getContentPane().remove(body);
//body = new JPanel();
//body.revalidate();
//body.repaint();
//body.updateUI();
//body.setOpaque(false);
//set window frame's title
if (panelZone == MYVIEW) { setTitle("My View"); }
else if (panelZone == OPERTR) { setTitle("Operations"); }
else if (panelZone == SHIFTS) { setTitle("Scheduling"); }
else if (panelZone == FISCAL) { setTitle("Fiscal Report"); }
//builds body panel
if (panelZone == MYVIEW) {
add(new JScrollPane(buildMyView(model.getDate())), BorderLayout.CENTER); }
else if (panelZone == OPERTR) {
add(new JScrollPane(buildOperatorView(model.getDate())), BorderLayout.CENTER); }
else if (panelZone == SHIFTS) {
add(new JScrollPane(buildSchedulingView(model.getDate())), BorderLayout.CENTER); }
else if (panelZone == FISCAL) {
add(new JScrollPane(buildFiscalView(model.getDate())), BorderLayout.CENTER); }
validate();
revalidate();
repaint();
pack(); }
I know I should put more comments, etc... but I put those methods up separately because the rest is kinda messy right now and that seems to be the problem area. For completeness sake here's a screenshot of the problem and the rest of the code segment (I call UserInterface frame = new UserInterface(Current_User); in my main and that's about it).
Note Tried to include the rest of my code but hit the character limit. If anyone want's me to post more of the content I'll reply with whatever method they think is an issue.
Update
Realized I was using JScrollPane, so I created an instance variable:
private JScrollPane pane = new JScrollPane();
so I could .removeAll() + revalidate() + repaint() pane
Now I've just got nothing in the body. Here's where my method is at:
public void viewConstructor(DefaultDateModel m) {
DefaultDateModel model = m;
pane.removeAll();
//set window frame's title
if (panelZone == MYVIEW) { setTitle("My View"); }
else if (panelZone == OPERTR) { setTitle("Operations"); }
else if (panelZone == SHIFTS) { setTitle("Scheduling"); }
else if (panelZone == FISCAL) { setTitle("Fiscal Report"); }
//builds body panel
// JScrollPane pane = new JScrollPane();
if (panelZone == MYVIEW) {
// add(new JScrollPane(buildMyView(model.getDate())), BorderLayout.CENTER); }
pane.setViewportView(buildMyView(model.getDate())); }
else if (panelZone == OPERTR) {
// add(new JScrollPane(buildOperatorView(model.getDate())), BorderLayout.CENTER); }
pane.setViewportView(buildOperatorView(model.getDate())); }
else if (panelZone == SHIFTS) {
// add(new JScrollPane(buildSchedulingView(model.getDate())), BorderLayout.CENTER); }
pane.setViewportView(buildSchedulingView(model.getDate())); }
else if (panelZone == FISCAL) {
// add(new JScrollPane(buildFiscalView(model.getDate()))); }
pane.setViewportView(buildFiscalView(model.getDate())); }
//validate();
// body.revalidate();
// body.repaint();
pane.revalidate();
pane.repaint();
//repaint();
pack(); }
SSCCE
package interfaceComponents;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
public class SSCCE extends JFrame {
private static final long serialVersionUID = -5249508445298805323L;
private JScrollPane pane = new JScrollPane();
private int stage = 0;
public SSCCE() {
JPanel header = new JPanel();
JButton cycler = new JButton("Cycle");
cycler.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
stage++;
if (stage == 3) { stage = 0; }
buildBody(); }
});
header.add(cycler);
add(header, BorderLayout.NORTH);
add(pane, BorderLayout.CENTER);
buildBody();
JPanel footer = new JPanel();
footer.add(new JLabel("Random Text"));
add(footer, BorderLayout.SOUTH); }
public void buildBody() {
JPanel body = new JPanel();
if (stage == 0) { body.setBackground(Color.RED); }
else if (stage == 1) { body.setBackground(Color.WHITE); }
else { body.setBackground(Color.BLUE); }
pane.setViewportView(body); }
}
As I understood BorderLayout, each time I added a JPanel (or any component) to the CENTER (or anywhere else in the layout) it would overwrite the old content.
No it doesn't overwrite the old content. The BorderLayout only keeps track of the last component added at any given constraint position. So when the layout manager is invoked, only that component is given a size/location.
However, any previous component will still have a size/location. Also because of how Z-Ordering works, the last component added is painted first. So the new component is painted and then the old component is painted over top of the new component.
To prevent this from happening you must remove the old panel first before adding the new panel.
attempting to cycle different JPanel's for the body
The better solution is to use a CardLayout as it will manage the cycling of the panels for you. So add a panel using the CardLayout to the CENTER of the BorderLayout. Then add all the swappable panels to the card layout panel. Read the section from the Swing tutorial on How to Use CardLayout for more information and examples.
Edit:
How would you "remove the old panel first"
Well, somewhere you must have code like:
panel.add(panel1, BorderLayout.CENTER);
So if you want to swap panels you need code like:
panel.remove(panel1);
panel.add(panel2, BorderLayout.CENTER);
panel.revalidate();
panel.repaint();
So it is up to you to manage the reference to the last component added to the CENTER so you can manually remove it. This is why the CardLayout is easier, it does the management for you.
Edit2:
Didn't notice the scroll pane before. This makes it even easier. When you create the frame you just use:
frame.add(scrollPane, BorderLayout.CENTER);
Then when you want to swap panels its one line of code:
scrollPane.setViewportView( panel2 );
Related
I am new to Swing and cannot find a page that helps me understand JTabbedPane. I cannot find a way to control the layout of components of the tabbed panels. I can layout each of my panels correctly as separate GUIs but not in a tabbed pane like I need to do. I would like to use the BorderLayout not FlowLayout.
Also, you can see I'm trying to use colors to keep track of my panels and their components. I cannot set the background of the JTabbedPane. It is still the default grey. Can someone tell me why this is?
Thank you for any advice you can give.
What I have so far appears to follow a 'flow layout' despite any changes I've tried
(Methods have been removed or nearly removed to keep code shorter)
public class GUIFrame extends JFrame {
public GUIFrame(String title) {
JFrame frame = new JFrame(title);
Container c = frame.getContentPane();
buildGUI(c);
setFrameAttributes(frame);
}
private void buildGUI(Container c) {
c.setLayout(new BorderLayout());
c.setBackground(Color.BLACK);
JTabbedPane tabs = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
tabs.setBackground(Color.YELLOW);
c.add("Center", tabs);
tabs.addTab("Specialty", new SpecialtyPanel());
tabs.addTab("Treatment", new TreatmentPanel());
tabs.addTab("Doctor", new DoctorPanel());
tabs.addTab("Patient", new PatientPanel());
}
private void setFrameAttributes(JFrame f) {
f.setSize(500, 500);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
MedicalSystemIO test = new MedicalSystemIO();
new GUIFrame("Tabbed Title");
}
public class SpecialtyPanel extends JPanel implements ActionListener {
JTextField jteInput = null;
DefaultListModel<String> model = new DefaultListModel<String>();
JList<String> list = new JList(model);
JScrollPane pane = new JScrollPane(list);
public SpecialtyPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.black));
buildGUI(panel);
}
private void buildGUI(JPanel panel) {
JPanel jpaInput = createInputPanel();
JPanel jpaProcess = createProcessPanel();
JPanel jpaOutput = createOutputPanel();
//panel.setLayout(new BorderLayout());
add("North", jpaInput);
add("Center", jpaProcess);
add("South", jpaOutput);
}
private JPanel createInputPanel() {
JPanel jpaInput = new JPanel();
jpaInput.setBackground(Color.RED);
return jpaInput;
}
private JPanel createProcessPanel() {
JPanel jpaProcess = new JPanel();
jpaProcess.setBackground(Color.BLUE);
return jpaProcess;
}
private JPanel createOutputPanel() {
JPanel jpaOutput = new JPanel();
jpaOutput.add(pane);
return jpaOutput;
}
The SpecialtyPanel is shown that way (flow layout) as you are putting the components on it in the wrong way:
No need for passing a new panel into the buildGUI method as you want to put them directly on the SpecialtyPanel which already is a JPanel,
you commented out the setting of the BorderLayout and
you used the wrong notation of passing the layout constraints in the add methods.
Your constructor and build method should look like this:
public SpecialtyPanel() {
buildGUI();
}
private void buildGUI() {
setBorder(BorderFactory.createLineBorder(Color.black));
JPanel jpaInput = createInputPanel();
JPanel jpaProcess = createProcessPanel();
JPanel jpaOutput = createOutputPanel();
setLayout(new BorderLayout());
add(jpaInput, BorderLayout.NORTH);
add(jpaProcess, BorderLayout.CENTER);
add(jpaOutput, BorderLayout.SOUTH);
}
To have the panel another color than gray you have to color the component that is put on the tabbed pane as it covers the whole space. Add the desired color to the buildGUI method, e.g.:
private void buildGUI(JPanel panel) {
// ...
setBackground(Color.YELLOW);
}
As a JPanel is opaque by default (that means not transparent), you need to set panels on top (except those which you colored explicitly) to be transparent. In case of SpecialtyPanel:
private JPanel createOutputPanel() {
JPanel jpaOutput = new JPanel();
jpaOutput.add(pane);
jpaOutput.setOpaque(false); // panel transparent
return jpaOutput;
}
I'm trying to get my labels to be in the center of the window when I press the corresponding button, but it instead just sits on top of the button instead of sitting in the middle and i'm not sure why
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class navigator extends JFrame
{
Container con;
public navigator(){
super("JFrame");
JFrame newFrame = new JFrame("Navigator");
newFrame.setSize(400, 400);
newFrame.setVisible(true);
newFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
con = getContentPane();
BorderLayout newLayout = new BorderLayout();
con.setLayout(newLayout);
JButton newButton = new JButton("Up");
newFrame.add(newButton, BorderLayout.NORTH);
JButton newButton2 = new JButton("Left");
newFrame.add(newButton2, BorderLayout.WEST);
JButton newButton3 = new JButton("Down");
newFrame.add(newButton3, BorderLayout.SOUTH);
JButton newButton4 = new JButton("Right");
newFrame.add(newButton4, BorderLayout.EAST);
JLabel newLabel = new JLabel("Going up!");
newFrame.add(newLabel, BorderLayout.CENTER);
newLabel.setVisible(false);
newButton.add(newLabel);
JLabel newLabel2 = new JLabel("Going left!");
newFrame.add(newLabel2, BorderLayout.CENTER);
newLabel2.setVisible(false);
newButton2.add(newLabel2);
JLabel newLabel3 = new JLabel("Going down!");
newFrame.add(newLabel3, BorderLayout.CENTER);
newLabel3.setVisible(false);
newButton3.add(newLabel3);
JLabel newLabel4 = new JLabel("Going right!");
newFrame.add(newLabel4, BorderLayout.CENTER);
newLabel4.setVisible(false);
newButton4.add(newLabel4);
newButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
newLabel.setVisible(true);
newLabel2.setVisible(false);
newLabel3.setVisible(false);
newLabel4.setVisible(false);
}
});
newButton2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
newLabel2.setVisible(true);
newLabel.setVisible(false);
newLabel3.setVisible(false);
newLabel4.setVisible(false);
}
});
newButton3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
newLabel3.setVisible(true);
newLabel2.setVisible(false);
newLabel.setVisible(false);
newLabel4.setVisible(false);
}
});
newButton4.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
newLabel4.setVisible(true);
newLabel2.setVisible(false);
newLabel3.setVisible(false);
newLabel.setVisible(false);
}
});
}
public static void main(String[] args){
navigator myNavigator = new navigator();
}
}
JLabel newLabel = new JLabel("Going up!");
newFrame.add(newLabel, BorderLayout.CENTER);
newLabel.setVisible(false);
newButton.add(newLabel); // ???
A component can only have a single parent. So you can't add the label to the frame and the button. I'm not even sure why you would be attempting to add the label to the button.
In any case you can't add four labels to the center of the frame. The BorderLayout only allows one component in each area, so only the last component added will ever be visible. The BorderLayout will only set the size of the last button added. All the other buttons will have a size of (0, 0) so there is nothing to paint.
So just add a single label and then change the text using the setText(...) method in your ActionListener.
However, once you fix this you will still have a problem. By default a label is painted at the left of the space available to the label.
If you want the text displayed in the center then you need to use:
label.setHorizontalAlignment(JLabel.CENTER);
Also, all components should be added to the frame before making the frame visible.
Finally, class names should start with an upper case character. Look at the class names of the JDK API and follow the conventions used their.
When using a BorderLayout, you can only put one control in each section. So you can only put one button in CENTER, for instance.
If you want to put more things in one area, then you need to create a new JPanel, put it in CENTER, and then place the buttons on the newly created JPanel. (Of course following the same layout rules for it). You can recursively add as many jpanels as you like.
I have some code that get's data from a h2 dB and then displays it in a JScrollPane. I have added a button that should refresh the information but it isn't working.
This is my code:
Code that is used for the JPanel:
JPanel pList = new JPanel();
Component pListl = new JLabel("Here you can view players. Searching and more data will be coming soon.");
pList.add(pListl,SwingConstants.CENTER);
tabbedPane.addTab("Player List",pList);
tabbedPane.setMnemonicAt(0,KeyEvent.VK_1);
JButton ref = new JButton("Refresh");
pList.add(ref);
ref.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JScrollPane stable;
try {
stable = memlistpop(stat);
pList.remove(stable);
pList.add(stable);
JOptionPane.showMessageDialog(pList, stable);
} catch (SQLException e) {
e.printStackTrace();
}
}
});
JScrollPane stable = memlistpop(stat);
pList.add(stable);
And this is the memlistpop funcation:
public static JScrollPane memlistpop(Statement stat) throws SQLException{
ResultSet rs = stat.executeQuery("SELECT id,name,level,xp,trophycount FROM avatars");
JTable t = new JTable(DbUtils.resultSetToTableModel(rs)){;
private static final long serialVersionUID = 1L;
public boolean isCellEditable(int row,int column){
return false;
}
};
t.setPreferredSize(new Dimension(500, 300));
JScrollPane stable = new JScrollPane (t);
stable.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
stable.setPreferredSize(new Dimension(500, 323));
//add the table to the frame
return stable;
}
Does anyone have any idea why this isn't working? Thanks!
You may add a layout manager before adding your objects.
If you want to clear the content of your panel, then:
panel.removeAll();
panel.setLayout(new BorderLayout()); // or other layout managers, such as boxlayout, etc
panel.add(myobject); // in this case, pList.add(stable);
That is what I do for myself;
Don't keep removing and adding components from a visible GUI. Instead you can just update the component in the scroll pane by using:
scrollPane.setViewportView( someComponent );
If you ever do need to remove/add components to a visible GUI, then the basic code is:
panel.remove(...);
panel.add(...);
panel.revalidate(); // to invoke the layout manager
panel.repaint();
Currently working on a project and I need to add a panel I've made to a scrollpane or a table dynamically. The scrollpane should start out empty and add the panels.
The GuiConstructor is where i make the window.
My problem is that if I don't comment out the setSize in the GuiConstructor, the window starts out very small.
Secondly, when i press the add button, it doesn't add the panels.
public GuiConstructor(){
super(APPLICATION_NAME);
setLayout(new BorderLayout());
LoopControlWindow loopwin = new LoopControlWindow(connect);
add(loopwin , BorderLayout.NORTH);
pack();
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
//this.setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public class LoopControlWindow extends JPanel {
IConnector connect;
public LoopControlWindow(IConnector connect) {
super(new BorderLayout());
this.connect = connect;
initPane();
}
private void initPane() {
JPanel panel = new JPanel(new GridLayout(3,1));
FolderSearchComp fsc = new FolderSearchComp(connect);
JScrollPane scrollPane = new JScrollPane();
JButton button = new JButton("Add");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
panel.add(new FolderSearchComp(connect));
scrollPane.getViewport().setView(panel);
}
});
scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setViewportBorder(new LineBorder(Color.BLACK));
add(scrollPane, BorderLayout.CENTER);
add(button, BorderLayout.SOUTH);
}
This is typical of this style of GUI app. You need to tell the layout manager how big to make the Window initialy without using setSize(). The way to do this is to override getPreferredSize() to return a default size. In your case:
public LoopControlWindow extends JPanel {
private Dimension size;
public LoopControlWindow() {
Preferences prefs = Preferences.userNodeForPackge("your.java.package");
size = new Dimension(prefs.getInt("width", 800), prefs.getInt("height", 600));
}
public Dimension getPreferredSize() {
return size;
}
}
By doing it this way you can store the user preferences for the window dimensions but also provide sensible defaults to start.
You should also make sure that this JPanel is your main panel and is added to the JFrame at BorderLayout.CENTER to ensure that your window gets drawn properly. All other panels should be somewhere inside this one.
Once you have this set up calling pack() will work correctly.
For your first problem, you need to specify a size for the initial JFrame(). One way is to call setSize as you are doing. Another is to override getPreferredSize() to return the default size. And one other option is to find the size of the user's monitor and set the JFrame to be a percentage of that size. That way you can ensure your window always fits on your user's screen.
int height = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration().
getBounds().height;
height = (int) (height * .85);
int width = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration().
getBounds().width;
width = (int) (width * .85);
frame.setSize(width, height);
Second, you need to call revalidate() and repaint() anytime you add or remove from a layout in order to see the changes.
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
panel.add(new FolderSearchComp(connect));
scrollPane.getViewport().setView(panel);
revalidate();
repaint();
}
});
One note on border layout. The components in it will not resize with your JFrame. Whatever component that is placed in BorderLayout.CENTER will, however. That component will grow to fill all extra space as the JFrame grows. It will also be the component that shrinks when the JFrame windows gets smaller.
The structure likes this:
I have a Frame and two button - btnA, btnB
when I press btnA, a createPanel() function will return a panelA to be displayed in the frame,
and so does btnB.
btnA and btnB can be switched.
Before I add the panel into the frame, I use a clearPanel() function to clear the existing panels in the frame.
but the question is when I resize or click the panel, I can see the previous panels that should be removed already.
Is there anything I lost?
public void actionPerformed(ActionEvent e) {
String buttonString = e.getActionCommand();
if (buttonString.equals("A")) {
clearPanel();
A = new APanel();
this.getContentPane().add(A.createPanel(), BorderLayout.CENTER);
this.pack();
componentMap.put("A", A);
btnB.setEnabled(true);
btnA.setEnabled(false);
}
else if (buttonString.equals("B")) {
clearPanel();
chart = new BPanel();
this.getContentPane().add(B.createPanel(), BorderLayout.CENTER);
this.pack();
componentMap.put("B", B);
btnA.setEnabled(true);
btnB.setEnabled(false);
}
}
private void clearPanel() {
if (!componentMap.isEmpty()) { // I store panels in a HashMap
for (Object o: componentMap.values()) {
this.getContentPane().remove((JPanel)o);
}
this.getContentPane().invalidate();
componentMap.clear();
}
}
You are adding A.createPanel() and B.createPanel() to the contentPane but you store A and B in your componentMap. Therefore, when you call this.getContentPane().remove((JPanel)o);, you are doing this on A and/or B which are not in the content pane and therefore you don't remove anything.
You could use a simpler/safer approach if you want to clear the content pane:
this.getContentPane().removeAll();