java swing about dynamic panel - java

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();

Related

Create a Method to add components in Selected JTabbedPane

I am a newbie, and I have already passed some days to get the idea, but could not solve it, if anyone can help me.
I have main two JPanel in one JFrame. I have some buttons in the first panel. I have One method to add a panel to theJTabbedPane by calling another method to create that JPanels then add it to the Tab.
I want a method which will add any buttons/components to the Selected tab which I want whenever I click the button from the first panel.
How can I do this. I can post those three method's code here if anyone wish to help me. Thanks in advance.
The first code add new tabs to the JTabbedPane,
private class TabPlus implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JPanel panel = CreateSlice();
String title = "Slice " + String.valueOf(pane.getTabCount());
pane.insertTab(title, null, panel, null, pane.getTabCount() - 1);
}
}
The second code is for selecting the tabs,
private class TabSelect implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
JTabbedPane source = (JTabbedPane) e.getSource();
if (source.getSelectedComponent() != null) {
int index = source.getSelectedIndex();
System.out.print(index);
}
}
}
This creates the JPanel to be added to the JTabbedPane,
public JPanel CreateSlice() {
JPanel Slice = new JPanel();
Slice.setPreferredSize(new Dimension(550, 600));
return Slice;
}
The buttons are in another Panel with their own action.
GUI Image
You can add a panel with butttons this way:
public JPanel CreateSlice() {
JPanel Slice = new JPanel();
Slice.setPreferredSize(new Dimension(550, 600));
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(new JButton("dumpButton"));
buttonsPanel.setVisible(false);
Slice.add(buttonsPanel);
return Slice;
}
And whenever a selection occurs set the visibility of buttonsPanel to true.
I think, I found one way to do this, My index variable int index = source.getSelectedIndex(); must be public variable, then in Action event of the buttons from another tab will add component to this selected JTabbedPane.
In my case I introduced and ArrayList of panel (network_slices), and every time I am adding a panel to the JTabbedPane, it is also adding to the ArrayList. then I called the panel from the ArrayList by using the index of selected JTabbedPane. Here is the part of the code.
private class TabSelect implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
JTabbedPane source = (JTabbedPane) e.getSource();
if (source.getSelectedComponent() != null) {
index = source.getSelectedIndex();
System.out.print(index);
}
}
}
private class AddTab implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
Slice panel = new Slice();
panel.setPreferredSize(new Dimension(550, 600));
String title = "Slice " + String.valueOf(pane.getTabCount());
pane.insertTab(title, null, panel, null, pane.getTabCount() - 1);
network_slices.add(panel);
}
}
private class AddNetworkFuncitons implements ActionListener {
public void actionPerformed(ActionEvent e) {
String buttonselected = (String) e.getActionCommand();
JButton button = new JButton(buttonselected);
network_slices.get(index).add(button).setVisible(true);
System.out.print(buttonselected);
}
}

When adding JPanel to BorderLayout.Center previous content remains visible

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 );

Add Object to JPanel after button click

I have created a JScrollPane with a JPanel inside it and I want to add JPanel/JLabel/Other objects after pressing the button. For example after three button presses I want to get something like this:
I tried myJPane.add(testLabel) with testlabel.setBounds()but no result, I don't want to use GridLayout because of the unchangeable sizes. I would like it if the added objects had different sizes - adjusted to the text content.
What should I use for it and how?
Thanks in advance.
Best regards,
Tom.
Here is a JPanel inside a JScrollPane that adds JLabels to it when pressing the button:
public class Example extends JFrame {
public Example() {
JPanel boxPanel = new JPanel();
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.PAGE_AXIS));
JTextField textField = new JTextField(20);
JButton sendButton = new JButton("Send");
sendButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JLabel label = new JLabel(textField.getText());
label.setOpaque(true);
label.setBackground(Color.RED);
boxPanel.add(label);
boxPanel.add(Box.createRigidArea(new Dimension(0,5)));
textField.setText("");
boxPanel.revalidate();
// pack();
}
});
JPanel southPanel = new JPanel();
southPanel.add(textField);
southPanel.add(sendButton);
add(new JScrollPane(boxPanel));
add(southPanel, BorderLayout.PAGE_END);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new Example();
}
}
The BoxLayout will stack the labels on top of each other.
Notes:
setOpaque(true) must be called on label for it to honor the background color.
Box.createRigidArea is used for creating gaps. Use it as you wish.
The call to revalidate() is imperative in order to display the new components immediately.
Calling pack() (on the JFrame) will resize it each time to fit all the new components. I just put it there for demonstration since the initial frame size is too small to display the initial components added.
I will use a BoxLayout, creating a vertical box, and after each button action, it will add a new JPanel to this box.
Example:
public class YourChat extends JPanel{
private JScrollPane sc;
private Box bv;
public YourChat(){
bv = Box.createVerticalBox();
sc = new JScrollPane(bv);
//your functions (panel creation, addition of listeners, etc)
add(sc);
}
//panel customized to have red backgroud
private class MyPanel extends JPanel(){
private JLabel label=new JLabel();
public MyPanel(String text){
setBackgroundColor(Color.red);
add(label);
}
}
//inside the action listener
public void actionPerformed(ActionEvent e) {
sc.add(new MyPanel(textField.getText()));
textField.setText("");
}
}
For extra information check on:
[https://docs.oracle.com/javase/tutorial/uiswing/layout/box.html]
See also the example
[http://www.java2s.com/Code/Java/Swing-JFC/VerticalandhorizontalBoxLayouts.htm]
Use BoxLayout if you want only add vertically, otherwise you can use FlowLayout for both directions.

Java: Removing a JLayeredPane and adding a ContentPane

I have set a game menu screen on my JFrame using a layered panel. I want to remove all the components from the JFrame and add a new content panel. I've spent a few hours trying to work through this issue but I am unable to come up with a good solution.
public myJFrame ()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setSize (1030, 727);
setVisible(true);
newGame = new JButton();
loadGame = new JButton();
quitGame = new JButton();
newGame.setBounds(new Rectangle(10,600,200,110));
newGame.setIcon(pic1);
newGame.setOpaque(false);
newGame.setContentAreaFilled(false);
newGame.setBorderPainted(false);
loadGame.setBounds(new Rectangle(210,600,200,110));
loadGame.setIcon(pic2);
loadGame.setOpaque(false);
loadGame.setContentAreaFilled(false);
loadGame.setBorderPainted(false);
quitGame.setBounds(new Rectangle(410,600,200,110));
quitGame.setIcon(pic3);
quitGame.setOpaque(false);
quitGame.setContentAreaFilled(false);
quitGame.setBorderPainted(false);
background.setBounds(0,0,1030,727);
titletext.setBounds(0,0,726,170);
lp = getLayeredPane();
lp.add(titletext, new Integer (4)); //* I messed around with these integers quite a bit. *//
lp.add(newGame, new Integer (20)); //* They don't seem to be logically adding to the panel. *//
lp.add(loadGame, new Integer(20)); //* I want the background and titletext to appear behind *//
lp.add(quitGame, new Integer (20)); //* the JButtons. *//
lp.add(background, new Integer (1));
newGame.addActionListener(this);
loadGame.addActionListener(this);
quitGame.addActionListener(this);
}
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(4);
getContentPane().add(mop, "Center"); //* Removes myWelcomePanel mwp and adds myOptionsPanel mop *//
repaint();
mop.startgame.addActionListener(this);
}
I have tried repainting, revalidating and removing only certain components but nothing seems to work. I would expect the code posted above to remove the titletext image, but it removes the background image instead.
I would prefer not to mess with the second content pane because it contains a number of objects which I have configured previously.
JLayeredPane.remove(int) refers to the child's absolute index, not its layer position. Why don't you either remove by reference, or simply set the child component's visibility to hidden?
I found a way to make it work. It seems that I must remove each component individually instead of using removeAll(). I identified a new GridLayout sp and set that as the layout. Finally, I had to use revalidate instead of repaint. Repaint left me with a blank screen. If anyone could comment on what is happening here, I would appreciate it!
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(background);
lp.remove(titletext);
lp.remove(newGame);
lp.remove(loadGame);
lp.remove(quitGame);
getContentPane().setLayout(sp);
getContentPane().add(mop, "Center");
revalidate();
mop.startgame.addActionListener(this);
}

Passing event from child to parent

Is it possible to pass events from an instance class to a parent class?
I have a JFrame with a container that has two JPanels. The upper panel contains another smaller JPanel and I've set up a MouseListener successfully to execute a command when the small panel is clicked. I want to display text on the bottom panel when the small panel is clicked. The problem is that the event handling is done within the upper panel class, which does not have access to the instance of the other panel on the JFrame.
Here is the main window code below:
public CalendarWindow() {
// Set global variables
size = new Dimension(600,600);
setMinimumSize(size);
// Set default window layout and behaviour
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
Container c = getContentPane();
// Create panels and components
eventPanel = new EventPanel(new Dimension(size.width - 30, size.height/3));
CalendarPanel calendarPanel = new CalendarPanel(new Dimension(size.width - 30, size.height*2/3));
// Add components and panels to content pane
c.add(eventPanel, BorderLayout.SOUTH);
c.add(calendarPanel, BorderLayout.NORTH);
}
This is the event handling within calendarPanel:
boxes[i][j].addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if(selectedBox != null)
selectedBox.unselect();
selectedBox = (DateBox)e.getSource();
selectedBox.getParent().getParent().dispatchEvent(e);
if(SwingUtilities.isRightMouseButton(e)) {
rightClickMenu menu = new rightClickMenu();
menu.show(e.getComponent(), e.getX(), e.getY());
menu.newEvent.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
selectedBox.addCalendarEvent(new CalendarEvent("Kaupa mjólk", 14, 15));
}
});
}
}
});
The boxes variable is a type that extends JPanel. How can I make it so that the CalendarWindow detects the mouse click in the upper panel?
Make a method in your parent class that changes the text,
and pass the parent class to the eventPanel
(add a parameter eg. public eventPanel( CalendarWindow cw),
then pass it, new eventPanel(this)),
and then call the method of the parent within the eventPanel (cw.themethod())

Categories

Resources