Java Swing: How to change GUI dynamically - java

I need to add components dynamically. Moreover, I need to alter the layout dynamically.

For reference, here's an sscce that shows the essential method, validate(). This more elaborate example shows both requirements: it changes the layout and adds components dynamically.
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
/** #see http://stackoverflow.com/questions/5750068 */
public class DynamicLayout extends JPanel {
private static final LayoutManager H = new GridLayout(1, 0);
private static final LayoutManager V = new GridLayout(0, 1);
public DynamicLayout() {
this.setLayout(H);
this.setPreferredSize(new Dimension(320, 240));
for (int i = 0; i < 3; i++) {
this.add(new JLabel("Label " + String.valueOf(i), JLabel.CENTER));
}
}
private void display() {
JFrame f = new JFrame("DynamicLayout");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
JPanel p = new JPanel();
p.add(new JButton(new AbstractAction("Horizontal") {
#Override
public void actionPerformed(ActionEvent e) {
DynamicLayout.this.setLayout(H);
DynamicLayout.this.validate();
}
}));
p.add(new JButton(new AbstractAction("Vertical") {
#Override
public void actionPerformed(ActionEvent e) {
DynamicLayout.this.setLayout(V);
DynamicLayout.this.validate();
}
}));
f.add(p, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new DynamicLayout().display();
}
});
}
}

Example of changing the layout Dynamically :
package swinglayout;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LayoutChanger implements ActionListener{
JButton b1;
JButton b2;
JButton b3;
JButton b4;
JButton b5;
JButton b6;
/** This button set the flowlayout on panel2 with left orientation */
JButton flowLayout;
/** This button set the Gridlayout of 2,3 grid on panel2 */
JButton gridLayout;
/** This button set the Borderlayout on panel2*/
JButton borderLayout;
/**
* This panel is control panel where we use button to change
* layout of another panel
*/
JPanel panel;
/** This panel contain multiple button from b1 to b6 */
JPanel panel2;
JFrame frame;
public LayoutChanger() {
//set Default Look and Feel on frame
JFrame.setDefaultLookAndFeelDecorated(true);
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container con = frame.getContentPane();
con.setLayout(new BorderLayout());
panel = new JPanel();
panel2 = new JPanel();
//This button are used to only showing the layout effect
b1 = new JButton("HelloButton1");
b2 = new JButton("HelloButton2");
b3 = new JButton("HelloButton3");
b4 = new JButton("HelloButton4");
b5 = new JButton("HelloButton5");
b6 = new JButton("HelloButton6");
// By default panel have layout
panel2.add(b1);
panel2.add(b2);
panel2.add(b3);
panel2.add(b4);
panel2.add(b5);
panel2.add(b6);
// Layout changer button
flowLayout = new JButton("FlowLayout");
gridLayout = new JButton("GridLayout");
borderLayout = new JButton("BorderLayout");
//call Action listener on every layout changer button
flowLayout.addActionListener(this);
gridLayout.addActionListener(this);
borderLayout.addActionListener(this);
panel.add(flowLayout);
panel.add(gridLayout);
panel.add(borderLayout);
// add layout changer button panel at a top
//button panel at the center of container
con.add(panel, BorderLayout.PAGE_START);
con.add(panel2, BorderLayout.CENTER);
frame.setVisible(true);
frame.pack();
}
public void actionPerformed(ActionEvent e) {
//set the flowlayout on panel2
if(e.getSource() == flowLayout) {
FlowLayout flow = new FlowLayout(FlowLayout.LEFT);
panel2.setLayout(flow);
panel2.validate();
}
//set the gridlayout on panel2
if(e.getSource() == gridLayout) {
GridLayout grid = new GridLayout(2,3);
panel2.setLayout(grid);
panel2.validate();
}
//set the gridlayout but the problem if we don't set the constraint
//all button are set on center. So you remove the all button from panel
//Then set grid layout on panel and add them with constraints.
if(e.getSource() == borderLayout) {
panel2.remove(b1);
panel2.remove(b2);
panel2.remove(b3);
panel2.remove(b4);
panel2.remove(b5);
panel2.remove(b6);
BorderLayout border = new BorderLayout();
panel2.setLayout(border);
panel2.add(b1,BorderLayout.NORTH);
panel2.add(b2,BorderLayout.SOUTH);
panel2.add(b3,BorderLayout.EAST);
panel2.add(b4,BorderLayout.WEST);
panel2.add(b5,BorderLayout.CENTER);
panel2.add(b6,BorderLayout.BEFORE_FIRST_LINE);
panel2.validate();
}
}
public static void main(String args[]) {
new LayoutChanger();
}
}
One thing remember is you set new layout on panel don't forgot call the method validate() on panel.If you don't call this method you don't see the effect of change in layout. If you want to see the effect with out call the method you must resize the frame.
Also you can set same layout very easily like FlowLayout,GridLayout and BoxLayout, but when set BorderLayout it required constraints for adding element, so we first remove all component from panel by remove(Component comp) method then add the component in panel by constraint

Related

Java Swing BoxLayout: Adjusting space between panels in a multi-panel setup?

I'm writing this swing application and I'm using multiple panels in a BoxLayout format, but it seems to be that the empty space between the panels is being divided up between them and it looks really ugly. How do you adjust and customize how much space is put between panels? Sorry if this is a repost; I was unable to find an older post.
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
class gui extends JFrame implements ActionListener{
JFrame frame = new JFrame("Ark Admin Spawn Commands");
JPanel panel = new JPanel();
JComboBox dinos;
JComboBox corruptedDinos;
public JSlider dinoLevel;
JLabel input;
JLabel dino;
JLabel title;
gui(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
JPanel panelOne = new JPanel();
JPanel panelTwo = new JPanel();
JPanel panelThree = new JPanel();
mainPanel.add(panelOne);
mainPanel.add(panelTwo);
mainPanel.add(panelThree);
title = new JLabel("Spawning Dinos");
String[] dinoNames= {"The Island:"};
String[] corruptedDinoNames = {"Extinction:"};
dinos = new JComboBox(dinoNames);
corruptedDinos = new JComboBox(corruptedDinoNames);
dinoLevel = new JSlider(JSlider.HORIZONTAL, 1, 600, 400);
dinoLevel.setMajorTickSpacing(20);
dinoLevel.setPaintTicks(true);
input = new JLabel("Select Level: ");
event e = new event();
dinoLevel.addChangeListener(e);
dinos.addActionListener(this);
corruptedDinos.addActionListener(this);
panelOne.add(title);
panelTwo.add(input);
panelTwo.add(dinoLevel);
panelThree.add(dinos);
panelThree.add(corruptedDinos);
this.getContentPane().add(mainPanel);
this.setTitle("Ark Admin Spawn Commands");
this.setSize(600, 600);
this.setVisible(true);
}
public static void main (String args[]) {
new gui();
}
public class event implements ChangeListener{
#Override
public void stateChanged(ChangeEvent e) {
int value = dinoLevel.getValue();
input.setText("Level: " + value);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
}
}
but it seems to be that the empty space between the panels is being divided up between them
Correct. A BoxLayout will attempt to allocate extra space to all components.
However, it will respect the maximum size of each panel.
So to prevent the panels height from growing you can use code like:
JPanel panelOne = new JPanel()
{
#Override
public dimension getMaximumSize()
{
Dimension d = getPreferredSize()
d.width = Integer.MAX_VALUE;
return d;
}
};
Or because the default layout manager of the frame is a BorderLayout, you can just use:
//this.getContentPane().add(mainPanel);
add(mainPanel, BorderLayout.PAGE_START); // getContentPane() is not needed
The PAGE_START of the BorderLayout respects the preferred height of the component.

Moving JButtons

What would be the best way for me to move the buttons so they are under each other instead of beside each other (see image below)?
The code for this class is as follows. The Main method is in a different class.
package guiplay;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class MainGUI extends JFrame {
private JButton openReportSelection = new JButton("Open Report Viewer");
private JButton closeButton = new JButton("Close Program");
private JButton getCloseButton(){
return closeButton;
}
private JButton getOpenReportSelection(){
return openReportSelection;
}
public MainGUI(){
mainInterface();
}
private void mainInterface(){
setTitle("Program Information Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel centerPanel = new JPanel(new FlowLayout());
centerPanel.add(openReportSelection);
openReportSelection.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
JFrame reports = new JFrame();
new ReportGUI();
}
});
centerPanel.add(closeButton);
getCloseButton().addActionListener(new Listener());
add(centerPanel, BorderLayout.CENTER);
setSize(700,200);
setVisible(true);
}
}
You can use a BoxLayout as it aligns all elements either horizontally or vertically. Simply set BoxLayout's axis to BoxLayout.Y_AXIS.
Example:
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.BoxLayout;
import javax.swing.JButton;
public class BoxLayoutExample extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
BoxLayoutExample frame = new BoxLayoutExample();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public BoxLayoutExample() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 180, 150);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
JButton btnOpenReportViewer = new JButton("Open Report Viewer");
contentPane.add(btnOpenReportViewer);
JButton btnCloseProgram = new JButton("Close Program");
contentPane.add(btnCloseProgram);
}
}
If you want to control the size so that they are similar to each other, you can use a grid layout by setting the JFrame's content pane to a GridLayout:
contentPane.setLayout(new GridLayout(0, 1, 0, 0)); // the value of 1 here means 1 column
Don't put the JButtons in a container that uses FlowLayout but rather one that uses another layout that allows stacking of components. A GridLayout comes to mind if the buttons are to be the same size, or if they need to be different sizes, a BoxLayout.
Check out the Layout Manager Tutorial.
You could try using a BoxLayout instead of a FlowLayout. In that case, you could have:
JPanel centerPanel = new JPanel(new BoxLayout());
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); // Y_AXIS will cause the components to be added vertically
centerPanel.add(openReportSelection);
centerPanel.add(closeButton);
centerPanel.setMaximumSize(new Dimension(100, 60)); // Set the maximum horizontal and vertical distances used, as BoxLayouts expand to fill the provided area
Or as Hovercraft said, you could use a GridLayout, in which case you would specify it as follows:
JPanel centerPanel = new JPanel(new GridLayout(1,0); // The "0" parameter specifies as many rows as needed, but only one column
centerPanel.add(openReportSelection);
centerPanel.add(closeButton);
centerPanel.setMaximumSize(new Dimension(100, 60)); // GridLayouts will also expand to fill the entire area, so you'll probably want some size parameters.
You could also see this link for more on BoxLayouts, or this link for more on GridLayouts.

How to set Jbuttons to a specific place when you have a background in JLabel : code below

How to set Jbuttons to a specific place when you have a background in JLabel : code below
i can't get the jlabel to stay at the top and the buttons to stay south(bottom) ??
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonsClass extends JFrame
implements ActionListener {
JButton b1 = new JButton("button1");
JButton b2 = new JButton("button2");
JButton b3 = new JButton("button3");
JButton b4 = new JButton("button4");
JLabel label = new JLabel("buttons:");
public static void main(String[] args) {
new ButtonsClass();
}
public Jukebox() {
setLayout(new BorderLayout());
setContentPane(new JLabel(new ImageIcon("image.png")));
setLayout(new FlowLayout());
setSize(500,150);
setTitle("Backgroundwithbuttons");
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
JPanel top = new JPanel();
top.add(label);
add("North", top);
JPanel bottom = new JPanel();
bottom.add(b1);
bottom.add(b2);
bottom.add(b3);
bottom.add(b4);
add("South", bottom);
setVisible(true);
}
}
" i can't get the jlabel to stay at the top and the buttons to stay south(bottom)"
That's because you set the layout the BorderLayout, then immediately set it to FlowLayout. With FlowLayout, your BorderLayout positioning will do nothing.
setLayout(new BorderLayout());
setContentPane(new JLabel(new ImageIcon("image.png")));
setLayout(new FlowLayout());
Just get rid of the setLayout(new FlowLayout());
Also your constructor is wrong
public Jukebox() {
-Should be-
public ButtonClass() {
Also you need to set the layout of the JLabel that you set as the content pane. Yout constructor should look like this
public ButtonClass() {
JLabel background = new JLabel(new ImageIcon("image.png"));
background.setLayout(new BorderLayout());
setContentPane(background);
setTitle("Background with buttons");
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
JPanel top = new JPanel();
top.add(label);
add(top, BorderLayout.NORTH);
JPanel bottom = new JPanel();
bottom.add(b1);
bottom.add(b2);
bottom.add(b3);
bottom.add(b4);
add(bottom, BorderLayout.SOUTH);
//pack();
setVisible(true);
}
Also, add("North", top); is a deprecated method. Instead use add(top, BorderLayout.NORTH) and same for add(bottom, BorderLayout.SOUTH)
Also, Swing apps should be run on the Event Dispatch Thread. You can do so by wrapping the code in your main with a SwingUtilities.invokeLater...
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ButtonClass();
}
});
}
Also, you should set the panel's opaque property to false, if you want the image to show behind them.
top.setOpaque(false);
bottom.setOpaque(false);
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonClass extends JFrame
implements ActionListener {
JButton b1 = new JButton("button1");
JButton b2 = new JButton("button2");
JButton b3 = new JButton("button3");
JButton b4 = new JButton("button4");
JLabel label = new JLabel("buttons:");
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ButtonClass();
}
});
}
public ButtonClass() {
label.setForeground(Color.WHITE);
JLabel background = new JLabel(new ImageIcon(getClass().getResource("/resources/space.png")));
background.setLayout(new BorderLayout());
setContentPane(background);
setTitle("Background with buttons");
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
JPanel top = new JPanel();
top.setOpaque(false);
top.add(label);
add(top, BorderLayout.NORTH);
JPanel bottom = new JPanel();
bottom.setOpaque(false);
bottom.add(b1);
bottom.add(b2);
bottom.add(b3);
bottom.add(b4);
add(bottom, BorderLayout.SOUTH);
setSize(400, 300);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {}
}
Try using:
add(bottom, BorderLayout.SOUTH);
instead of:
add("South", bottom);
BorderLayout tutorial

Overlapping panel swing

I'm trying to do the following layout, where all JPanels are visible with the exception of panel2 when the program starts. When the user clicks btn1 then JCalendar and panel3 are set to invisible and panel2 becomes visible. The problem I'm having is that panel2 is not showing up with btn1 is clicked. However if i change the borderlayout of panel2 to one that is not being used (in this case WEST) it will show when the button is clicked, but its aligned on the left side and i want it to be centered across the form.
Code:
public class GUI extends JFrame implements ActionListener, PropertyChangeListener
{
private JPanel panel1, panel2, panel3;
private com.toedter.calendar.JCalendar calendar;
private Button btn1, btn2;
private JLabel label1, label2;
public GUI()
{
init();
}
private void init()
{
//panel1 components
panel1 = new JPanel();
btn1 = new JButton("Click me");
panel1.add(btn1);
//panel2 components
panel2 = new JPanel();
label1 = new JLabel("Time:");
label2 = new JLabel("Date:");
panel2.add(label1); panel2.add(label2);
//JCalendar
calendar = new com.toedter.calendar.JCalendar();
//panel3
panel3 = new JPanel();
//Add panels to JFrame
add(panel1, BorderLayout.NORTH);
add(calendar, BorderLayout.CENTER);
add(panel2, BorderLayout.CENTER); //if i set this to WEST it show!!
add(panel3, BorderLayout.EAST);
//event handling
btn1.addActionListener(this);
//hide panel2
panel2.setVisible(false);
pack();
}
public void actionPerformed(ActionEvent ae)
{
if(ae.getSource().equals(btn1)
{
calendar.setVisible(false);
panel3.setVisible(false);
panel2.setVisible(true); //make panel2 visible
panel2.updateUI();
revalidate();
repaint();
}
}
public static void main(String args[])
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new GUI().setVisible(true);
}
});
}
When i click btn1, JCalendar and panel3 is invisible but panel2 does not show
There are a number of issues that I can find...
BorderLayout will only ever allow a single component to occupy any given position. That is, two components can not share the CENTER position at the same time, regardless if one is invisible or not.
You should never call updateUI, this is used to tell UI components that the look and feel has changed that they should update in response to it.
Use revalidate to tell the container that some change has occurred to the layout that it should perform a new layout process...
Before click...
After Click...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
public class GUI extends JFrame implements ActionListener, PropertyChangeListener {
private JPanel panel1, panel2, panel3;
// private com.toedter.calendar.JCalendar calendar;
private JPanel calendar;
private JButton btn1, btn2;
private JLabel label1, label2;
public GUI() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
init();
}
private void init() {
//panel1 components
panel1 = new JPanel();
btn1 = new JButton("Click me");
panel1.add(btn1);
//panel2 components
panel2 = new JPanel();
label1 = new JLabel("Time:");
label2 = new JLabel("Date:");
panel2.add(label1);
panel2.add(label2);
//JCalendar
calendar = new JPanel();//new com.toedter.calendar.JCalendar();
calendar.setBorder(new LineBorder(Color.RED));
calendar.add(new JLabel("Calendar"));
//panel3
panel3 = new JPanel();
panel3.setBorder(new LineBorder(Color.BLUE));
panel3.add(new JLabel("Panel3"));
panel2.setBorder(new LineBorder(Color.GREEN));
//Add panels to JFrame
add(panel1, BorderLayout.NORTH);
add(calendar, BorderLayout.WEST);
add(panel2, BorderLayout.CENTER);
add(panel3, BorderLayout.EAST);
//event handling
btn1.addActionListener(this);
//hide panel2
panel2.setVisible(false);
pack();
}
public void actionPerformed(ActionEvent ae) {
if (ae.getSource().equals(btn1)) {
calendar.setVisible(false);
panel3.setVisible(false);
panel2.setVisible(true); //make panel2 visible
// panel2.updateUI();
revalidate();
repaint();
}
}
public static void main(String args[]) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new GUI().setVisible(true);
}
});
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
}
}
Now, I'm pretty sure this won't meet you over all requirements (as I see them)...You have at least two options...
Remove the Calendar component and add panel2 to the CENTER position when the button is clicked
Preferably, use a CardLayout
BorderLayout doesn't do overlapping, IIRC none of the layout managers are about "overlapping layout".
You'll need to do it a different way -- try using JLayeredPane with your existing JPanel & BorderLayout as the bottom layer, and then your (optional) panel as the top layer.
See: http://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html

How to Change a Swing JFrame Size on the fly

What I am trying to achieve is
Create a custom component (mypanel) that extends JPanel with JLabels and JButtons in it arranged via GridBagLayout.
Have a JFrame that would display multiple mypanel in a vertical stack and have its height change accordingly, depending on the number of mypanels added to it (width of the JFrame = width of mypanel).
When the JFrame's height becomes greater than the screen height, have a vertical scrollbar appear for scrolling
I have created mypanel successfully but having lot of trouble with the adding to the JFrame and setting its size, scrollbars part.
this is the code for my jframe
this.window = new JFrame("ADesktop Notifications");
this.window_panel = new JPanel();
this.window_panel_scroll = new JScrollPane(this.window_panel);
this.window.setBounds(this.top_left_x,this.top_left_y, this.width, this.height);
this.window_panel.setLayout(new FlowLayout());
this.window_panel.setAutoscrolls(true);
this.window.add(this.window_panel);
Try this example out (for dynamic expanding JFrame).
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class DynaFrame extends JFrame{
private JPanel basePnl = new JPanel();
public DynaFrame(){
this.setTitle("Dynamic panel addition");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//this.setSize(600, 700);
this.add(getMainPanel());
this.setLocationRelativeTo(null);
this.pack();
this.setVisible(true);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DynaFrame();
}
});
}
public JPanel getMainPanel(){
basePnl.setLayout(new BoxLayout(basePnl, BoxLayout.Y_AXIS));
basePnl.add(getRowPanel());
return basePnl;
}
public JPanel getRowPanel(){
JPanel pnl = new JPanel();
GridLayout gLayout = new GridLayout();
gLayout.setColumns(4);
gLayout.setRows(1);
pnl.setLayout(gLayout);
pnl.add(new JLabel("Filetype"));
pnl.add(new JTextField());
pnl.add(new JButton("Browse"));
JButton addBtn = new JButton("Add");
addBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
basePnl.add(getRowPanel());
DynaFrame.this.pack();
}
});
pnl.add(addBtn);
return pnl;
}
}

Categories