I am trying to make a text editor with a button that appears at the bottom right of the editor regardless if you scroll up or down and appears over the text area
import javax.swing.*;
import java.awt.*;
public class Problem{
public static void main(String[] args){
//Setting up the frame
JFrame window = new JFrame();
window.setSize(600, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Making the LayeredPane
JLayeredPane LP = new JLayeredPane();
LP.setLayout(new BorderLayout());
//Making the ScrollPane and JTextArea
JTextArea textArea = new JTextArea(100,50);
textArea.setText("Test Text");
JScrollPane back = new JScrollPane();
back.setViewportView(textArea);
//Making the panel that appears in the front of the text
JPanel front = new JPanel();
front.setLayout(null);
front.setBackground(new Color(0,0,0,0));
front.setOpaque(false);
JButton button = new JButton("test");
button.setBounds(200,200,50,20);
front.add(button);
LP.add(back,BorderLayout.CENTER);
LP.setLayer(back,0,0);
LP.add(front,BorderLayout.CENTER);
LP.setLayer(front,1,0);
window.add(LP);
window.setVisible(true);
}
}
I am seeing just the JButton with a white background, if I don't add the second layer "front" I see my back JScrollPane with the JTextArea
Caveat
I'm not a fan of this is idea. It's not a "common" UX concept that many desktop users would be presented with and there are a number of, arguably, better solutions which leverage the pre-existing experience of users.
This requires some "hacking" to get to work, so, there's no guarantee that it will work on all platforms or continue to work into the future.
Why doesn't it work?
This is a rather technical question which delves deep into the core of how Swing, and in particular, the JScrollPane work. Let's just say, I don't have the time or desire to dig into, but I know the JScrollPane is heavy optimised, which may be affecting the way in which anything which overlays it gets updated - or it could just be the way that the painting system works.
Runnable example...
This takes the idea by camickr (all credit to him), but instead of using a OverlayLayout, makes use of a GridBagLayout to position the button. Why? Because the GridBagLayout gives me more control over the position of the button - it's a personal thing.
import java.awt.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JTextArea textArea = new JTextArea(40, 40);
try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/resources/StarWarsNewHope.txt"))) {
textArea.read(reader, "A long list");
} catch (IOException exp) {
exp.printStackTrace();
}
JButton button = new JButton("Am I in your way yet");
JPanel contentPane = new JPanel() {
#Override
public boolean isOptimizedDrawingEnabled() {
return false;
}
};
contentPane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
// Change this to reposition the button some where else
gbc.anchor = GridBagConstraints.FIRST_LINE_END;
gbc.insets = new Insets(32, 32, 32, 32);
gbc.ipadx = 16;
gbc.ipady = 16;
contentPane.add(button, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
JScrollPane scrollPane = new JScrollPane(textArea);
contentPane.add(scrollPane, gbc);
add(contentPane);
}
}
}
You should probably also look at How to Use Scroll Panes and the section on Providing Custom Decorations for some alternatives
Swing is designed/optimized to display/paint components in 2 dimensions. The vast majority of layout managers will make sure that the components don't overlap.
This means that you can't use a layout manager on your layered pane (if you want the components to overlap). Instead, you must manually set the size/location of components on each layer.
When you use a JLayeredPane the painting of components on each layer is managed so that the higher layer is painted last.
So your code might be changed to something like:
import java.awt.*;
import javax.swing.*;
public class Main
{
public static void main(String[] args)
{
JFrame window = new JFrame();
window.setSize(600, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Making the LayeredPane
JLayeredPane LP = new JLayeredPane();
//Making the ScrollPane and JTextArea
JTextArea textArea = new JTextArea(20,40);
textArea.setText("Test Text");
textArea.setSize( textArea.getPreferredSize() );
JScrollPane back = new JScrollPane( textArea);
back.setSize( textArea.getSize() );
JButton button = new JButton("test");
button.setBounds(200,200,50,20);
LP.add(back, new Integer(0));
LP.add(button, new Integer(1));
window.add(LP);
window.setVisible(true);
}
}
There is one layout manager in the JDK, the OverlayLayout, which is designed to stack components on top of one another. However, even this layout manager does not paint components properly when the components overlap. The trick when using this layout manager is to override the isOptimizedDrawing() method of the panel using the layout manager to make sure all components are repainted all the time. In this case, make sure the bottom panel is always painted before the top panel.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class Main2
{
public static void main(String[] args)
{
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPane = new JPanel()
{
#Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
};
contentPane.setLayout( new OverlayLayout(contentPane) );
JPanel top = new JPanel( new GridBagLayout() );
top.setBorder( new EmptyBorder(0, 0, 16, 16) );
top.setOpaque(false);
top.setAlignmentX(1.0f);
top.setAlignmentY(1.0f);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.anchor = GridBagConstraints.LAST_LINE_END;
JButton button = new JButton("test");
top.add(button, gbc);
contentPane.add(top);
//Making the ScrollPane and JTextArea
JTextArea textArea = new JTextArea(10,25);
textArea.setText("Test Text");
JScrollPane back = new JScrollPane( textArea);
back.setAlignmentX(1.0f);
back.setAlignmentY(1.0f);
contentPane.add(back);
window.add(contentPane);
window.pack();
window.setVisible(true);
}
}
The benefit of this approach is that the button will move as the frame is resized.
However, as a user I would still get annoyed with a button appearing over top of the text in my text area.
Edit:
If you really need components to overlap then I would suggest you could:
look at MadProgrammers solution to use a GridBagLayout. This approach gives far more control over the alignment of the components
check out the Overlap Layout which also provides more flexibility when aligning overlapping components
It should be noted that both above approaches may still require you to override the isOptimizedDrawEnabled(...) method to make sure components are painted properly. I am not aware of any layout manager the allows you to overlap components and works without this override.
Related
The problem I've been having for a while is that whenever I add a JButton to a JPanel, any other JButton that I had on was shifted in that direction.
Here is the first code:
//imports
import java.awt.*;
import javax.swing.*;
public class Example {
public static void main(String[] args) {
// Create the frame and panel and the Grid Bag Constraints
JFrame frame = new JFrame();
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
// Create ONE JButton
JButton button1 = new JButton("Button1");
// Set the frame's properties
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.getContentPane().add(panel, BorderLayout.NORTH);
frame.setVisible(true);
// Set Basic Grid Bag Constraints settings.
c.gridx = 0;
c.gridy = 0;
// Set the Insets
c.insets = new Insets(0, 0, 0, 0);
// Add the Grid Bag Constraints and button1 the panel
panel.add(button1, c);
}
}
Everything seems to working right?
Well if we add a second button:
//imports
import java.awt.*;
import javax.swing.*;
public class Example {
public static void main(String[] args) {
// Create the frame and panel and the Grid Bag Constraints
JFrame frame = new JFrame();
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
// Create TWO JButtons
JButton button1 = new JButton("Button1");
JButton button2 = new JButton("Button2");
// Set the frame's properties
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.getContentPane().add(panel, BorderLayout.NORTH);
frame.setVisible(true);
// Set Basic Grid Bag Constraints settings.
c.gridx = 0;
c.gridy = 0;
// Set the Insets
c.insets = new Insets(0, 0, 0, 0);
// Add the Grid Bag Constraints and button1 the panel
panel.add(button1, c);
// Set the Insets
c.insets = new Insets(500, 0, 0, 0);
// Add the Grid Bag Constraints and button2 the panel
panel.add(button2, c);
}
}
Then button1 moves down towards button2. Does anyone know why and/or a fix for it?
EDIT: What I am asking, is how do you add another button without moving the other buttons.
I don't know what your intent is. You only state it doesn't do what you expect. So I can't give you an exact solution.
In any case start by reading the section from the Swing tutorial on How to Use GridBagLayout for working examples using a GridBagLayout.
I see two issues:
You never change the gridx/y values. Both components are added to grid (0, 0) which is not how the GridBagLayout should work. Every component needs to be added to a different cell.
The Insets value of (500, ....) seems very big.
package swingtraining;
import javax.swing.*;
import java.awt.*;
import static java.awt.Color.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import static javafx.scene.paint.Color.TEAL;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class MyFrame extends JFrame{
public MyFrame(){
setSize(500,500);
setVisible(true);
setResizable(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
};
public static class MyPanel extends JPanel{
public MyPanel(){
setLayout(new GridBagLayout());
setBackground(BLACK);
setOpaque(true);
GridBagConstraints gbc1 = new GridBagConstraints();
GridBagConstraints gbc2 = new GridBagConstraints();
gbc1.insets = new Insets(200,0,0,200);
gbc1.ipadx = 100;
gbc1.ipady = 100;
gbc1.gridx = 1;
gbc1.gridy = 1;
gbc2.insets = new Insets(0,200,200,0);
gbc2.ipadx = 150;
gbc2.ipady = 10 ;
gbc2.gridx = 1;
gbc2.gridy = 1;
JTextArea jta1 = new JTextArea();
jta1.setLineWrap(true);
jta1.setWrapStyleWord(true);
JButton jb1 = new JButton("Have a banana!");
jb1.setToolTipText("Button prints Banana.");
ActionListener action1 = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println("\nBananas!");
}
};
jb1.addActionListener(action1);
add(jta1,gbc2);
add(jb1,gbc1);
};
};
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
MyFrame jf1 = new MyFrame();
MyPanel jp1 = new MyPanel();
jf1.add(jp1);
}
});
}
}
The code is just a simple JFrame, Panel, with a Button that prints bananas, and a JTextArea. When typed into without the LineWrap etc, it simply extends itself depending on which direction input is being applied. (pressing enter to go down in the area pulls it upwards/downwards, and typing into it pulls it to the left and to the right, making it bigger.) This makes sense, I didn't add a LineWrap or anything. However, adding those;
jta1.setLineWrap(true);
jta1.setWrapStyleWord(true);
I get the following result;
Just a picture to show what I'm talking about:
What I'm aiming for is a JTextArea that has limits, when those are exceeded creates a scrollbar, and doesn't change size at all.
Suggestions?
What I'm aiming for is a JTextArea that has limits, when those are exceeded creates a scrollbar, and doesn't change size at all.
JTextArea jta1 = new JTextArea();
Your text area doesn't have preferred size so it keeps growing. You need to create the text area with a preferred size. This is done by using:
JTextArea jta1 = new JTextArea(5, 30);
Now the layout manager can use the preferred size of the text area and the scrollpane will display the scrollbars when the preferred size of the text area is greater than the size of the scroll pane.
when those are exceeded creates a scrollbar, and doesn't change size at all.
And as pointed out by MadProgrammer if you want a scrollbar then you also need to actually add your text area to a JScrollPane and then add the scrollpane to the frame. So you would also need to use code like:
//add(jta1,gbc2);
add(new JScrollPane(jta1), gbc);
Read the section from the Swing tutorial on How to Use Text Areas for more information and examples.
I'm trying to create this basic GUI, but cannot get my panels to setup correctly.(Numbers are pixel sizes)
I've tried using this tutorial as a reference (http://www.youtube.com/watch?v=Kl3klve_rmQ) but, mine never works the same.
My code declares variables in the top of the class, then creates a constructor which add the components (panels, buttons, etc), then it calls the constructor in the main method.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FinalProject extends JPanel
{
private static final long serialVersionUID = 1L;
static JPanel nav;
static JPanel queue;
static JPanel menu;
GridBagConstraints gbc = new GridBagConstraints();
public FinalProject()
{
nav = new JPanel();
nav.setLayout(new GridBagLayout());
nav.setBackground(Color.RED);
gbc.gridy = 0;
gbc.gridx = 0;
gbc.gridheight = 1;
gbc.gridwidth = 1;
add(nav, gbc);
queue = new JPanel();
queue.setLayout(new GridBagLayout());
queue.setBackground(Color.GREEN);
gbc.gridy = 1;
gbc.gridx = 1;
gbc.gridheight = 1;
gbc.gridwidth = 1;
add(queue, gbc);
menu = new JPanel();
menu.setLayout(new GridBagLayout());
menu.setBackground(Color.BLUE);
gbc.gridy = 2;
gbc.gridx = 2;
gbc.gridheight = 1;
gbc.gridwidth = 1;
add(menu, gbc);
}
public static void main(String[] args)
{
FinalProject p = new FinalProject();
JFrame f = new JFrame();
f.add(nav);
f.add(queue);
f.add(menu);
f.setTitle("Subway");
f.setSize(800, 500);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.setResizable(false);
f.add(p);
}
}
How should I go about getting this layout right? Panels in Panels, Panels independent of each other, etc.?
No, don't use a GridBagLayout for this as you'll be adding more complexity than is actually needed. Myself, I try to avoid using this layout and all its potential pitfalls as much as possible, and usually you can get all you need by nesting JPanels, each using its own more simple layout. For instance here, all you need is a BorderLayout:
Place the top JPanel in the BorderLayout.NORTH position
Place the left JPanel in the BorderLayout.WEST position
Place the center JPanel in the BorderLayout.CENTER position.
That's it.
Again, please check out the Swing and the layout manager tutorials as the information is well presented there.
Edit
Note that nothing shows up on your JFrame because you're not adding your JPanel to the JFrame!
Edit 2
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.*;
public class SimpleLayout {
private static final Color GREEN = new Color(200, 255, 200);
private static final Color BLUE = new Color(200, 200, 255);
private static void createAndShowGui() {
JFrame frame = new JFrame("SimpleLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// note that a JFrame's contentPane uses BorderLayout by default
frame.getContentPane().add(new ColorPanel(Color.pink, 800, 80), BorderLayout.NORTH);
frame.getContentPane().add(new ColorPanel(GREEN, 300, 420), BorderLayout.WEST);
frame.getContentPane().add(new ColorPanel(BLUE, 500, 420), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ColorPanel extends JPanel {
private static final float FONT_POINTS = 24f;
private int prefW;
private int prefH;
public ColorPanel(Color color, int prefW, int prefH) {
setBackground(color);
this.prefW = prefW;
this.prefH = prefH;
// GBL can be useful for simply centering components
setLayout(new GridBagLayout());
String text = String.format("%d x %d", prefW, prefH);
JLabel label = new JLabel(text, SwingConstants.CENTER);
label.setFont(label.getFont().deriveFont(FONT_POINTS));
label.setForeground(Color.gray);
add(label);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
}
This displays as:
GridLayout I've never actually used but I'm not sure if it can do this.
BorderLayout can do this and is simpler to use than GridBagLayout.
GridBagLayout can do this.
In general GridBagLayout is one of the most flexible LayoutManagers and is well worth learning so I'd recommend using it here to get used to it on a simple case before you need it anyway for something more complex.
If you are familiar with HTML then think of GridBagLayout as working like HTML tables.
Very quickly - create the three panels and set the sizes/borders/whatever you need in them.
Add one in cell 0,0 with a colspan of 2.
Add one in cell 1,0
Add one in cell 1,1
After that you are done although you will probably want to specify resize/anchor/etc behaviour.
I'm trying to draw a gui like shown in the figure, but somehow I'm not able to place the objects in right place (I guess that the problem is with the layout) the textArea is suppose to go in the middle... but is not showing at all
package Chapter22Collections;
import javax.swing.*;
import java.awt.*;
public class Exercise226 extends JFrame {
private JButton jbSort;
private JButton jbReverse;
private JButton jbAdd;
private JButton jbShuffle;
private JLabel jlAddnum;
private JTextArea jTextDisplay;
private JTextField jTextAdd;
public Exercise226() {
jbSort = new JButton("Sort");
jbReverse = new JButton("Reverse");
jbShuffle = new JButton("Shuffle");
jbAdd = new JButton("Add");
jlAddnum = new JLabel("Add number here: ");
jTextDisplay = new JTextArea();
jTextAdd = new JTextField(8);
setLayout(new BorderLayout());
JPanel p1 = new JPanel(new GridLayout(1,3));
p1.add(jlAddnum);
p1.add(jTextAdd);
p1.add(jbAdd);
JPanel p2 = new JPanel(new GridLayout(1,3));
p2.add(jbSort);
p2.add(jbReverse);
p2.add(jbShuffle);
add(p1, BorderLayout.NORTH);
add(jTextDisplay, BorderLayout.CENTER);
add(p2, BorderLayout.SOUTH);
}
public static void main(String... args) {
Exercise226 gui = new Exercise226();
gui.setTitle("Numbers");
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.setSize(300, 200);
gui.setLocationRelativeTo(null);
gui.setVisible(true);
}
}
The JTextArea is actually where you expect it to be but has no outline border. It is usual to place the component in a JScrollPane which will give this effect:
add(new JScrollPane(jTextDisplay), BorderLayout.CENTER);
or simply
add(new JScrollPane(jTextDisplay));
To make the textArea re-size with the window, try BoxLayout. Box is "A lightweight container that uses a BoxLayout object as its layout manager."
Box p1 = new Box(BoxLayout.X_AXIS);
How could I add spacing/padding between the elements in the frame? So the text area is more visible and centered.
Borders and padding. E.G.
Compared with:
import javax.swing.*;
import java.awt.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
public class Exercise226 {
private JButton jbSort;
private JButton jbReverse;
private JButton jbAdd;
private JButton jbShuffle;
private JLabel jlAddnum;
private JTextArea jTextDisplay;
private JTextField jTextAdd;
private JPanel gui;
public Exercise226() {
gui = new JPanel(new BorderLayout(5,5));
jbSort = new JButton("Sort");
jbReverse = new JButton("Reverse");
jbShuffle = new JButton("Shuffle");
jbAdd = new JButton("Add");
jlAddnum = new JLabel("Add number here: ");
// set the size constraints using columns/rows
jTextDisplay = new JTextArea("Here I am!", 6,20);
jTextAdd = new JTextField(8);
JPanel p1 = new JPanel(new GridLayout(1,3,3,3));
p1.add(jlAddnum);
p1.add(jTextAdd);
p1.add(jbAdd);
JPanel p2 = new JPanel(new GridLayout(1,3,3,3));
p2.add(jbSort);
p2.add(jbReverse);
p2.add(jbShuffle);
JPanel textAreaContainer = new JPanel(new GridLayout());
textAreaContainer.add(new JScrollPane(jTextDisplay));
textAreaContainer.setBorder(new TitledBorder("Text Area Here"));
gui.add(p1, BorderLayout.PAGE_START);
gui.add(textAreaContainer, BorderLayout.CENTER);
gui.add(p2, BorderLayout.PAGE_END);
gui.setBorder(new EmptyBorder(4,4,4,4));
}
public Container getGui() {
return gui;
}
public static void main(String... args) {
JFrame f = new JFrame();
Exercise226 gui = new Exercise226();
f.setContentPane(gui.getGui());
f.setTitle("Numbers");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
}
This code:
Primarily provides 'white space' in the GUI using different constructors for the layouts that accept 2 int arguments for horizontal & vertical spacing.
Also adds 2 borders:
An empty border around the entire GUI to provide some spacing between it and the frame decorations.
A titled border around the text area, to make it very obvious.
Does implement a change for one unnecessary part of the original code. Instead of extending frame, it simply retains an instance of one.
Uses the JScrollPane container for the text area, as suggested by #Reimeus. It adds a nice beveled border of its own to an element that needs no scroll bars.
Creates a textAreaContainer specifically so that we can set a titled border to surround the scroll pane - without interfering with its existing border. It is possible to use a CompoundBorder for the scroll pane that consists of the existing border (scroll.getBorder()) & the titled border. However that gets complicated with buttons & other elements that might change borders on selection or action. So to set an 'outermost border' for a screen element (like the text area here) - I generally prefer to wrap the entire component in another container first.
Does not create and show the GUI on the EDT. Swing GUIs should be created and modified on the EDT. Left as an exercise for the user. See Concurrency in Swing for more details.
Old Code
The original code on this answer that provides the 'comparison GUI image' seen above. IT is closely based on the original code but with the text area wrapped in a scroll pane (and gaining a beveled border because of that) & given some text to display.
import javax.swing.*;
import java.awt.*;
public class Exercise226 extends JFrame {
private JButton jbSort;
private JButton jbReverse;
private JButton jbAdd;
private JButton jbShuffle;
private JLabel jlAddnum;
private JTextArea jTextDisplay;
private JTextField jTextAdd;
public Exercise226() {
jbSort = new JButton("Sort");
jbReverse = new JButton("Reverse");
jbShuffle = new JButton("Shuffle");
jbAdd = new JButton("Add");
jlAddnum = new JLabel("Add number here: ");
// set the size constraints using columns/rows
jTextDisplay = new JTextArea("Here I am!", 6,20);
jTextAdd = new JTextField(8);
setLayout(new BorderLayout());
JPanel p1 = new JPanel(new GridLayout(1,3));
p1.add(jlAddnum);
p1.add(jTextAdd);
p1.add(jbAdd);
JPanel p2 = new JPanel(new GridLayout(1,3));
p2.add(jbSort);
p2.add(jbReverse);
p2.add(jbShuffle);
add(p1, BorderLayout.NORTH);
add(new JScrollPane(jTextDisplay), BorderLayout.CENTER);
add(p2, BorderLayout.SOUTH);
}
public static void main(String... args) {
Exercise226 gui = new Exercise226();
gui.setTitle("Numbers");
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//gui.setSize(300, 200);
gui.pack();
//gui.setLocationRelativeTo(null);
gui.setLocationByPlatform(true);
gui.setVisible(true);
}
}
I'm prgramming a simple input diagram in Swing. I use boxLayout to create a simple GUI of user input. Problem is that creating a horizontal strut between the JPanel of all the labels and the JPanel of the JTextFields causes the whole panel to shift downwards (weird) this is the whole panel:
private JPanel secondCard() {
//main panel. set the boxlayout
secondCard = new JPanel();
secondCard.setLayout(new BoxLayout(secondCard,BoxLayout.Y_AXIS));
// create vertical strut for looks
secondCard.add(Box.createVerticalStrut(20));
// create title. center it.
JLabel title = new JLabel("Configure main network parameters ");
title.setAlignmentX(CENTER_ALIGNMENT);
secondCard.add(title);
// create vertical strut for looks
secondCard.add(Box.createVerticalStrut(20));
// create panel for the description labels
JPanel labelPanel = new JPanel();
labelPanel.setLayout(new BoxLayout(labelPanel,BoxLayout.Y_AXIS));
labelPanel.setAlignmentX(LEFT_ALIGNMENT);
JLabel inPut =new JLabel("number of inputs");
inPut.setAlignmentX(LEFT_ALIGNMENT);
labelPanel.add(inPut);
inPut =new JLabel("number of outputs");
inPut.setAlignmentX(LEFT_ALIGNMENT);
labelPanel.add(inPut);
inPut =new JLabel("number of layers");
inPut.setAlignmentX(LEFT_ALIGNMENT);
labelPanel.add(inPut);
JPanel textFieldPanel = new JPanel();
textFieldPanel.setLayout(new BoxLayout(textFieldPanel,BoxLayout.Y_AXIS));
textFieldPanel.setAlignmentX(LEFT_ALIGNMENT);
JTextField inputTextField = new JTextField();
inputTextField.setAlignmentX(LEFT_ALIGNMENT);
textFieldPanel.add(inputTextField);
inputTextField.setMinimumSize(new Dimension(0,0));
inputTextField = new JTextField();
inputTextField.setAlignmentX(LEFT_ALIGNMENT);
textFieldPanel.add(inputTextField);
inputTextField.setMinimumSize(new Dimension(0,0));
inputTextField = new JTextField();
inputTextField.setAlignmentX(LEFT_ALIGNMENT);
textFieldPanel.add(inputTextField);
inputTextField.setMinimumSize(new Dimension(0,0));
textFieldPanel.setMaximumSize(new Dimension(50, labelPanel.getMaximumSize().height));
JPanel inputPanel = new JPanel();
inputPanel.setLayout(new BoxLayout(inputPanel,BoxLayout.X_AXIS));
inputPanel.setAlignmentX(CENTER_ALIGNMENT);
inputPanel.add(labelPanel);
//this is the problem strut!! it causes inputPanel to shift downwards
inputPanel.add(Box.createHorizontalStrut(20));
inputPanel.add(textFieldPanel);
secondCard.add(inputPanel);
return secondCard;
}
without the strut it looks like:
With strut it looks like (I know I suck at picture editing):
You are adding a Box strut to a BoxLayout.
As the javadoc states, createHorizontalStrut(int width):
Creates an invisible, fixed-width component. In a horizontal box, you
typically use this method to force a certain amount of space between
two components. In a vertical box, you might use this method to force
the box to be at least the specified width. The invisible component
has no height unless excess space is available, in which case it takes
its share of available space, just like any other component that has
no maximum height.
As such, it is filling the height between your title JLabel and the bottom of the JPanel.
You might want to consider using Box.createRigidArea(new Dimension(20, height)) instead, where height could be specified or set to the height of labelPanel.
Or, you could reconsider the layout for your JPanel - take a look at the visual guide.
For future reference, if you cannot make sense of your Swing layout, try putting adding a coloured LineBorder to the JComponents you're unsure of. In this case, the Box struts are not JComponents but Components, so you'd have to put them into a JPanel, but this would at least have shown you what space each component was taking up in your top-level JPanel.
use Cardlayout for wizard logics
put JLabel(Configure ...., JLabel.CENTER) to the BorderLayout.NORTH
put JPanel with JButtons to the BorderLayout.SOUTH
put JPanel with SpringLayout, GridLayout, or GridBagLayout to the BorderLayout.CENTER
Top-Level Container have got implemened BorderLayout by default, then there no reason to re_define BorderLayout
above mentioned steps are called NestedLayout
alternative are put all JComponents by using GridBagLayout, SpringLayout or todays MigLayout to the one JPanel, but why bothering
Example of a nested layout, one using BorderLayout, FlowLayout (JPanel's default), and GridBagLayout:
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
public class LayoutFoo {
private static final String TITLE = "Configure Main Foobar Parameters";
private static final String[] LABEL_TEXTS = {
"Number of Spams", "Number of Frapzats", "Number of Zignuts"
};
private static final int TEXTFIELD_SIZE = 10;
private static final Insets WEST_INSETS = new Insets(5, 5, 5, 10);
private static final Insets EAST_INSETS = new Insets(5, 10, 5, 5);
private static final int EB_GAP = 5;
private Map<String, JTextField> textFieldMap = new HashMap<String, JTextField>();
public JPanel getConfigFooPanel() {
JPanel textFieldPanel = new JPanel(new GridBagLayout());
for (int i = 0; i < LABEL_TEXTS.length; i++) {
addTextAndField(textFieldPanel, LABEL_TEXTS[i], i);
}
int blVertGap = 20;
JPanel borderLayoutPanel = new JPanel(new BorderLayout(0, blVertGap));
borderLayoutPanel.setBorder(BorderFactory.createEmptyBorder(EB_GAP, EB_GAP,
EB_GAP, EB_GAP));
JLabel titleLabel = new JLabel(TITLE, JLabel.CENTER);
borderLayoutPanel.add(titleLabel, BorderLayout.PAGE_START);
borderLayoutPanel.add(textFieldPanel, BorderLayout.CENTER);
JPanel outerWrapperFlowPanel = new JPanel();
outerWrapperFlowPanel.add(borderLayoutPanel);
return outerWrapperFlowPanel;
}
public String getFieldText(String labelText) {
JTextField field = textFieldMap.get(labelText);
if (field == null) {
return ""; // ?? throw exception
} else {
return field.getText();
}
}
private void addTextAndField(JPanel panel, String text, int i) {
JLabel label = new JLabel(text, JLabel.LEFT);
JTextField textField = new JTextField(TEXTFIELD_SIZE);
textFieldMap.put(text, textField);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = i;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = WEST_INSETS;
panel.add(label, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.EAST;
gbc.insets = EAST_INSETS;
panel.add(textField, gbc);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("LayoutFoo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new LayoutFoo().getConfigFooPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}