Swing MigLayout cannot grow fill column to fill container length - java

I am using MigLayout for a very long window.
and I wish to "push" the second and fourth column to fill all the length of the whole window, but I cannot achieve it. There's no push option in column constraint, only grow and fill.
Here's a SCCEE, as someone once suggested, whose name I already forgot:
package com.WindThunderStudio.MigLayoutTest;
import java.awt.Cursor;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public class MigLayoutTest extends JFrame{
private JFrame mainFrame;
private JPanel panel;
private JLabel lblResumenAuto;
private JLabel lblResumenAutoResult;
private JLabel lblResumenRazonSocial;
private JLabel lblResumenRazonSocialResult;
private JLabel lblResumenPeriodo;
private JLabel lblResumenPeriodoResult;
private JLabel lblResumenFechaHora;
private JLabel lblResumenFechaHoraResult;
public MigLayoutTest(){
run();
}
public void run(){
mainFrame = new JFrame();
mainFrame.setBounds(0, 0, 1250, 500);
mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel p = new JPanel();
p.setSize(mainFrame.getSize());
p.setLayout(new MigLayout("fill","[max!, grow]","[50:20:30]10[100::]10[20::]10[50!]10[20!]"));
mainFrame.setContentPane(p);
panel = new JPanel();
panel.setLayout(new MigLayout("fillx", "[left, 15%]10[left, grow, 35%]10[left, 15%]10[left, grow, 35%]", "[center]10[center]"));
lblResumenAuto = new JLabel("MY LABEL 1111111111111");
lblResumenAutoResult = new JLabel("1111111111111111111111");
panel.add(lblResumenAuto);
panel.add(lblResumenAutoResult);
lblResumenRazonSocial = new JLabel("MY LABEL 2222222222");
lblResumenRazonSocialResult = new JLabel("2222222222222222222222");
panel.add(lblResumenRazonSocial);
panel.add(lblResumenRazonSocialResult,"wrap");
lblResumenPeriodo = new JLabel("MY LABEL 33333333333333");
lblResumenPeriodoResult = new JLabel("3333333333333333333333333333333333333333333333333333333");
panel.add(lblResumenPeriodo);
panel.add(lblResumenPeriodoResult);
//poner el texto como html puede tener otra linea, porque es muy largo
lblResumenFechaHora = new JLabel("<html>MY LABEL <br /> 4444444444444444</html>");
lblResumenFechaHoraResult = new JLabel("4444444444444444444444444");
panel.add(lblResumenFechaHora);
panel.add(lblResumenFechaHoraResult);
p.add(panel,"cell 0 0");
getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setBounds(0, 0, 1250, 500);
getContentPane().add(mainFrame.getContentPane());
pack();
setVisible(true);
setLocationRelativeTo(null);
setResizable(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MigLayoutTest test = new MigLayoutTest();
}
});
}
}
If you run the code, you can note that the columns' width increases as its containing text's length changes. But it never fills the whole width of its container.
What's desirable, is to fix the column 0 and 2 by 15% of the whole width, and let column 1 and 3 to ocupy the rest, 35%, with the first two columns occupying the 50% size of the whole width.
Am I missing something here? I don't want to specify the width of every column, setting pre:min:max, because it is bad practice, as suggested by this post, which gets lots of vote up.
panel.setLayout(new MigLayout("fillx",
"[left, 15%]10[left, grow, 35%]10[left, 15%]10[left, grow, 35%]",
"[center]10[center]"));
But, if I set pref:min:max, it can fill the whole width.

First the code, then explanation. Try this:
import java.awt.Cursor;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public class MigLayoutTest extends JFrame {
private JPanel panel;
private JLabel lblResumenAuto;
private JLabel lblResumenAutoResult;
private JLabel lblResumenRazonSocial;
private JLabel lblResumenRazonSocialResult;
private JLabel lblResumenPeriodo;
private JLabel lblResumenPeriodoResult;
private JLabel lblResumenFechaHora;
private JLabel lblResumenFechaHoraResult;
public MigLayoutTest() {
run();
}
public void run() {
panel = new JPanel();
panel.setLayout(new MigLayout("debug, fill",
"[left, 15%]10[left, 35%]10[left, 15%]10[left, 35%]", "[center]10[center]"));
lblResumenAuto = new JLabel("MY LABEL 1111111111111");
lblResumenAutoResult = new JLabel("1111111111111111111111");
panel.add(lblResumenAuto, "sg label");
panel.add(lblResumenAutoResult, "sg value");
lblResumenRazonSocial = new JLabel("MY LABEL 2222222222");
lblResumenRazonSocialResult = new JLabel("2222222222222222222222");
panel.add(lblResumenRazonSocial, "sg label");
panel.add(lblResumenRazonSocialResult, "sg value, wrap");
lblResumenPeriodo = new JLabel("MY LABEL 33333333333333");
lblResumenPeriodoResult = new JLabel("3333333333333333333333333333333333333333333333333333333");
panel.add(lblResumenPeriodo, "sg label");
panel.add(lblResumenPeriodoResult, "sg value");
// poner el texto como html puede tener otra linea, porque es muy largo
lblResumenFechaHora = new JLabel("<html>MY LABEL <br /> 4444444444444444</html>");
lblResumenFechaHoraResult = new JLabel("4444444444444444444444444");
panel.add(lblResumenFechaHora, "sg label");
panel.add(lblResumenFechaHoraResult, "sg value");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
getContentPane().add(panel);
pack();
setVisible(true);
setLocationRelativeTo(null);
setResizable(true);
}
public static void main(String[] args) {
MigLayoutTest test = new MigLayoutTest();
}
}
The explanation of my changes:
Now except for simplifying the code and layout I used “debug” within the layout constraints to see what actually happens to the layout. I suggest using it anytime things go wrong with the layout - it makes MigLayout draw the borders of components and cells, thus visualizing potential problems.
I removed the unnecessary mainframe and p - if you really need it for a nested layout try to add it once you have solved the inner layout to your liking.
As to p and panel - it might be that you need here two different layouts, one nested in another, but this is the actual source of your problem. p had also its own grid layout, with
p.add(panel,"cell 0 0");
you put panel in the top left cell of the p - this is why panel was not distributed over the whole window but sat in the upper left corner.
As you see without p it positions nicely in the middle of the screen without any constant size, still showing all components, but more importantly it has 50% of the window size for the first and 50% for the last two columns. This was achieved by giving the components a “sizegroup”:
Gives the component a size group name. All components that share a
size group name will get the same BoundSize (min/preferred/max). It is
used to make sure that all components in the same size group gets the
same min/preferred/max size which is that of the largest component in
the group. An empty name "" can be used.
And it also resizes like it should!
The nested layout might also had been the root of another problem – don’t know if you didn’t notice it or it just didn’t show up on your machine, but if I tried to resize your window the panel got wider and wider (never narrower) even if I shrunk the window. At some point it got wider than the window itself and even then growed further on each resize.
Next - setting the dimensions to a constant value didn’t make sense, since after pack the layout manager starts sizing everything based on preferred sizes of the window and its content. Besides, you never know which size is your users’ screen, so any constant size could be equally bad if effectively used. Better to drive the size through the content and available runtime environment. With your code on my machine it took all available horizontal space of my two screens (2 x 1280) and did’t look pretty.
I also think that you do not need to start the frame using EventQueue.invokeLater, just create a MigLayoutTest and that’s it.
EDIT after OP's own answer
Setting the size using setBounds(0, 0, 1250, 500) before pack is not working correctly (by this I mean making the window be that size). Even in the screenshot below OP's own answer it is not 500px high. Here is what I get on Windows 7 running JDK 1.8.0_91:
The size of my screen is 1280 x 1024, the size of the programm's window is 914 x 301.
I'd suggest using one of the following:
To set it to the constant size of 1250 x 500 px move the setSize between pack and setVisible:
...
pack();
setSize(1250, 500);
I'd use setSize, setBounds doesn't make sense, since by calling setLocationRelativeTo(null) you centering the programm's window on the screen anyway, so the origin is being dismissed immediately.
To maximize horizontally and let the height be 500 px set main window's preferred size before pack:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setPreferredSize(new Dimension(screenSize.width, 500));
And to maximize horizontally and let the preferred height as it was originally:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setPreferredSize(new Dimension(screenSize.width, getPreferredSize().height));
Of course you can make the window 1250 x 500 big by setting its preferred size instead of using setSize, too.
The resize problem is not so big now, but it's still there - make the window wider, than narrow it just a little bit. You'll notice that the panel gets wider even if the window got narrowed. The problem is that panel component doesn't get big enough to fill the one column of p initially (BTW you can add the 'debug' flag to each MigLayout, also that of the panel - it will then outline all of the inner components as well).
To make it fill the parent container add it like this:
p.add(panel, "cell 0 0, grow");
Now it is the full width of p from the very beginning and resizing works as expected.
Regarding starting the JFrame using invokeLater - we start our main windows usually without it and had never had problems, since there were no interactions with Swing until the first frame was visible, yet I have just noticed that it is regarded to be the best practise - even in Oracle's tutorials. It looks like I had learned something here, too :-).
Comparision of the frame's window with and without adding with "grow"
Test scenario: start the application and resize it to be wider.
As you see in the first screenshot the component size is smaller than the column width - it looks like the component were lying behind the column size while resizing. On the second screenshot the component width remains the same as the column width at all times. As I said previously the reason might be the Java and/or operating system combination, I don't know. But obviously it behaves differently and on my machine less than optimal.

Thanks to #Tomasz Stanczak, I have solved it finally. However, I found part of what he said is relevant, and others are not. For future readers who may see this, I have to made it clearer.
The final working code is:
import java.awt.Cursor;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public class MigLayoutMySCCEE extends JFrame{
private JFrame mainFrame;
private JPanel panel;
private JLabel lblResumenAuto;
private JLabel lblResumenAutoResult;
private JLabel lblResumenRazonSocial;
private JLabel lblResumenRazonSocialResult;
private JLabel lblResumenPeriodo;
private JLabel lblResumenPeriodoResult;
private JLabel lblResumenFechaHora;
private JLabel lblResumenFechaHoraResult;
public MigLayoutMySCCEE(){
run();
}
public void run(){
JPanel p = new JPanel();
p.setLayout(new MigLayout("debug, fill","[grow]","[50:20:30]10[100::]10[20::]10[50!]10[20!]"));
panel = new JPanel();
panel.setLayout(new MigLayout("fillx", "[left, 15%]10[left, grow, 35%]10[left, 15%]10[left, grow, 35%]", "[center]10[center]"));
lblResumenAuto = new JLabel("MY LABEL 1111111111111");
lblResumenAutoResult = new JLabel("1111111111111111111111");
panel.add(lblResumenAuto);
panel.add(lblResumenAutoResult);
lblResumenRazonSocial = new JLabel("MY LABEL 2222222222");
lblResumenRazonSocialResult = new JLabel("2222222222222222222222");
panel.add(lblResumenRazonSocial);
panel.add(lblResumenRazonSocialResult,"wrap");
lblResumenPeriodo = new JLabel("MY LABEL 33333333333333");
lblResumenPeriodoResult = new JLabel("3333333333333333333333333333333333333333333333333333333");
panel.add(lblResumenPeriodo);
panel.add(lblResumenPeriodoResult);
//poner el texto como html puede tener otra linea, porque es muy largo
lblResumenFechaHora = new JLabel("<html>MY LABEL <br /> 4444444444444444</html>");
lblResumenFechaHoraResult = new JLabel("4444444444444444444444444");
panel.add(lblResumenFechaHora);
panel.add(lblResumenFechaHoraResult);
p.add(panel,"cell 0 0");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setBounds(0, 0, 1250, 500);
getContentPane().add(p);
pack();
setVisible(true);
setLocationRelativeTo(null);
setResizable(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MigLayoutMySCCEE test = new MigLayoutMySCCEE();
}
});
}
}
And the window looks like:
Some notes:
The debug trick is very useful and urges me to read DOC again. I wish it can gain more attention and importance in the Quick Start page, however.
The unnecessary nesting is a problem and Tomasz make me to review the hierarchy, great! I changed the nesting part to make it clearer. But it's irrelevant.
The sizeGroup part is great idea and I decide to use it in future development as much as possible, but it's irrelevant to my case. I solved it without using it.
I have found the wider-and-wider problem after Tomasz's tip, but it is due to [max!] combined with adding the panel to the first cell of grid layout, not frame/panel nesting. I removed [max!] and changed it to [grow] and the width is not expanding anymore. I didn't touch the p.add(panel, "cell 0 0") part. As observed and by definition,
p.setLayout(new MigLayout("debug, fill","[grow]","[50:20:30]10[100::]10[20::]10[50!]10[20!]"));
the first line of the panel has only one column, if I understand well.
EDIT after Tomasz's edit
I surely have learned more than you did :) I tried to get rid of setBounds() part and to change add(panel, "cell 0 0") to add(panel, "grow"), but I cannot see much difference, am I missing something here? Yet "grow" is almost always the better choice and desirable.
Here's 2 GIFs showing what I got: (by ScreenToGif, a light-weighted but powerful tool, especially useful for showcase)

Related

JButton in GridBagLayout in a JPanel

I am learning swing from past week, I have some issue with GridBagConstraints to put one button in top left corner but all other buttons in default in GridBagConstraints ?
I am using code that like not original but states the problem
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
class MyPanel extends JPanel
{
JButton menu = new JButton("Menu"), button = new JButton("Puzzle");
GridBagConstraints gbc1 = new GridBagConstraints(), gbc2 = new GridBagConstraints();
private void setup()
{
gbc1.anchor = GridBagConstraints.FIRST_LINE_START;
gbc2.anchor = GridBagConstraints.CENTER;
gbc2.weightx = 1.0;
gbc2.weighty = 1.0;
}
public MyPanel()
{
this.setLayout(new GridBagLayout());
this.setup();
this.button.setPreferredSize(new Dimension(250, 140));
this.add(menu, gbc1);
this.add(button, gbc2);
}
}
#SuppressWarnings("serial")
public class Test extends JFrame
{
public Test()
{
this.setTitle("Test");
this.setContentPane(new MyPanel());
this.setResizable(false);
this.setSize(800, 600);
this.setVisible(true);
}
public static void main(String args[])
{
SwingUtilities.invokeLater(() -> new Test());
}
}
output
I want menu is top corner.
I read from here but i did not understand this Could you please explain GridBagConstraints for how to do that.
I Hope that problem is clear to understand, if not please let me know in comments.
EDIT:
#camickr suggestion works but a little problem, the Puzzle Button is not in extract center.
Thanks.
By default the GridBagLayout will display all the components centered horizontally and vertically, unless one of the components has a weightx/weighty value not equal to 0. Then that component will fill the extra space in the frame.
So if you want one component at the "top/left" and one in the "center", you need to:
use the "anchor" constraint. It will be different for both components.
the component in the center will need to use the "weightx/weighty" constraints.
However, an easer solution might be to use a combination of panels with different layout managers.
For example:
JPanel menuPanel = new JPanel( new FlowLayout(FlowLayout.LEFT) );
menuPanel.add(menuButton);
JPanel centerPanel = new JPanel( new GridBagLayout() );
centerPanel.add(puzzle, new GridBagConstraints());
frame.add(menuPanel, BorderLayout.PAGE_START);
frame.add(centerPanel, BorderLayout.CENTER);
So now the "top" of the frame will contain a panel with components displayed from the left and the "center" of the frame will contain your puzzle centered in the remaining space of the frame.
Edit:
I solved it but put gridx and gridy to 0 in center component, but i did not completely understand the setttings
Well I mentioned that you would need to use the gridx/gridy constraints. You should always use those constraints as it is very obvious what grid you want to add the component to. The examples from the tutorial always specify those values.
Using gridx/gridy both equal to 0, does not really make sense. The effect is that you have two components trying to share the same grid.
Remove the setResizable(false) statement and shrink the size of the frame to see how the button repositions itself.
It is not normal that two components share the same grid. Normally you would have the menu on the first row and the button on the second row. This will center the button horizontally in the frame and vertically in the space below the menu.
What you are doi

How to center the view by middle panel in swing

I have to build a UI with three panels, leftPanel, middlePanel and rightPanel, Panels should be aligned Horizontally
left and right panels should get the maximum and equal width as possible while the middle panel can get the minimum width as its child component required, once the panels are added view should be centered by middle panel.
I have done following test code to build the UI
import java.awt.Color;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class SwingTest extends JFrame {
private static final long serialVersionUID = 1L;
public SwingTest() {
setTitle("Swing Test");
setSize(750, 350);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(getParent());
// Rule used to find the component positions, for the testing purpose
Rule rule = new Rule(Rule.HORIZONTAL, true);
rule.setPreferredWidth(10);
rule.setPreferredHeight(40);
SidePanel leftPanel = new SidePanel("Left");
SidePanel rightPanel = new SidePanel("Right");
JPanel middlePanel = new JPanel();
middlePanel.setBorder(BorderFactory.createLineBorder(Color.black));
// Add left,right and middle panel horizontally
JPanel containerPanel = new JPanel();
containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.X_AXIS));
containerPanel.add(leftPanel);
containerPanel.add(middlePanel);
containerPanel.add(rightPanel);
// Add rule and container panel Vertically
JPanel outerPanel = new JPanel();
outerPanel.setLayout(new BoxLayout(outerPanel, BoxLayout.Y_AXIS));
outerPanel.add(rule);
outerPanel.add(containerPanel);
add(outerPanel);
}
public static void main(String[] args) {
SwingTest test = new SwingTest();
test.setVisible(true);
}
private static class SidePanel extends JPanel {
private static final long serialVersionUID = 1L;
private SidePanel(String text) {
setLayout(new GridBagLayout());
add(new JLabel(text));
}
}
}
I have used Rule class from oracle swing tutorial site, it can be used to find the position of components. get Rule.java
The problem i have is as shown in the UI screen shot, view is not get centered by middle panel, Right now the view centered as width of `left + middle panel = right panel'
I don't want to set the panel height and width manually, It should be handled by the LayoutManager itself.
What i want is middle panel has to be get centered in a way left + middle/2 = middle/2 + right panel width.
I couldn't find a way to make the middle panel to get centered to view, Could someone help me to find a solution for this?.
You need to understand how the BoxLayout works.
First space is allocated at the preferred size of each component. Then if there is extra space, that space is allocated to each component.
The middle doesn't appear to be centered because the preferred size of the right label is larger than the left label. So when the extra space is allocated the right side is larger than the left side.
This is easy to test. Just change the text of the right side panel to "LEFT".
However, that leads to another question - Why doesn't the size of the middle panel change? I don't know the answer to this. Although for some reason the default FlowLayout seems to work different than the GridBagLayout. Again you can test this by changing the layout of the middle panel to be a GridBagLayout.
If you always want the left/right panels to be the same size then you might want to consider using the Relative Layout. Using this layout the basic code would be:
RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
JPanel panel = new JPanel( rl );
panel.add(leftPanel, new Float(1));
panel.add(middle, new Float(3));
panel.add(rightPanel, new Float(1));
Now the left/right panels will be the same size independent of the components added to each panel.

JFrame Layout set to null issues

I am trying to learn GUI from a book I just got, but I am having tons of problems (my code is attached). When I launch this app, All I get is a minimum window that need to expand every time, and the only thing it shows is one of my radio buttons. I am obviously doing something wrong here. Can somebody please advise me?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CarPayment
{
public static void main(String[] args)
{
new CarPaymentCalc();
} // main
} // CarPayment
class CarPaymentCalc extends JFrame
{
private JLabel labelTitle, labelInterest, labelLoan;
private JTextField tfLoan, tfInterest, tfAnswer;
private ButtonGroup bgSelect;
private JRadioButton rbPmts36, rbPmts48, rbPmts60;
private JButton bClear;
public CarPaymentCalc()
{
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null); // Centers the window
setTitle("Car Payments Calculator");
// Labels
labelTitle = new JLabel("Calculate My Car Payment");
labelTitle.setVerticalAlignment(JLabel.TOP);
add(labelTitle, JLabel.CENTER);
labelLoan = new JLabel("Loan Amount");
labelLoan.setLocation(0, 10);
add(labelLoan);
labelInterest = new JLabel("Interest");
labelInterest.setLocation(0, 45);
add(labelInterest);
// Input Fields
tfLoan = new JTextField(20);
tfLoan.setLocation(0, 25);
add(tfLoan);
tfInterest = new JTextField(5);
tfInterest.setLocation(0, 60);
add(tfInterest);
JTextArea tfAnswer = new JTextArea(50,10);
tfAnswer.setLocation(0, 110);
add(tfAnswer);
// Radio buttons
bgSelect = new ButtonGroup();
rbPmts36 = new JRadioButton();
rbPmts36.setText("36 Payments");
rbPmts36.setLocation(0, 80);
bgSelect.add(rbPmts36);
add(rbPmts36);
bgSelect.add(rbPmts48);
rbPmts48.setText("48 Payments");
rbPmts48.setLocation(150, 80);
rbPmts48 = new JRadioButton();
add(rbPmts48);
bgSelect.add(rbPmts60);
rbPmts60.setText("60 Payments");
rbPmts60.setLocation(300, 80);
rbPmts60 = new JRadioButton();
add(rbPmts60);
setLayout(null);
pack();
} // CarPaymentCalc
}
Don't use null layouts. Pixel perfect layouts are an illusion in modern UI design, you have no control over fonts, DPI, rendering pipelines or other factors that will change the way that you components will be rendered on the screen.
Swing was designed to work with layout managers to overcome these issues. If you insist on ignoring these features and work against the API design, be prepared for a lot of headaches and never ending hard work.
By looking at JavaDocs for pack...
Causes this Window to be sized to fit the preferred size and layouts
of its subcomponents. The resulting width and height of the window are
automatically enlarged if either of dimensions is less than the
minimum size as specified by the previous call to the setMinimumSize
method. If the window and/or its owner are not displayable
yet, both of them are made displayable before calculating the
preferred size. The Window is validated after its size is being
calculated.
You will note that pack relies on the layout manager API to determine the preferred viewable size of the frames content. By setting the layout manager to null, you've prevented it from been able to determine this information, so basically, it's done nothing.
If your book is telling you to use null layouts, get rid of it, it's not teaching you good habits or practices.
Take a look at Laying Out Components Within a Container for more details about layout managers and how to use them
Other problems you are having:
Calling setVisible(true); before you've finished building the UI can sometimes prevent the UI from appearing the way you intended it to. You could call revalidate on the frame, but it's simpler to just call setVisible last.
The calculation used by setLocationRelativeTo uses the frames current size, but this hasn't been set yet. Instead, you should do something like:
public CarPaymentCalc() {
//...build UI here with appropriate layout managers...
pack();
setLocationRelativeTo(null);
setVisible(true);
}
I would also discourage you from extending directly from top level containers like JFrame, apart from the fact that you're not adding any functionality to the frame per se, it prevents you from re-using the IU later.
Better to start with a JPanel and add this to whatever you want, but that's just me.

JTextField displayed as slit when using FlowLayout...please explain

Can someone please explain to me, why every time I use the FlowLayout Layout manager
my textfields are displayed as slits.
I have bumped my head against this problem for some time now, and I can't seem to figure
out why it goes wrong.
I get a feeling it is a simple thing that I am overlooking time and time again, so if
someone would please explain this phenomenon to me, I would be forever grateful.
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class Console
{
public Console()
{
makeConsole();
}
private void makeConsole()
{
JFrame console = new JFrame("ArchiveConsole");
Container base = console.getContentPane();
base.setSize(400, 250);
console.setSize(base.getSize());
base.setLayout(new FlowLayout(FlowLayout.CENTER, 5,5));
JTextField tf = new JTextField();
tf.setSize(base.getWidth(), 25);
base.add(tf);
console.setVisible(true);
}
}
From the Swing layout manager tutorial
The FlowLayout class puts components in a row, sized at their preferred size. If the horizontal space in the container is too small to put all the components in one row, the FlowLayout class uses multiple rows. If the container is wider than necessary for a row of components, the row is, by default, centered horizontally within the container
So you need to adjust the preferred size of your textfield, preferably by using the setColumns method.
Note that if you want your text field to span the whole width you might want to use another layout then the FlowLayout for the reason quoted above
For example, the following code gives a nice looking JTextField, but I have hardcoded the number of columns
import javax.swing.JFrame;
import javax.swing.JTextField;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.FlowLayout;
public class TextFieldWithFlowLayout {
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
#Override
public void run() {
JFrame console = new JFrame("ArchiveConsole");
Container base = console.getContentPane();
base.setLayout(new FlowLayout( FlowLayout.CENTER, 5,5));
JTextField tf = new JTextField();
tf.setColumns( 20 );
base.add(tf);
console.pack();
console.setVisible(true);
}
} );
}
}
Use:
JTextField tf = new JTextField(25);
Instead of:
JTextField tf = new JTextField();
tf.setSize(base.getWidth(), 25);
When you use LayoutManagers, don't try to set size and position manually. You are conflicting with what the LayoutManager does. Layoutmanagers size and position components based on constraints and preferred/minimum/maximum size. Most Swing components handle that automatically, so you should usually not force their preferred size.
As for your JTextField, since it does not contain any text, its preferred size is close to nothing. Use setColumns to indicate a preferredSize or invoke setPreferredSize
Thanks!
The parameter that using in the JTextField function is very important!
JPanel panel_buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
panel_buttons.add(...);
...
panel_buttons.add(...);
JTextField txt_search = new JTextField(20);
panel_buttons.add(txt_search);
If change 20 to 30 or large, may be u couldn't find it.May be the txt_search is in the next line.

Swing BoxLayout problem with JComboBox without using setXXXSize

here's an SSCCE:
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BoxLayoutTest extends JFrame {
public BoxLayoutTest(){
JPanel main = new JPanel();
main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
main.setBackground(Color.red);
this.add(main);
JPanel northPanel = new JPanel();
JPanel middle = new JPanel();
middle.setLayout(new BoxLayout(middle, BoxLayout.X_AXIS));
middle.add(new JButton("FOO"));
middle.add(Box.createHorizontalGlue());
JPanel aPanel = new JPanel();
aPanel.setBackground(Color.black);
JComboBox b = new JComboBox();
//b.setPreferredSize(new Dimension(100,16)); //uncomment this to see the layout I would like to achieve
//b.setMaximumSize(new Dimension(100,16));
//middle.add(b); //uncomment this line
middle.setBackground(Color.green);
northPanel.setBackground(Color.blue);
main.add(northPanel);
main.add(middle);
main.add(Box.createVerticalGlue());
this.setSize(800,600);
this.setResizable(true);
this.setVisible(true);
}
public static void main(String[] args) {
new BoxLayoutTest();
}
}
I'm trying to refactor some classes I wrote some time ago, when I didn't know that using setXXXSize methods on components is wrong.
Using a resizable frame ,the result I want to achieve is the following:
The northPanel should stay on top and change it's size accordingly to the frame size modifications (seems to work fine)
The green panel where I put the JButton should keep the maximum dimension of the JButton and stay just below the blue panel above (this works fine if I only put JButtons inside that panel).
The problem arise if I put a JComboBox inside the green panel (try to uncomment the line in the SSCCE). I guess JComboBox hasn't a maximum size specified, so it stretches with the frame. In the previous wrong version of my code I was using setxxxSize methods on the JComboBox to limit it's dimension(try to uncomment the line on setXXXSize methods to see it).
My question are:
Is it possible to achieve the same result using BoxLayout without invoking setXXXSize() methods?
If yes, how?
Is there any other LayoutManager that can I use to get that effect?
Please put me in the right direction
JComboBox is misbehaving (the same as JTextField) in reporting an unbounded max height: should never show more than a single line. Remedy is the same: subclass and return a reasonable height
JComboBox b = new JComboBox() {
/**
* #inherited <p>
*/
#Override
public Dimension getMaximumSize() {
Dimension max = super.getMaximumSize();
max.height = getPreferredSize().height;
return max;
}
};
just for fun, here's a snippet using MigLayout (which is my personal favorite currently :-)
// two panels as placeholders
JPanel northPanel = new JPanel();
northPanel.setBackground(Color.YELLOW);
JPanel southPanel = new JPanel();
southPanel.setBackground(Color.YELLOW);
// layout with two content columns
LC layoutContraints = new LC().wrapAfter(2)
.debug(1000);
AC columnContraints = new AC()
// first column pref, followed by greedy gap
.size("pref").gap("push")
// second
.size("pref");
// three rows, top/bottom growing, middle pref
AC rowContraints = new AC()
.grow().gap().size("pref").gap().grow();
MigLayout layout = new MigLayout(layoutContraints, columnContraints,
rowContraints);
JPanel main = new JPanel(layout);
main.setBackground(Color.WHITE);
// add top spanning columns and growing
main.add(northPanel, "spanx, grow");
main.add(new JButton("FOO"));
// well-behaved combo: max height == pref height
JComboBox combo = new JComboBox() {
#Override
public Dimension getMaximumSize() {
Dimension max = super.getMaximumSize();
max.height = getPreferredSize().height;
return max;
}
};
// set a prototype to keep it from constantly adjusting
combo.setPrototypeDisplayValue("somethingaslongasIwant");
main.add(combo);
// add top spanning columns and growing
main.add(southPanel, "spanx, grow");
I have always seen using the layout managers in the jdk are not easy. They are either too simple and inflexible or the gridbaglayout is just too much trouble. Instead I started using the jgoodies form layout and never looked back since.. Have a look at it. Its very simple and easy to use. Here's a link:
http://www.jgoodies.com/freeware/forms/
Make sure you go through the white paper.
And now, we also have google providing us a WYSISWG editor for the formlayout as a plugin for eclipse. This just makes life a lot lot easier.
http://code.google.com/javadevtools/wbpro/palettes/swing_palette.html

Categories