I have a little problem with size of a JList:
There is a JFrame with three JLists(each in JScrollPane). You can add an item to list by click on it. After program start all JLists have the same width. But after populate any list is his width reduced and created space is added to empty list equally.
I want to stop this resizing. I hope that the code below will describe my problem better.
package test;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class Test extends JFrame {
public Test() {
GroupLayout layout = new GroupLayout(this.getContentPane());
this.setLayout(layout);
JList listA = new JList(new DefaultListModel<String>());
JList listB = new JList(new DefaultListModel<String>());
JList listC = new JList(new DefaultListModel<String>());
MouseListener listener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
((DefaultListModel) ((JList) e.getSource()).getModel()).addElement("some text");
revalidate();
}
};
listA.addMouseListener(listener);
JScrollPane paneA = new JScrollPane(listA);
listB.addMouseListener(listener);
JScrollPane paneB = new JScrollPane(listB);
listC.addMouseListener(listener);
JScrollPane paneC = new JScrollPane(listC);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(paneA).addComponent(paneB).addComponent(paneC));
layout.setVerticalGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER).addComponent(paneA).addComponent(paneB).addComponent(paneC)));
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Test test = new Test();
test.setVisible(true);
}
});
}
}
To make your components to be of constant size you will need to use addComponent(Component component, int min, int pref, int max). So just replace your code with this
layout.setHorizontalGroup(layout.createSequentialGroup()
.addComponent(paneA, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE)
.addComponent(paneB, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE)
.addComponent(paneC, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE));
instead of this
layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(paneA).addComponent(paneB).addComponent(paneC));
Here I have used properties of GroupLayout and a constant width 200 to make it stable.
The JLists are likely been resized because of the size of the values been rendered are smaller than they were when they were first created. They are then be re-laid out by the layout manager to meet these new requirements.
There is a complicated amount of work going on between the JScrollPane, the JViewport, the JList and the JLists ListCellRenderer in which you don't want to get involved.
Instead, you want to provide "hints" to the JLists about how you want them to be sized.
For example...
You could use JList#setPrototypeCellValue to provide the JList with informaiton about the maximum size of the value that would be added to the list in the future. This is especially handy when you have a large number of values to add, as the JList won't need to check each one to determine the height of each row and the width required to accommodate the values.
The drawback is, you will need to know the maximum width of any of the values are likely to add...
Alternatively, you could use a LayoutManager which gives you more control over how components should be positioned and changed, such as GridBagLayout or even MigLayout
You should also check out Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Here is one way to fix the issue.
JScrollPane paneA = new JScrollPane(listA);
paneA.setPreferredSize(new Dimension(paneA.getPreferredSize().height, 300));
listB.addMouseListener(listener);
JScrollPane paneB = new JScrollPane(listB);
paneB.setPreferredSize(new Dimension(paneB.getPreferredSize().height, 300));
listC.addMouseListener(listener);
JScrollPane paneC = new JScrollPane(listC);
paneC.setPreferredSize(new Dimension(paneC.getPreferredSize().height, 300));
I am not familiar with the GroupLayout manager. The following document might provide a way to control the layout further. http://docs.oracle.com/javase/tutorial/uiswing/layout/group.html (See Component Size and Resizability)
Related
This has probably been answered here, but my searches drew up a blank, perhaps I am looking for the wrong thing.
How can I stop a JTextArea and a JTextField from stretching? Does swing provide a panel spacer of some sort which I can add that will be stretched before any other elements will?
I have a JPanel which fills a JFrame with a set size. It uses a BoxLayout set to PAGE_AXIS. If I add a JTextArea with size 3,100 it ignores these row/columns sizes and fills all the available space.
Similarly I have a JPanel with a GridLayout. If I add a JTextField, rather than be the size in columns that I specify, it fills the entire space in the grid.
Edit: The drawing below shows what I would like (at the top) and what I get.
Few words directly from documentation on How to Use GridLayout
A GridLayout object places components in a grid of cells. Each component takes all the available space within its cell, and each cell is exactly the same size. If the GridLayoutDemo window is resized, the GridLayout object changes the cell size so that the cells are as large as possible, given the space available to the container.
GridLayout doesn't consider the size of the component. It tries to fit the component in available space in rows and columns.
Try to avoid setting size explicitly. There is a very good post about Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? that can help you to design it in a better way.
There are lots of layout managers that can fit as per your requirements. Please have a look at How to Use Various Layout Managers
You can use GridBagLayout instead of GridLayout to divide the components in rows and columns just like table layout that gives you more control on grid width, height, margin, padding, column, rows etc.
You cannot do serious layouts withe GridLayout or BoxLayout. Forget
about them.
The first question is: Are you sure you don't want the JTextArea and JTextField to grow? It is a standard in UI that these components are growing and shrinking; the first one in both directions and the second one horizontally.
Anyway, I post solutions for GroupLayout and MigLayout, which are
the managers I highly recommend.
a) Solution with GroupLayout
Each component has three sizes: minimum, preferred, and maximum. These
are set to some sensible defaults for each component. This is why the
JTextField tends to grow horizontally; its maximum value is set to a
very large number.
package com.zetcode;
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class GroupLayoutStretchingEx extends JFrame {
public GroupLayoutStretchingEx() {
initUI();
}
private void initUI() {
JTextArea area = new JTextArea(12, 20);
area.setBorder(BorderFactory.createEtchedBorder());
JLabel input = new JLabel("Input");
JTextField textField = new JTextField(15);
createLayout(area, input, textField);
setTitle("Stretching");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
Container pane = getContentPane();
GroupLayout gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createParallelGroup(GroupLayout.Alignment.TRAILING)
.addComponent(arg[0], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addGroup(gl.createSequentialGroup()
.addComponent(arg[1])
.addComponent(arg[2], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(arg[0], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addGroup(gl.createParallelGroup()
.addComponent(arg[1])
.addComponent(arg[2], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
GroupLayoutStretchingEx ex = new GroupLayoutStretchingEx();
ex.setVisible(true);
});
}
}
In GroupLayout, you need to set the correct values for the maximum
component size in the addComponent() method.
JTextArea area = new JTextArea(12, 20);
By setting the number of rows and columns for the JTextArea, we
set a preffered size for this component. This value is later used
by the layout manager in its calculations.
b) Solution with MigLayout
In MigLayout, we control the resizement of the components with various
constraints, such as fill, grow, or push. By not using them, the
components do not grow.
package com.zetcode;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
public class MigLayoutStretchingEx extends JFrame {
public MigLayoutStretchingEx() {
initUI();
}
private void initUI() {
JTextArea area = new JTextArea(12, 20);
area.setBorder(BorderFactory.createEtchedBorder());
JLabel input = new JLabel("Input");
JTextField textField = new JTextField(15);
createLayout(area, input, textField);
setTitle("MigLayout example");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
setLayout(new MigLayout());
add(arg[0], "wrap");
add(arg[1], "split 2");
add(arg[2], "growx");
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
MigLayoutStretchingEx ex = new MigLayoutStretchingEx();
ex.setVisible(true);
});
}
}
And here is the screenshot:
In My code I just set the number of columns
JTextField data = new JTextField();
data.setColumns(30);
I building a quite complicated GUI using Swing, and have come across a alignment issue that I need help with. The code below is a simplified version of the situation I have, in order to make the problem clearer.
Basically I'm wondering it there is a possibility to align a JComponent with a JComponent that has a different parent component? I am aware that I could insert invisible Box components in order to solve this, but I don't think this is a good solution for several reasons.
In the code below, I want the Child to be the same size and vertically aligned with the Niece (basically have Child in the same position as Niece, just in a row below it). Underneath Sister should just be empty space.
Is there someway to get the left alignment of Niece relative to the main content pane, and set the left alignment of Child to be the same?
Code:
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import java.awt.Container;
import javax.swing.JComponent;
import javax.swing.GroupLayout;
import javax.swing.JTextField;
import java.awt.Dimension;
public class Test extends JFrame {
public static void main(String args[]) {
new Test();
}
public Test() {
JTabbedPane tabPane = new JTabbedPane();
JTextField edPane1 = new JTextField("Sister");
JTextField edPane2 = new JTextField("Child");
JTextField tabEdPane = new JTextField("Niece");
edPane1.setPreferredSize(new Dimension(300, 100));
edPane2.setPreferredSize(new Dimension(300, 100));
tabEdPane.setPreferredSize(new Dimension(300, 100));
tabPane.addTab("Tab", tabEdPane);
Container contPane = getContentPane();
GroupLayout layout = new GroupLayout(contPane);
setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(
layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addComponent(edPane1)
.addComponent(tabPane))
.addComponent(edPane2)
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(edPane1)
.addComponent(tabPane))
.addComponent(edPane2)
);
setTitle("Title");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
Any suggestions of a solution might be helpful!
EDIT: This is what it should look like:
Here I want to add buttons as per 4-3-3 formation in football but i am getting 3-3-3. how can i put 4 buttons in one row??
I have been referral to this site: https://weblogs.java.net/blog/tpavek/archive/2006/02/getting_to_know_2.html
Code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import static javax.swing.GroupLayout.Alignment.*;
class Abc extends JFrame
{
JButton b[];
Abc()
{
b=new JButton[11];
JPanel jp=new JPanel();
for(int i=0;i<b.length;i++)
{
b[i]=new JButton();
}
GroupLayout layout=new GroupLayout(jp);
jp.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(LEADING)
.addComponent(b[0])
.addComponent(b[1])
.addGroup(layout.createSequentialGroup())
.addComponent(b[2])
.addComponent(b[3]))
.addGroup(layout.createParallelGroup(LEADING)
.addComponent(b[4])
.addComponent(b[5])
.addGroup(layout.createSequentialGroup())
.addComponent(b[6]))
.addComponent(b[7])
.addGroup(layout.createParallelGroup(LEADING)
.addComponent(b[8])
.addComponent(b[9])
.addGroup(layout.createSequentialGroup())
.addComponent(b[10]))
);
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(b[0])
.addComponent(b[4])
.addComponent(b[8]))
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(b[1])
.addComponent(b[5])
.addComponent(b[9]))
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(b[2])
.addComponent(b[3])
.addComponent(b[6])
.addComponent(b[10]))
.addComponent(b[7])
);
setTitle("kuvh b");
setSize(1000,1000);
for(int i=0;i<11;i++)
{
add(b[i]);
}
add(jp);
pack();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(
"javax.swing.plaf.metal.MetalLookAndFeel");
// "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
//UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
new Abc().setVisible(true);
}
});
}
}
I want to make a structure of 4-3-3 formation with this. Please help me
The code is resulting in 3-3-3 formation. there are 3 buttons on third row but i want 4 how can i do this please help
See the output:
[1]:
http://imgur.com/jxADf2t
i hope i will find my solution as far as possible
I've found two ways to do something like you want; centering the buttons turns out to be something that Swing does not make as easy as it might, but then centering components is probably not as common as other alignments.
You can center components in a FlowLayout; the disadvantage in a FlowLayout is that, if the user shrinks the window to the point the components no longer fit, the layout wraps the components. This is very useful for some things, but not for your football players. I've wrapped my example in a scrollpane so this won't happen.
The other way to center components is with GroupLayout, but GroupLayout is not good for the overall layout you are trying to achieve. GroupLayout is intended for use where you have overall rows and columns in which to line things up, and your four lines of football players are not lined up that way vertically, only horizontally. But you can use the centering characteristic of GroupLayout to do the horizontal centering, and make a separate GroupLayout for each line.
My example uses FlowLayout for the first line, and GroupLayout for the second, just to show how it could be done. I did not address the problem of the gap that appears between the players' lines when the window is made large enough. Especially for examples, I do not use the style of tacking method invocations onto other method invocations and constructors; I think the deeply nested parentheses and non-straightforward logic of this style makes it more difficult to figure out (or keep track of) what's going on.
You can use GridBagLayout to center things, also, but I don't use it at all if anything else will do what I need.
I hope this answers your question.
package grouplayout;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main2 extends JFrame
{
public static void main(String ... arguments)
{
Main2 main2 = new Main2();
main2.createUI();
main2.setVisible(true);
}
public void createUI()
{
JPanel wingPanel = new JPanel();
FlowLayout flowLayout = new FlowLayout();
flowLayout.setHgap(35);
wingPanel.setLayout(flowLayout);
JButton btnone = new JButton("Lwing");
JButton btntwo = new JButton("center");
JButton btnthr = new JButton("Rwing");
wingPanel.add(btnone);
wingPanel.add(btntwo);
wingPanel.add(btnthr);
// -------------------------------------------
JButton mid1 = new JButton("mid1");
JButton mid2 = new JButton("mid2");
JButton mid3 = new JButton("mid3");
JButton mid4 = new JButton("mid4");
JPanel midfieldPanel = new JPanel();
GroupLayout groupLayout = new GroupLayout(midfieldPanel);
GroupLayout.SequentialGroup horizontalGroup = groupLayout.createSequentialGroup();
groupLayout.setHorizontalGroup(horizontalGroup);
horizontalGroup.addComponent(mid1);
horizontalGroup.addComponent(mid2);
horizontalGroup.addComponent(mid3);
horizontalGroup.addComponent(mid4);
GroupLayout.SequentialGroup verticalGroup = groupLayout.createSequentialGroup();
groupLayout.setVerticalGroup(verticalGroup);
GroupLayout.ParallelGroup midButtonGroup = groupLayout.createParallelGroup(GroupLayout.Alignment.CENTER);
midButtonGroup.addComponent(mid1);
midButtonGroup.addComponent(mid2);
midButtonGroup.addComponent(mid3);
midButtonGroup.addComponent(mid4);
verticalGroup.addGroup(midButtonGroup);
JPanel teamPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(teamPanel, BoxLayout.PAGE_AXIS);
teamPanel.setLayout(boxLayout);
teamPanel.add(wingPanel);
teamPanel.add(midfieldPanel);
JScrollPane scrollPane = new JScrollPane(teamPanel);
getContentPane().add(scrollPane);
pack();
}
}
EDIT: as requested, the below does the same thing with only GroupLayout.
There is no interaction between the two groups, because GroupLayout aligns things in columns, and your players are not in columns.
And yes, I suppose it is difficult -- GroupLayout, as I understand it, was really intended for use by GUI builder tools, not really for building UIs by hand. I personally have a supporter class or two that allows GroupLayout UIs to be built with slightly simpler logic. But in any event, I think you need to understand the building blocks:
GroupLayout allows - and requires - that you place each component in both horizontal and vertical row/column position independently; this is useful since so many UIs require rows and columns of mixed components and variable extra components.
A sequential group of components in dimension X is arranged sequentially in dimension X; a parallel group in dimension X is also arranged sequentially, but perpendicular to dimension X.
The layout maintains preferred sizes of components; row width and column height are set at the maximum preferred size of the constituent components.
The overall GroupLayout object has one vertical and one horizontal grouping; within that, sequential and parallel groups are created to create the overall layout desired.
I know the examples in the tutorial(s) I've read do not create separate variables to hold the internal sequential and parallel groups, preferring to use forms like new X().addComponent().addGroup() etc. But I think that makes it harder to understand what the code is actually doing, not easier; and the nested parentheses become their own maintenance problem. So I think this is a better way to do things, especially for those just getting started with this layout.
package grouplayout;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main3 extends JFrame
{
public static void main(String ... arguments)
{
Main3 main2 = new Main3();
main2.createUI();
main2.setVisible(true);
}
public void createUI()
{
JButton btnone = new JButton("Lwing");
JButton btntwo = new JButton("center");
JButton btnthr = new JButton("Rwing");
JPanel wingPanel = new JPanel();
GroupLayout wingGroupLayout = new GroupLayout(wingPanel);
GroupLayout.SequentialGroup wingHorizontalGroup = wingGroupLayout.createSequentialGroup();
wingGroupLayout.setHorizontalGroup(wingHorizontalGroup);
wingHorizontalGroup.addComponent(btnone);
wingHorizontalGroup.addComponent(btntwo);
wingHorizontalGroup.addComponent(btnthr);
GroupLayout.SequentialGroup wingVerticalGroup = wingGroupLayout.createSequentialGroup();
wingGroupLayout.setVerticalGroup(wingVerticalGroup);
GroupLayout.ParallelGroup wingButtonGroup = wingGroupLayout.createParallelGroup();
wingButtonGroup.addComponent(btnone);
wingButtonGroup.addComponent(btntwo);
wingButtonGroup.addComponent(btnthr);
wingVerticalGroup.addGroup(wingButtonGroup);
// -------------------------------------------
JButton mid1 = new JButton("mid1");
JButton mid2 = new JButton("mid2");
JButton mid3 = new JButton("mid3");
JButton mid4 = new JButton("mid4");
JPanel midfieldPanel = new JPanel();
GroupLayout groupLayout = new GroupLayout(midfieldPanel);
GroupLayout.SequentialGroup horizontalGroup = groupLayout.createSequentialGroup();
groupLayout.setHorizontalGroup(horizontalGroup);
horizontalGroup.addComponent(mid1);
horizontalGroup.addComponent(mid2);
horizontalGroup.addComponent(mid3);
horizontalGroup.addComponent(mid4);
GroupLayout.SequentialGroup verticalGroup = groupLayout.createSequentialGroup();
groupLayout.setVerticalGroup(verticalGroup);
GroupLayout.ParallelGroup midButtonGroup = groupLayout.createParallelGroup(GroupLayout.Alignment.CENTER);
midButtonGroup.addComponent(mid1);
midButtonGroup.addComponent(mid2);
midButtonGroup.addComponent(mid3);
midButtonGroup.addComponent(mid4);
verticalGroup.addGroup(midButtonGroup);
JPanel teamPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(teamPanel, BoxLayout.PAGE_AXIS);
teamPanel.setLayout(boxLayout);
teamPanel.add(wingPanel);
teamPanel.add(midfieldPanel);
JScrollPane scrollPane = new JScrollPane(teamPanel);
getContentPane().add(scrollPane);
pack();
}
}
I'm trying to get two components,
frame.getContentPane().setLayout(new MigLayout("", "[53px,grow][57px][grow]", "[23px][][][]"));
JTextPane itemTitle = new JTextPane();
frame.getContentPane().add(itemTitle,"cell 0 4,alignx left,aligny top");
itemTitle.setVisible(true);
itemTitle.setMinimumSize(new Dimension(50, 50));
List choices = new List();
frame.add(choices, "cell 0 4,alignx left,aligny top");
choices.setVisible(true);
to be in the same place, but all the happens is this:
The two components highlighted at itemTitle and choices.
My aim is to have the buttons above set one "setVisible" to true and the other to false. They would never both be true. How can I get two components in one cell at the same time? It also puts my above buttons out of place and I'm not too sure why. I put above the important code referring to the two components, I could put the full GUI code if you requested.
I found this: Fill Entire Cell With Two Components Using MigLayout However it is over years old and to be honest, I don't understand the solution.
I'm sort of learning as I go, I've never used MigLayout before. Should I be using a different layout?
Thanks for any help
to be in the same place... My aim is to have the buttons above set one "setVisible" to true and the other to false. They would never both be true
Then you should be using a JPanel that contains those two components. Then you use a Card Layout on that panel and use the CardLayout to determine which component is visible at any given time. This panel can be added to the panel using the MigLayout just like any other component.
List choices = new List();
Looks to me like you are using an AWT component. You should be using JList for a Swing application.
Should I be using a different layout?
No, stick with MigLayout. You have chosen the right layout manager.
I advice you to invest some time into learning this manager; create couple
of smaller practical examples, learn the numerous constraints that this
manager provides.
It also puts my above buttons out of place and I'm not too sure why.
MigLayout is a grid-based layout manager. (Its most important mode is.)
The gap between the New and Open buttons is created because the
highlighted components and the New button form a column. The column width
is determined by the width of the largest cell. To bypass this, we can
use the split constraint. (Often used in combination with span constraint.)
In my example I use this technique to center two buttons above the
currently visible label.
If we are not sure about something in the layout, we can use the
debug layout constraint which paints the lines of the grid and bounds
of the components.
frame.getContentPane().setLayout(new MigLayout("", "[53px,grow][57px][grow]", "[23px][][][]"));
Do not set bounds in pixels. You are not utilizing of one of the
most important advantages of this manager -- independence of resolution and
DPI. Bounds in pixels are not portable. A 3px gap between buttons looks OK on a smaller screen but is not acceptable on a larger one.
MigLayout provides several options to choose from including related and unrelated gaps, logical pixels, points, or centimeters.
itemTitle.setMinimumSize(new Dimension(50, 50));
Setting a minimum size of a component is usually not necessary. But if we need to
do it, we have the wmin and wmax constraints. This should be done by the
layout manager, not by code outside of it. The set(Minimum|Maximum|Preferred)size
methods should be avoided. (With poorer managers, one cannot do without them, however.)
And again, setting dimensions in pixels is not optimal.
Now we get to the solution. MigLayout has hidemode constraint to deal
with your requirement. There are four hide modes. I assume that we need
the hidemode 3, in which all invisible components do not participate in the
layout.
package com.zetcode;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public class HidingComponentsEx extends JFrame {
private ArrayList<JLabel> lbls;
private int itemVisible = 0;
public HidingComponentsEx() {
initUI();
}
private void initUI() {
createLabels();
JButton prevBtn = new JButton("Previous");
prevBtn.addActionListener(new PrevAction());
JButton nextBtn = new JButton("Next");
nextBtn.addActionListener(new NextAction());
JPanel pnl = new JPanel(new MigLayout("ins dialog"));
pnl.add(prevBtn, "split 2, center");
pnl.add(nextBtn, "wrap");
for (JLabel lbl : lbls) {
pnl.add(lbl, "cell 0 1, w 250lp, h 100lp, hidemode 3");
}
add(pnl);
pack();
setTitle("MigLayout example");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void createLabels() {
lbls = new ArrayList<>();
lbls.add(createLabel("North Sea"));
lbls.add(createLabel("Ionian Sea"));
lbls.add(createLabel("Norwegian Sea"));
lbls.add(createLabel("Bering Sea"));
lbls.add(createLabel("Dead Sea"));
lbls.get(itemVisible).setVisible(true);
}
private class NextAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
lbls.get(itemVisible).setVisible(false);
itemVisible++;
if (itemVisible > 4) {
itemVisible = 0;
}
lbls.get(itemVisible).setVisible(true);
}
}
private class PrevAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
lbls.get(itemVisible).setVisible(false);
itemVisible--;
if (itemVisible < 0) {
itemVisible = 4;
}
lbls.get(itemVisible).setVisible(true);
}
}
private JLabel createLabel(String text) {
JLabel lbl = new JLabel(text, JLabel.CENTER);
lbl.setVisible(false);
lbl.setBorder(BorderFactory.createEtchedBorder());
return lbl;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
HidingComponentsEx ex = new HidingComponentsEx();
ex.setVisible(true);
}
});
}
}
Our example has two buttons and five labels. The buttons dynamically change their
visibility.
I'm trying to design a JButton (an "Ok" button) that to look good has to be horizontally centered in the containing JFrame.
I'm using the GUI Builder with the Free Form layout (GroupLayout).
I've taken several GUI builder tutorials (http://netbeans.org/kb/docs/java/quickstart-gui.html) but haven't found this topic. In other gui builders (delphi) this can be done by removing the anchors from both edges.
GroupLayout does support centering of components. It is a very capable
layout manager. I personally put it after the MigLayout manager and before
the FormLayout manager.
In the following three examples, we have a panel and a button. The button
is horizontally centered.
Centering with NetBeans Builder
To center a component using a Netbeans Builder, we need to create
horizontal resizable gaps from both sides of the button.
The green area of the screenshot is a currently selected gaps. The strings
inside the gaps suggest that it is a resizable gap.
The gaps are automatically added when we place components on the form.
To define a gap to be resizable, we right click on the gap and select
the "Edit Layout Space" option. We get the following dialog:
To get a resizable gap, we check the Resizable check box.
Centering manually by using a parallel group
Components can be centered in the parallel group by passing
the GroupLayout.Alignment.CENTER parameter.
package com.zetcode;
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import static javax.swing.GroupLayout.Alignment.CENTER;
import static javax.swing.GroupLayout.DEFAULT_SIZE;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GroupLayoutCenter extends JFrame {
public GroupLayoutCenter() {
initUI();
setTitle("Centered button");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
private void initUI() {
Container pane = getContentPane();
GroupLayout gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateGaps(true);
gl.setAutoCreateContainerGaps(true);
JPanel pnl = new JPanel();
pnl.setBorder(BorderFactory.createEtchedBorder());
JButton btn = new JButton("Button");
gl.setHorizontalGroup(gl.createParallelGroup(CENTER)
.addComponent(pnl, DEFAULT_SIZE, 200, DEFAULT_SIZE)
.addComponent(btn)
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(pnl, DEFAULT_SIZE, 200, DEFAULT_SIZE)
.addComponent(btn)
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GroupLayoutCenter ex = new GroupLayoutCenter();
ex.setVisible(true);
}
});
}
}
Centering manually by using gaps
This solution is what NetBeans generated code does. We place
two resizable gaps to the left and right sides of the button.
package com.zetcode;
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import static javax.swing.GroupLayout.DEFAULT_SIZE;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GroupLayoutCenter2 extends JFrame {
public GroupLayoutCenter2() {
initUI();
setTitle("Centered button");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
private void initUI() {
Container pane = getContentPane();
GroupLayout gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateGaps(true);
gl.setAutoCreateContainerGaps(true);
JPanel pnl = new JPanel();
pnl.setBorder(BorderFactory.createEtchedBorder());
JButton btn = new JButton("Button");
gl.setHorizontalGroup(gl.createParallelGroup()
.addComponent(pnl, DEFAULT_SIZE, 200, DEFAULT_SIZE)
.addGroup(gl.createSequentialGroup()
.addGap(5, 100, Short.MAX_VALUE)
.addComponent(btn)
.addGap(5, 100, Short.MAX_VALUE))
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(pnl, DEFAULT_SIZE, 200, DEFAULT_SIZE)
.addComponent(btn)
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GroupLayoutCenter2 ex = new GroupLayoutCenter2();
ex.setVisible(true);
}
});
}
}
If you want your component to remain centered in its container if the container is resized, you have several options available to you, but I don't think that GroupLayout is one of them (please correct me if I'm wrong). One way is to change the container's layout to GridBagLayout, and then simply add the JButton into it without any constraints.
Try GridbagLayout See this
Try another LayoutManager!
GroupLayout is very obscure when just looking at the code generated by NetBeans.
If you used DesignGridLayout then what you need is as simple as:
DesignGridLayout layout = new DesignGridLayout(container);
layout.row().center().add(okButton);
javax.swing.Box.Filler can be used to pad space around a component inside a GroupLayout. In NetBeans Matisse (GUI Builder) they're called 'Horizontal Strut' under the 'Swing Fillers' section of the palette. Just put one on either side of your button, size them to fill all the empty space between the button and the edge of the container, and make sure they're both flagged to Auto Resize Horizontally.
As for using another LayoutManager, I know people don't like GroupLayout because it is not conducive to manual coding. That is by design. As the docs say, GroupLayout is intended to be used by GUI builders. So as long as you're ok with tying yourself to using NetBeans & Matisse for the life of your GUI, GroupLayout is really the best option.