Resize makes things wrong - java

I want to create a JInternalFrame with some components in it.
My aim is to design a bash console in Java.
My frame is made of 4 components:
JTextArea included into a JScrollPane
JLabel with the text "Cmd:"
JTextField
JButton with the text "Send"
And I have the following code:
Box box = Box.createHorizontalBox();
box.add(Box.createVerticalStrut(5));
box.add(this.cmd_label);
box.add(Box.createVerticalStrut(5));
box.add(this.cmd_input);
box.add(Box.createVerticalStrut(5));
box.add(this.submit);
box.add(Box.createVerticalStrut(5));
Box mainBox = Box.createVerticalBox();
mainBox.add(Box.createHorizontalStrut(5));
mainBox.add(this.result_scroll);
mainBox.add(Box.createHorizontalStrut(5));
mainBox.add(box);
mainBox.add(Box.createHorizontalStrut(5));
add(mainBox);
So when the frame has not been maximized, I have a correct look:
But when I maximize it, all components are incorrectly located:
So, here is my question: How can I set a weight to the components to fix their location every time, or, how can I fix it?
Thanks.

I think this would be better done with a BorderLayout. In a BorderLayout, the component specified as the center component will expand to fill as much space as possible, and the other components will remain at their preferred sizes.
int hgap = 5;
int vgap = 5;
internalFrame.getContentPane().setLayout(new BorderLayout(hgap, vgap));
internalFrame.getContentPane().add(this.result_scroll, BorderLayout.CENTER);
JPanel bottomPanel = new JPanel();
bottomPanel.add(this.cmd_label);
bottomPanel.add(this.cmd_input);
bottomPanel.add(this.submit);
internalFrame.getContentPane().add(bottomPanel, BorderLayout.SOUTH);

Here try this code, is this behaviour exceptable :
import java.awt.*;
import javax.swing.*;
public class LayoutExample
{
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("LAYOUT EXAMPLE");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new BorderLayout(5, 5));
centerPanel.setBorder(
BorderFactory.createEmptyBorder(2, 2, 2, 2));
JTextArea tarea = new JTextArea(10, 10);
tarea.setBackground(Color.DARK_GRAY.darker());
tarea.setForeground(Color.WHITE);
tarea.setCaretColor(Color.WHITE);
tarea.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(tarea);
centerPanel.add(scrollPane, BorderLayout.CENTER);
JPanel footerPanel = new JPanel();
footerPanel.setLayout(new BorderLayout(5, 5));
footerPanel.setBorder(
BorderFactory.createEmptyBorder(2, 2, 2, 2));
JLabel cmdLabel = new JLabel("Cmd : ");
JTextField tfield = new JTextField(10);
JPanel buttonPanel = new JPanel();
buttonPanel.setBorder(
BorderFactory.createEmptyBorder(2, 2, 2, 2));
JButton sendButton = new JButton("SEND");
footerPanel.add(cmdLabel, BorderLayout.LINE_START);
footerPanel.add(tfield, BorderLayout.CENTER);
buttonPanel.add(sendButton);
footerPanel.add(buttonPanel, BorderLayout.LINE_END);
frame.getContentPane().add(centerPanel, BorderLayout.CENTER);
frame.getContentPane().add(footerPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new LayoutExample().createAndDisplayGUI();
}
});
}
}
OUTPUT :

Base problem here is what I consider a bug in JTextField's max layout hint: it's unbounded in both horizontal and vertical dimension. The latter is pure nonsense for a component designed for showing a single line of text. To fix, subclass and let it return its pref for the height, like:
JTextField cmdInput = new JTextField() {
#Override
public Dimension getMaximumSize() {
Dimension max = super.getMaximumSize();
max.height = getPreferredSize().height;
return max;
}
};
As BoxLayout respects maxSize, the excess height now will be given to the top box only.
On the long run, consider switching to a third party manager which allows fine-tuning in a all-in-one-panel approach. Yeah, here comes my current favourite: MigLayout. Compare the following lines to all the nesting and border tricks above and have fun :-)
MigLayout layout = new MigLayout("wrap 3, debug",
"[][grow, fill][]", // 3 columns, middle column filled and allows growing
"[grow, fill][]"); // two rows, first filled and allows growing
JComponent content = new JPanel(layout);
// the scrollPane in the first row spanning all columns
// and growing in both directions
content.add(new JScrollPane(new JTextArea(20, 20)), "span, grow");
// auto-wrapped to first column in second row
content.add(new JLabel("Cmd:"));
content.add(new JTextField());
content.add(new JButton("Submit"));

Related

Set JComboBox height

I'm not able to set the height of a JComboBox, I searched in the web but didn't found the right answer.
As you can see from the image below the combo box fills nearly all the panel height and I'd like it to have a smaller height.
I tried setting size with getPreferredSize() method but it didn't work, it worked only for other components like the button.
My code
private JComponent firstPanel()
{
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JLabel spesaAnnuaSingola = new JLabel();
spesaAnnuaSingola.setText("Spesa Annua Singola");
panel.add(spesaAnnuaSingola, BorderLayout.NORTH);
JComboBox<String> listaSpese = new JComboBox<String>();
panel.add(listaSpese, BorderLayout.CENTER);
JTextField speseAnnuaSingolaTF = new JTextField();
speseAnnuaSingolaTF.setText("");
speseAnnuaSingolaTF.setEditable(false);
panel.add(speseAnnuaSingolaTF, BorderLayout.LINE_START);
JButton button = new JButton("CALCOLA")
{
public Dimension getPreferredSize()
{
return new Dimension(150,50);
};
};
JPanel leftflowpanel = new JPanel( new FlowLayout(FlowLayout.LEFT) );
leftflowpanel.add(speseAnnuaSingolaTF);
panel.add(leftflowpanel, BorderLayout.SOUTH);
JPanel rightflowpanel = new JPanel( new FlowLayout(FlowLayout.RIGHT) );
rightflowpanel.add(button);
panel.add(rightflowpanel, BorderLayout.SOUTH);
return panel;
}
And then:
public StatsPanel()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
GridBagConstraints c = new GridBagConstraints();
// I will need a grid layout
this.setLayout(new GridLayout(1, 1, 30, 30));
JPanel panelLeft = new JPanel(new BorderLayout());
panelLeft.setBorder(BorderFactory.createEmptyBorder(20, 10, 20, 50));
panelLeft.add(firstPanel(), BorderLayout.NORTH);
c.fill = GridBagConstraints.VERTICAL;
c.gridx = 0;
c.gridy = 0;
this.add(panelLeft);
}
JComboBox<String> listaSpese = new JComboBox<String>();
panel.add(listaSpese, BorderLayout.CENTER);
You add your combo box to the CENTER of the BorderLayout, which gets all the extra space of the frame. Don't add the combo box to the CENTER.
Instead you will need to nest panels. So create a panel for the NORTH of the BorderLayout. Then this panel will contain both your label and your combo box. Maybe use a vertical BoxLayout for this panel. Then both the label and the combo box will be displayed at their preferred heights.
Read the section from the Swing on Layout Manager for more information. The point is you can nest multiple panels each using a different layout to achieve your desired layout.

Need opinions about this basic layout

I guess this is a simple question... basically it's about layout considerations. So let consider the code below, I get this:
.
public class TestCode_Web {
public static void main(String[] args) {
JFrame window = new JFrame("Test");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(200, 300);
// Inner panel ---------------------------------------------------------
JPanel innerPanel = new JPanel(new BorderLayout());
innerPanel.setBackground(new Color(250, 250, 200));
window.add(innerPanel);
// Northern panel ------------------------------------------------------
JPanel panelN = new JPanel(new BorderLayout());
JLabel labelN = new JLabel("Label");
panelN.add(labelN, BorderLayout.WEST);
panelN.setBackground(new Color(200, 250, 250));
innerPanel.add(panelN, BorderLayout.NORTH);
// Center panel --------------------------------------------------------
JPanel panelC = new JPanel();
panelC.setBackground(new Color(250, 200, 250));
JPanel panelCheckBoxes = new JPanel(new GridLayout(0, 1));
final JCheckBox c1 = new JCheckBox("C1");
final JCheckBox c2 = new JCheckBox("C2");
final JCheckBox c3 = new JCheckBox("C3");
panelCheckBoxes.add(c1);
panelCheckBoxes.add(c2);
panelCheckBoxes.add(c3);
int width = panelCheckBoxes.getPreferredSize().width;
int height = panelCheckBoxes.getPreferredSize().height;
panelCheckBoxes.setPreferredSize(new Dimension(width, height));
panelC.add(panelCheckBoxes);
innerPanel.add(panelC, BorderLayout.CENTER);
// Southern panel --------------------------------------------------------
JPanel panelS = new JPanel(new BorderLayout());
JLabel labelS = new JLabel(String.valueOf(width) + "/" + String.valueOf(height));
panelS.add(labelS, BorderLayout.WEST);
panelS.setBackground(new Color(250, 250, 200));
innerPanel.add(panelS, BorderLayout.SOUTH);
// ...
window.setVisible(true);
}
}
What I would like is to have this:
How could I achieve that ? I guesss there are several ways, I'm waiting for your diverse proposals...
One way to do this would be to overwrite the getPreferredSize() method of panelCheckBoxes to return panelC's width. This way, panelCheckBoxes' size will automatically adapt to the width of panelC.
final JPanel panelC = new JPanel();
// [...]
JPanel panelCheckBoxes = new JPanel(new GridLayout(0, 1)) {
public Dimension getPreferredSize() {
return new Dimension(panelC.getWidth(),
super.getPreferredSize().height);
}
};
For accessing panelC inside the anonymous inner class, it has to be final (i.e., after initialization, the panelC variable can not be assigned a new value, which is no problem in your case).
Just setting the preferredSize at that point in the constructor will not work, since (1) the size is not known yet, and (2) it might change when the window is resized. You could, however, use setPreferredSize after the call to window.setVisible(true);, when panelC got a size:
// after window.setVisible(true);
panelCheckBoxes.setPreferredSize(new Dimension(panelC.getWidth(),
panelCheckBoxes.getHeight()));
However, note that this way panelCheckBoxes still won't resize when you resize the window.
If you just want the checkboxes to be aligned to the left (but not necessarily stretch over the whole width), a simpler way would be to put panelC into the WEST container of the BorderLayout. Assuming that the colors are only for debugging and in the end everything will be the same color, you won't see a difference.
Finally, for more complex layouts you might want to check out GridBadLayout. It takes some getting used to, but once mastered, it's worth the effort.

Java applets which layout suitable for this interface?

Which is the most suitable layout for this interface? I have tried FlowLayout, but can't get the SUBMIT button place in the correct position.
I recommend miglayout, it is very easy to use and it always handles, what I am throwing at it.
In this case, I would create two panels. The first panel has a "wrap 3" constraint as a parameter for the miglayout constructor, the second only has one button, which is added with a "alignx center, aligny center" constraint.
Here is an example:
public class TestApplet extends JApplet{
#Override
public void init() {
super.init();
setSize(400, 300);
setLayout(new MigLayout("fill, insets 0"));
JPanel leftPanel = new JPanel(new MigLayout("wrap 3"));
for (int i = 0; i < 9; i++) {
leftPanel.add(new JButton(""+i));
}
add(leftPanel);
JPanel rightPanel = new JPanel(new MigLayout());
rightPanel.add(new JButton("submit"), "alignx center, aligny center");
add(rightPanel);
}
}
You only have to sort the numbers on the buttons by your needs.
First Grid Layout with two columns.
in first column add panal1 and in second add submit with BorderLayout.CENTER.
In that panal1 you can use 3x3 grid layout.
I tried this out on a JFrame and you can do the same in your applet.
Create a Grid of One row two columns.
Create a Box Layout on the second Panel with Page Axis and then ensure you have vertical glues with button in center.
Create a Grid of 3x3 and add buttons.
Code sample:
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(1, 2));
JPanel leftPanel = new JPanel(new GridLayout(3, 3));
for(int i=0;i<9;i++)
{
JButton button = new JButton();
button.setText(i+"");
leftPanel.add(button);
}
frame.add(leftPanel);
JPanel rightPanel = new JPanel();
BoxLayout layout = new BoxLayout(rightPanel, BoxLayout.PAGE_AXIS);
rightPanel.setLayout(layout);
JButton button = new JButton("Submit");
button.setAlignmentX(
Component.CENTER_ALIGNMENT);
rightPanel.add(Box.createVerticalGlue()); //Ensure this order
rightPanel.add(button);
rightPanel.add(Box.createVerticalGlue());
frame.add(rightPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);

Keep BoxLayout From Expanding Children

I want to stack some JComponents vertically inside a JPanel so they stack at the top and any extra space is at the bottom. I'm using a BoxLayout. The components will each contain a JTextArea that should allow the text to wrap if necessary. So, basically, I want the height of each of these components to be the minimum necessary for displaying the (possibly wrapped) text.
Here's a contained code example of what I'm doing:
import javax.swing.*;
import java.awt.*;
public class TextAreaTester {
public static void main(String[] args){
new TextAreaTester();
}
public TextAreaTester(){
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel,BoxLayout.PAGE_AXIS));
panel.setPreferredSize(new Dimension(100,400));
for(int i = 0; i<3; i++){
JPanel item = new JPanel(new BorderLayout());
JTextArea textarea = new JTextArea("this is a line of text I want to wrap if necessary");
textarea.setWrapStyleWord(true);
textarea.setLineWrap(true);
textarea.setMaximumSize( textarea.getPreferredSize() );
item.add(textarea,BorderLayout.NORTH);
panel.add(item);
}
panel.add(Box.createGlue());
frame.add(panel);
frame.setVisible(true);
frame.pack();
}
}
The child JPanels are expanding to fill the vertical space. I tried using glue because I thought that's what glue was for, but it seems to do nothing at all. Any help?
Note: I have found questions that look almost identical, but none with answers I can apply.
One solution: nest JPanels with the outer JPanel using Borderlayout and adding the BoxLayout using JPanel to this one BorderLayout.NORTH, also known as BorderLayout.PAGE_START:
Edit for Kleopatra:
import javax.swing.*;
import java.awt.*;
public class TextAreaTester {
public static void main(String[] args) {
new TextAreaTester();
}
public TextAreaTester() {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
// panel.setPreferredSize(new Dimension(100,400));
for (int i = 0; i < 3; i++) {
JPanel item = new JPanel(new BorderLayout());
// item.setLayout(new BoxLayout(item,BoxLayout.LINE_AXIS));
JTextArea textarea = new JTextArea(
"this is a line of text I want to wrap if necessary", 3, 35);
textarea.setWrapStyleWord(true);
textarea.setLineWrap(true);
// textarea.setMaximumSize(textarea.getPreferredSize());
// item.setMaximumSize( item.getPreferredSize() );
item.add(new JScrollPane(textarea), BorderLayout.NORTH);
panel.add(item);
}
panel.add(Box.createGlue());
JPanel mainPanel = new JPanel(new BorderLayout()) {
private final int prefW = 100;
private final int prefH = 400;
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
};
// mainPanel.setPreferredSize(new Dimension(100, 400));
mainPanel.add(panel, BorderLayout.PAGE_START);
frame.add(mainPanel);
frame.setVisible(true);
// frame.getContentPane().add(jp);
frame.pack();
}
}
Alternatively, you can use Box.Filler. Just replace your call to panel.add(Box.createGlue()) with
panel.add(new Box.Filler(new Dimension(0, 0),
new Dimension(0, Short.MAX_VALUE),
new Dimension(0, Short.MAX_VALUE)));
If you want to achieve the same for a horizontal layout, just use Short.MAX_VALUE for width instead of height in the Dimension call.

horizontal split pane resize with window

I have a horizontal split pane that contains a vertical split pane. When the window shows up i want the vertical split pane in the top of the horizontal split pane to be split in the middle. I want the horizontal divider to be in the middle.
I have that working, however, i also want to the horizontal split pane to change its size when the window is maximized. (It currently does not.)
I also have a button box below the horizontal pane and would like it to always be visible when the window is resized. Currently when the window launches i can see everything in the horizontal split. I am unable to see the buttons, because they do not fit in the preferred size of the window (800, 600). But i would like everything to show up in the window automatically and stay Glue'd to the border of the window when it is resized...
How can i do this?
Thanks!
Below is the code i am currently using. I call the create methods in a controller. createView is called first then the rest in sequential order.
public void createView() {
dialog = new JFrame();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
dialog.setAlwaysOnTop(true);
dialog.setBounds(0, 0, 800, 600);
dialog.setMinimumSize(new Dimension(800, 600));
dialog.setPreferredSize(new Dimension(800, 600));
dialog.setResizable(true);
dialog.setTitle("MJLA Class Control Panel");
contentPanel = new JPanel();
// contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
contentPanel.setLayout(new BorderLayout());
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
dialog.getContentPane().add(contentPanel, BorderLayout.CENTER);
classQuizSRTSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
contentPanel.add(classQuizSRTSplit, BorderLayout.NORTH);
classQuizSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
classQuizSRTSplit.setTopComponent(classQuizSplit);
// classQuizHBox = Box.createHorizontalBox();
// contentPanel.add(classQuizHBox);
sRTHBox = Box.createHorizontalBox();
contentPanel.add(sRTHBox);
buttonBox = Box.createHorizontalBox();
contentPanel.add(buttonBox, BorderLayout.SOUTH);
refreshButton = new JButton("Refresh");
buttonBox.add(refreshButton);
doneButton = new JButton("Done");
buttonBox.add(doneButton);
this.validateView();
}
public void createClassTablePanel() {
this.classTablePanel = new JPanel();
this.classTablePanel.setBorder(BorderFactory.createLineBorder(Color.black, 3));
this.classTablePanel.setPreferredSize(new Dimension(300, 300));
this.classTablePanel.setLayout(new BorderLayout());
// this.classQuizHBox.add(classTablePanel);
this.classQuizSplit.setLeftComponent(classTablePanel);
classTableModel = cPModel.getClassTableModel();
classTable = new JTable(this.classTableModel);
classTable.getSelectionModel().addListSelectionListener(this);
JScrollPane scrollPane = new JScrollPane(classTable);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
classTablePanel.add(scrollPane, BorderLayout.CENTER);
this.validateView();
}
public void createQuizTablePanel() {
this.quizTablePanel = new JPanel();
this.quizTablePanel.setBorder(BorderFactory.createLineBorder(Color.black, 3));
this.quizTablePanel.setPreferredSize(new Dimension(300, 300));
this.quizTablePanel.setLayout(new BorderLayout());
// this.classQuizHBox.add(quizTablePanel);
this.classQuizSplit.setRightComponent(quizTablePanel);
quizTableModel = cPModel.getQuizTableModel();
this.quizSorter = new TableRowSorter<DefaultTableModel>(quizTableModel);
quizTable = new JTable(this.quizTableModel);
quizTable.getSelectionModel().addListSelectionListener(this);
quizTable.setRowSorter(quizSorter);
JScrollPane scrollPane = new JScrollPane(quizTable);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
quizTablePanel.add(scrollPane, BorderLayout.CENTER);
Box buttonHBox = Box.createHorizontalBox();
quizTablePanel.add(buttonHBox, BorderLayout.SOUTH);
addQuizButton = new JButton("Add Quiz");
buttonHBox.add(addQuizButton);
removeQuizButton = new JButton("Remove Quiz");
buttonHBox.add(removeQuizButton);
editQuizButton = new JButton("Edit Quiz");
buttonHBox.add(editQuizButton);
this.validateView();
}
public void createStudentRecordTablePanel() {
this.studentRecordTablePanel = new JPanel();
this.studentRecordTablePanel.setBorder(BorderFactory.createLineBorder(Color.black, 3));
this.studentRecordTablePanel.setLayout(new BorderLayout());
// this.sRTHBox.add(studentRecordTablePanel);
this.classQuizSRTSplit.setBottomComponent(studentRecordTablePanel);
this.studentRecordTableModel = cPModel.getStudentRecordTableModel();
this.sRTSorter = new TableRowSorter<DefaultTableModel>(studentRecordTableModel);
sRTTable = new JTable(this.studentRecordTableModel);
sRTTable.setRowSorter(sRTSorter);
JScrollPane scrollPane = new JScrollPane(sRTTable);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
studentRecordTablePanel.add(scrollPane, BorderLayout.CENTER);
Box buttonHBox = Box.createHorizontalBox();
studentRecordTablePanel.add(buttonHBox, BorderLayout.SOUTH);
editGradeButton = new JButton("Edit Grade");
buttonHBox.add(editGradeButton);
generateReportButton = new JButton("Generate Report");
buttonHBox.add(generateReportButton);
this.validateView();
}
Another issue.
That fixed one of the problems #TrashGod. However, how can i make the horizontal split pane resize its component to fit the new size of the window, instead of their being that big gap between the done and refresh button and the bottom of the horizontal split pane?
I was thinking that i would have to listen for an event for when the window size changes and then call the pack() method when that happens, is that the only way or would that even work? (Just tested this, it did not work... just puts everything back to preferred sizes. duh)
Initial look.
After window maximized.
You might look at setResizeWeight(); a value of 0.5 should distribute the space evenly.
The pack() method "Causes this Window to be sized to fit the preferred size and layouts of its subcomponents." BorderLayout.NORTH and BorderLayout.SOUTH seem like suitable layouts for staying with the divider.
For additional help, please provide an sscce that exhibits the problem.

Categories