Box Layout Won't Align Left - java

I have a custom panel (APanel). Two questions:
I need the label ("Top Label") and sub panel (bPanel) to align on the left side of the panel. bPanel is aligning on the left side but Top Label is being place in the middle. How do I fix?
How do I set the label and bPanel so they don't change in vertical size and maintain the same vertical distance between each other when the entire frame is expanded? In other words, how do I prevent the below from happening?
Code for APanel and bPanel are below. In case it matters, APanel is loaded as the CENTER component in a JFrame set to BorderLayout (not including the frame code here).
public class APanel extends JPanel {
private JLabel jLabel1;
private BPanel bPanel;
public APanel () {
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
jLabel1 = new JLabel("Top Label", JLabel.LEFT);
jLabel1.setHorizontalAlignment(SwingConstants.LEFT);
jLabel1.setBorder(BorderFactory.createLineBorder(Color.BLACK));
add(jLabel1);
bPanel = new BPanel();
bPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
add(bPanel);
this.setAlignmentX(LEFT_ALIGNMENT);
}
private class bPanel extends JPanel {
private JLabel sLabel;
private JLabel tLabel;
public bPanel() {
sLabel = new JLabel("Jeepers");
tLabel = new JLabel("Creepers");
setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.gridx = 0;
gc.gridy = 0;
gc.weightx = 1;
gc.weighty = 1;
gc.fill = GridBagConstraints.HORIZONTAL;
add(sLabel, gc);
gc.gridx = 1;
add(tLabel, gc);
}
}
}

I need the label ("Top Label") and sub panel (bPanel) to align on the left side of the panel.
jLabel1.setHorizontalAlignment(SwingConstants.LEFT);
Wrong method to control alignment with respect to the container. Read the JLabel API. That method is used to align the text within the bounds of the JLabel, in case the size of the label is greater than the preferred size of the text.
this.setAlignmentX(LEFT_ALIGNMENT);
Close, but that is not needed because if refers to the alignment of this entire panel in its parent container.
Instead, you need to set the "alignment" of all the components you are adding the this panel using the vertical BoxLayout. Each component may have a different default value. Some will default to "left" and some to "center".
Read the section from the Swing tutorial on Fixing Alignment Problems for more information.
How do I set the label and bPanel so they don't change in vertical size and maintain the same vertical distance between each other when the entire frame is expanded?
The BoxLayout will allow components to grow (up to their maximum size) when extra space is available. For a JLabel the maximum size is the same as its preferred size. For a JPanel the maximum size is the value of Integer.MAX_VALUE.
So you can override the getMaximumSize() method of you panel with code something like:
#Override
public Dimension getMaximumSize()
{
Dimension preferred = getPreferredSize();
Dimension maximum = super.getMaximumSize();
maximum.height = preferred.height;
return maximum;
}
Or another option is to wrap the panel in another panel that will respect the height. For example.
JPanel wrapper = new JPanel( new BorderLayout() );
wrapper.add(bPanel, BorderLayout.PAGE_START);
add(wrapper);
//add(bPanel);

Related

Creating a board game layout using JLayeredPane

I have an assignment which requires me to create the layout that you see in the image as part of the development of a game. I've never worked with Java for desktop applications before so i'm a complete noob when it comes to using the Swing & AWT libraries. The image suggests that we use a JLayeredPane as our root container and then add the rest on top of it. My issue is that i've tried starting with the background image and the gridLayout but i can't seem to make anything other than the background show up. The grid doesn't show up at all (no border line, no background of the cells) and any other component i've added to it has failed. Can somebody point me in the right direction here? I've read the docs & saw some example of various layouts,containers and components but i can't seem to make all of them work together.
Here's my code so far:
public class BoardView extends JFrame{
// Constructor
public BoardView() {
JFrame window = new JFrame("Sorry Game"); // create a new Jwindow instance
ImageIcon appIcon = new ImageIcon(getClass().getClassLoader().getResource("res/icon.png")); // create the icon for the app
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // when the 'X' button is clicked, the app stops
window.setSize(new Dimension(1000, 700)); // setting the size of the window
window.setResizable(false); // Window won't be resizable
window.setIconImage(appIcon.getImage()); // set the icon for the app
window.setLayout(new BorderLayout());
JLayeredPane layeredPane = new JLayeredPane();
JLabel background = new JLabel();
background.setSize(1000,700);
background.setIcon(new ImageIcon(getClass().getClassLoader().getResource("res/background.png"))); for the JLabel
layeredPane.add(background,0);
JPanel gridPanel = new JPanel(new GridLayout(16,16));
gridPanel.setSize(650,700);
layeredPane.add(gridPanel);
for(int i = 0; i < 256; i++) {
JLabel tile = new JLabel();
tile.setBackground(Color.red);
tile.setBorder(new LineBorder(Color.black));
gridPanel.add(tile);
}
JLabel logo = new JLabel();
logo.setIcon(new ImageIcon(getClass().getClassLoader().getResource("res/sorryImage.png")));
layeredPane.add(logo);
window.add(layeredPane);
window.setLocationRelativeTo(null); // centers the window to the screen
window.setVisible(true); // make the window visible
}
}
My thought process was that i could set the JFrame's layout to a BorderLayout so that i can brake the final layout down into two parts, the West one and the East one. The West one would contain the Grid and the various JLabels and Buttons and the East one would contain the rest of the components. I've tried using the BorderLayout.WEST & EAST parameters when adding components to the JFrame but none has worked or changed a single thing. I've also tried using an index for the depth when adding components to the JLayeredPane as per the docs but again nothing changes.
P.S. Please note that i'm not looking for someone to create the layout for me. I want someone to help me understand what i'm doing wrong and what the best way of creating such layouts is.
In order to initialize the cells of the grid that i want to have images in, don't i need to add them manually in those positions?
If you use a GridLayout every cell must have a component and the components must be added in sequential order. That is as components are added they will wrap automatically to the next row as required.
So even if you don't want an image in a cell you would need to add a dummy component, lets say a JLabel with no text/icon.
An easier approach might be to use a GridBagLayout. The GridBagLayout can be configured to "reserve" space for cells that don't have components. So you can add a component to a specific cell.
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
public class GridBagLayoutSparse extends JPanel
{
public GridBagLayoutSparse()
{
setBorder( new LineBorder(Color.RED) );
GridBagLayout gbl = new GridBagLayout();
setLayout( gbl );
/*
// Set up a grid with 5 rows and columns.
// The minimimum width of a column is 50 pixels
// and the minimum height of a row is 20 pixels.
int[] columns = new int[5];
Arrays.fill(columns, 50);
gbl.columnWidths = columns;
int[] rows = new int[5];
Arrays.fill(rows, 20);
gbl.rowHeights = rows;
*/
// Add components to the grid at top/left and bottom/right
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 0;
gbc.gridy = 0;
addLabel("Cell 0:0", gbc);
gbc.gridx = 3;
gbc.gridy = 4;
addLabel("Cell 3:4", gbc);
}
private void addLabel(String text, GridBagConstraints gbc)
{
JLabel label = new JLabel(text);
label.setBorder( new LineBorder(Color.BLUE) );
add(label, gbc);
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("GridBagLayoutSparse");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout( new GridBagLayout() );
frame.add(new GridBagLayoutSparse());
frame.setSize(300, 300);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
Run the code as is and the components will be grouped together in the center.
Uncomment the block comment and run again. The components will be positioned in the appropriate cell.

How can get components to stretch across an entire row using BoxLayout?

I'm looking at the How To Use BoxLayout documentation, which clearly says that
What if none of the components has a maximum width? In this case, if
all the components have identical X alignment, then all components are
made as wide as their container.
Let's assume we're adding a lot of JButton instances to a JPanel. If the maximum width of these buttons are none AND we invoke setAlignmentX(Component.LEFT_ALIGNMENT) on all of these buttons - then each of these buttons should stretch across its entire row. The documentation even illustrates this using the below picture.
I can't get this to work!
I've tried doing setMaximumSize(null) and setMaximumSize(new Dimension(-1,-1)) and setMaximumSize(new Dimension(0,0)) on the buttons but nothing gives me the described behaviour.
What excactly does the documentation mean when it says :
What if none of the components has a maximum width?
What is a maximum width of none?
The best I've been able to produce is the below. Reading the documentation I would expect that the buttons should be able to stretch across their entire rows. I know I can use other layout managers as well for this, but I would like to achieve this with BoxLayout (granted the documentation is right / I've understood the documentation right).
public class CustomList extends JPanel {
private final Box box = Box.createVerticalBox();
public CustomList() {
for (int i = 0; i < 10; i++) {
JButton b = new JButton("Button item" + i);
//b.setMaximumSize(new Dimension(0,0));
b.setAlignmentX(Component.LEFT_ALIGNMENT);
box.add(b);
}
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(box, BorderLayout.CENTER);
}
public static void main(String[] args) {
CustomList l = new CustomList();
l.setSize(200, 200);
l.setBackground(Color.red);
JFrame frame = new JFrame("Vertical Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(l, BorderLayout.CENTER);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
Your buttons actually have a maximum width.
What you can do is create JPanel objects with BorderLayout in your loop, add each button to each panel (to BorderLayout.CENTER, which is the default anyway).
BorderLayout.CENTER doesn't care about the maximum size of its child Component, so you end up with a JPanel whose whole content is filled by a JButton.
Since the JPanel itself has a huge default maximum size of new Dimension(Short.MAX_VALUE, Short.MAX_VALUE) (this is width=32767,height=32767 !!) which is the default maximum size of Component, you will get the expected result :
public CustomList() {
for (int i = 0; i < 10; i++) {
JPanel panel = new JPanel(new BorderLayout());
JButton b = new JButton("Button item" + i);
//b.setMaximumSize(new Dimension(0,0));
b.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(b);
box.add(panel);
}
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(box, BorderLayout.CENTER);
}

JScrollPane increases its size

I have a panel which is divided by two parts with BoxLayout.X_AXIS:
public TabsPanel() {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(createLeftPanel());
add(createRightPanel());
}
Each left and right panels have the following structure: an outer panel with BorderLayout, and an inner panel in BorderLayout.CENTER of the outer panel, which in its turn has BoxLayout.Y_AXIS and several components from top to bottom. The right panel has JTextArea with JScrollPane as one of its components:
protected JPanel createRightPanel() {
JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JTextArea label = createLabel();
JScrollPane scroll = new JScrollPane(label);
scroll.setMaximumSize(new Dimension(500, 200));
panel.add(Box.createRigidArea(new Dimension(0,106)));
panel.add(scroll);
JPanel panel_buttons = new JPanel();
panel_buttons.setLayout(new BoxLayout(panel_buttons, BoxLayout.LINE_AXIS));
panel_buttons.setAlignmentX(Component.CENTER_ALIGNMENT);
Font font_text = new Font("Georgia", Font.PLAIN, 20);
JButton[] buttons = new JButton[2];
buttons[0] = new JButton("Clear");
buttons[1] = new JButton("Exit");
for (int i = 0; i < buttons.length; i++) {
buttons[i].setMaximumSize(new Dimension(120, 40));
buttons[i].setFont(font_text);
panel_buttons.add(buttons[i]);
if (i == 0)
panel_buttons.add(Box.createRigidArea(new Dimension(40, 0)));
buttons[i].addActionListener(new TextActionListener(label));
}
panel.add(Box.createRigidArea(new Dimension(0,20)));
panel.add(panel_buttons);
pane.add(panel, BorderLayout.CENTER);
return pane;
}
When text goes beyond the borders, scroll bars appear and I can move them and read the text. Looks like everything is ok, but when I either click any place outside the scroll pane or even just move the pointer, the scroll pane moves to the left and grows down. It doesn't change its width, but it shifts to the left because the area between it and the right panel's borders increases. Accordingly, size of the left panel shrinks. When I clear the text area and again either click or move the pointer, it is back to its normal size.
What is the reason its height grows and its left and right margins increase? What am I doing wrong?
UPDATE. I've found the problem. The thing is that I didn't create JTextArea correctly. I initialized it without parameters:
JTextArea text = new JTextArea("Some initial text");
Now I have rewritten:
JTextArea text = new JTextArea(5,10);
It is now shifted to the left by about 5 mm and do not changes its height. Still not perfect, but looks like I am on the right track.
Thank you everybody for your help!
BoxLayout accepting Min, Max and PreferredSize override those methods for JPanel
use JSPlitPane, there you can to hide Divider
2 steps to correct:
Set the size of the JTextArea: JTextArea text = new JTextArea(row, col);
Still shifts to the left by the size of the vertical bar:
either add ChangeListener to adjust the size of the JScrollPane
scroll.getViewport().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (scroll.getVerticalScrollBar().isVisible())
scroll.setPreferredSize(480, 200);
}
}
});
or add scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

How to set the component size with GridLayout? Is there a better way?

I am working on a larger GUI with Java and I am becoming angry on Layout Managers.
I have a "Settings-Panel" with a variable number of JComponents in it (Labels, Buttons, JSpinners, JSliders,...). I just want the following:
JLabel <-> JComponent
JLabel <-> JComponent
JLabel <-> JComponent
...
My Panel has a size of 500px, so that there is enough space for a lot of components. Unfortunately the GridLayout always stretches the size of the Components to the whole Panel, even if I set a MaximumSize for every component. It looks stupid if there are only two buttons each with a height of 250px.
I tried FlowLayout, but I cannot figure out a way to make new lines properly. I tried BoxLayout.Y_AXIS, but the Components are always centered, and Label and Component are not in the same line.
Does anybody know a good and short way with LayoutManagers to handle this properly?
An alternative to other layouts, might be to put your panel with the GridLayout, inside another panel that is a FlowLayout. That way your spacing will be intact but will not expand across the entire available space.
Don't use GridLayout for something it wasn't meant to do. It sounds to me like GridBagLayout would be a better fit for you, either that or MigLayout (though you'll have to download that first since it's not part of standard Java). Either that or combine layout managers such as BoxLayout for the lines and GridLayout to hold all the rows.
For example, using GridBagLayout:
import java.awt.*;
import javax.swing.*;
public class LayoutEg1 extends JPanel{
private static final int ROWS = 10;
public LayoutEg1() {
setLayout(new GridBagLayout());
for (int i = 0; i < ROWS; i++) {
GridBagConstraints gbc = makeGbc(0, i);
JLabel label = new JLabel("Row Label " + (i + 1));
add(label, gbc);
JPanel panel = new JPanel();
panel.add(new JCheckBox("check box"));
panel.add(new JTextField(10));
panel.add(new JButton("Button"));
panel.setBorder(BorderFactory.createEtchedBorder());
gbc = makeGbc(1, i);
add(panel, gbc);
}
}
private GridBagConstraints makeGbc(int x, int y) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.gridx = x;
gbc.gridy = y;
gbc.weightx = x;
gbc.weighty = 1.0;
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = (x == 0) ? GridBagConstraints.LINE_START : GridBagConstraints.LINE_END;
gbc.fill = GridBagConstraints.HORIZONTAL;
return gbc;
}
private static void createAndShowUI() {
JFrame frame = new JFrame("Layout Eg1");
frame.getContentPane().add(new LayoutEg1());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
For more complex layouts I often used GridBagLayout, which is more complex, but that's the price. Today, I would probably check out MiGLayout.
In my project I managed to use GridLayout and results are very stable, with no flickering and with a perfectly working vertical scrollbar.
First I created a JPanel for the settings; in my case it is a grid with a row for each parameter and two columns: left column is for labels and right column is for components. I believe your case is similar.
JPanel yourSettingsPanel = new JPanel();
yourSettingsPanel.setLayout(new GridLayout(numberOfParams, 2));
I then populate this panel by iterating on my parameters and alternating between adding a JLabel and adding a component.
for (int i = 0; i < numberOfParams; ++i) {
yourSettingsPanel.add(labels[i]);
yourSettingsPanel.add(components[i]);
}
To prevent yourSettingsPanel from extending to the entire container I first wrap it in the north region of a dummy panel, that I called northOnlyPanel.
JPanel northOnlyPanel = new JPanel();
northOnlyPanel.setLayout(new BorderLayout());
northOnlyPanel.add(yourSettingsPanel, BorderLayout.NORTH);
Finally I wrap the northOnlyPanel in a JScrollPane, which should behave nicely pretty much anywhere.
JScrollPane scroll = new JScrollPane(northOnlyPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
Most likely you want to display this JScrollPane extended inside a JFrame; you can add it to a BorderLayout JFrame, in the CENTER region:
window.add(scroll, BorderLayout.CENTER);
In my case I put it on the left column of a GridLayout(1, 2) panel, and I use the right column to display contextual help for each parameter.
JTextArea help = new JTextArea();
help.setLineWrap(true);
help.setWrapStyleWord(true);
help.setEditable(false);
JPanel split = new JPanel();
split.setLayout(new GridLayout(1, 2));
split.add(scroll);
split.add(help);
You need to try one of the following:
GridBagLayout
MigLayout
SpringLayout
They offer many more features and will be easier to get what you are looking for.
I used WrapFlowLayout instead
JPanel yourPanel = new JPanel(new WrapFlowLayout(10, 10);

GridBagLayout padding

I am using a GridBagLayout to (currently) display two rows. I am aware this layout is overkill for this task, but am trying to learn how to use it. The problem is that I have added the two panels to the two separate rows and there is a huge gap around the content (see image and code below):
alt text http://www.imagechicken.com/uploads/1264533379009864500.png
Image background;
public Table(){
super();
ImageIcon ii = new ImageIcon(this.getClass().getResource("pokerTableV2.png"));
background = ii.getImage();
setSize(Constants.FRAME_WIDTH, Constants.TABLE_HEIGHT);
setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
JButton button = new JButton("hello world");
JPanel panel1 = new JPanel();
panel1.setPreferredSize(new Dimension(800,100));
panel1.add(button, BorderLayout.CENTER);
panel1.setBackground(Color.yellow);
add(panel1, constraints);
constraints.gridx = 0;
constraints.gridy = 1;
JPanel middlePanel = new JPanel();
middlePanel.setPreferredSize(new Dimension(800,350));
middlePanel.add(button, BorderLayout.CENTER);
middlePanel.setBackground(Color.blue);
add(middlePanel, constraints);
}
Use
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1d;
constraints.weighty = 1d;
JavaDoc for weightx/weighty says:
Specifies how to distribute extra horizontal/vertical space.
JavaDoc for fill:
This field is used when the component's display area is larger
than the component's requested size. It determines whether to
resize the component, and if so, how.
Unfortunately, with GridBagLayout, if the contents do not fill the entire container that it is in, it will automatically center all its contents within its container. That is why you are getting a really large gap.
There are essentially two ways to fix this:
The hard way: Fiddle with the GridBagConstraints. I had limited success with this when trying to avoid the centre-ing behaviour.
The easy way: Put the GridBagLayout inside of a FlowLayout, and then set the alignment of the FlowLayout to top-left, or whatever you wish.
I have asked, and answered, this question myself sometime last week.
So in your case you are adding your panel1 and middlePanel directly to the JFrame (?), with a GridBagLayout
JFrame (GridBagLayout)
- panel1
- middlePanel
I would suggest this alternative structure instead, to get rid of all the extra space (and centre alignment as well, if you like).
JFrame (FlowLayout)
- JPanel (GridBagLayout)
- panel1
- middlePanel
HTH

Categories