I have what appears to be a simple issue. I have some labels that I'd like to align to the left but when I resize, they start to drift towards the middle. This is going to throw off the alignment of other components I plan on adding. What do I do to keep them to the left?
It's short, easy code, not sure what my problem is here:
package com.protocase.notes.views;
import com.protocase.notes.model.Note;
import com.protocase.notes.model.User;
import java.awt.Component;
import java.util.Date;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.BevelBorder;
/**
* #author dah01
*/
public class NotesPanel extends JPanel{
public NotesPanel(Note note){
this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
JLabel creatorLabel = new JLabel("Note by "+note.getCreator()+ " # "+note.getDateCreated());
creatorLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
creatorLabel.setHorizontalAlignment(JLabel.LEFT);
JTextArea notesContentsArea = new JTextArea(note.getContents());
notesContentsArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(notesContentsArea);
JLabel editorLabel = new JLabel(" -- Last edited by "+note.getLastEdited() +" at "+note.getDateModified());
editorLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
editorLabel.setHorizontalAlignment(JLabel.LEFT);
this.add(creatorLabel);
this.add(scrollPane);
this.add(editorLabel);
this.setBorder(new BevelBorder(BevelBorder.RAISED));
}
public static void main(String[] args) {
JFrame frame = new JFrame("Notes Panel");
Note note = new Note();
User user = new User();
user.setFirstName("d");
user.setLastName("h");
user.setUserID("dah01");
note.setCreator(user);
note.setLastEdited(user);
note.setDateCreated(new Date());
note.setDateModified(new Date());
note.setContents("A TEST CONTENTS");
NotesPanel np = new NotesPanel(note);
JScrollPane scroll = new JScrollPane(np);
frame.setContentPane(scroll);
np.setVisible(true);
frame.setVisible(true);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
If you want to align things in your panel, you have to align everything. You forgot to align your JScrollPane. If you add this line to your code, the alignment should be fixed for you:
scrollPane.setAlignmentX(JScrollPane.LEFT_ALIGNMENT);
And what your new constructor would look like:
public NotesPanel(Note note){
this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
JLabel creatorLabel = new JLabel("Note by "+note.getCreator()+ " # "+note.getDateCreated());
creatorLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
creatorLabel.setHorizontalAlignment(JLabel.LEFT);
JTextArea notesContentsArea = new JTextArea(note.getContents());
notesContentsArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(notesContentsArea);
scrollPane.setAlignmentX(JScrollPane.LEFT_ALIGNMENT);
JLabel editorLabel = new JLabel(" -- Last edited by "+note.getLastEdited() +" at "+note.getDateModified());
editorLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
editorLabel.setHorizontalAlignment(JLabel.LEFT);
this.add(creatorLabel);
this.add(scrollPane);
this.add(editorLabel);
this.setBorder(new BevelBorder(BevelBorder.RAISED));
}
As per your comments, I would recommend you to go with some other layout, I would recommend MigLayout
Here you go with MigLayout:
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import net.miginfocom.swing.MigLayout;
import javax.swing.JLabel;
import javax.swing.JTextArea;
public class MigLayoutDemo {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MigLayoutDemo();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public MigLayoutDemo() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
frame.setContentPane(contentPane);
contentPane.setLayout(new MigLayout("", "[grow]", "[][grow][]"));
JLabel lblLabel = new JLabel("Label 1");
lblLabel.setBorder(BorderFactory.createLineBorder(Color.red));
contentPane.add(lblLabel, "cell 0 0,alignx left");
JTextArea textArea = new JTextArea();
contentPane.add(textArea, "cell 0 1,grow");
JLabel lblLabel_1 = new JLabel("Label 2");
lblLabel_1.setBorder(BorderFactory.createLineBorder(Color.red));
contentPane.add(lblLabel_1, "cell 0 2,alignx left");
frame.setVisible(true);
}
}
OUTPUT :
As you can see labels marked with red border are not stretched to the middle, they are left aligned .
Using a BorderLayout definitely fixes your issue.
this.setLayout(new BorderLayout());
this.add(creatorLabel, BorderLayout.NORTH);
this.add(scrollPane);
this.add(editorLabel, BorderLayout.SOUTH);
Alternatively, if you have more components to display in the UI than what you show in the sample code, you can still use a GridBagLayout. I know that not many people like to use this one because it's quite verbose but in my opinion, it's the most powerful layout manager of swing.
Related
I have three components inside a top to down boxLayout (JLabel, JTextField, JButton). The problem is that when i set the X alignment for the label it looks as if i would've changed the X alignment of the button and vice versa, only when both have the same alignment it works fine.
When the screen gets wider both components take a weird alignment.
when both components have the same alignment everything works fine.
here is my code:
public void create(){
JPanel panel = new JPanel();
BoxLayout boxLayout = new BoxLayout(panel, BoxLayout.Y_AXIS);
panel.setLayout(boxLayout);
panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
JLabel etiqueta = new JLabel("Numero de consultorio: ");
etiqueta.setBackground(Color.BLUE);
etiqueta.setOpaque(true);
etiqueta.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(etiqueta);
JTextField consultorio = new JTextField();
panel.add(consultorio);
JButton registrar = new JButton("Registrar");
registrar.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(registrar);
this.getContentPane().add(panel, BorderLayout.CENTER);
}
Here is the proposed by Andrew Thompson solution:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class TestFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TestFrame()::create);
}
private void create() {
JPanel panel = new JPanel();
BoxLayout boxLayout = new BoxLayout(panel, BoxLayout.Y_AXIS);
panel.setLayout(boxLayout);
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JLabel etiqueta = new JLabel("Numero de consultorio: ");
etiqueta.setBackground(Color.BLUE);
etiqueta.setOpaque(true);
JPanel layout = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
layout.add(etiqueta);
panel.add(layout);
JTextField consultorio = new JTextField();
panel.add(consultorio);
JButton registrar = new JButton("Registrar");
layout = new JPanel(new FlowLayout(FlowLayout.TRAILING, 0, 0));
layout.add(registrar);
panel.add(layout);
JFrame frm = new JFrame("Test");
frm.getContentPane().add(panel, BorderLayout.CENTER);
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
}
When I set the outpanel into a BoxLayout then the panel disappears. However the scrollbar shows that indicates my panel in ArrayList are in the right position.
I am totally new to Java so I'll appreciate any comments.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.util.*;
import javax.swing.*;
public class gui extends JFrame{
int ctr=0, top=5;
public List<JPanel> o_panels = new ArrayList<JPanel>(); //Your List
public gui(){
super("MCC");
setLayout(null);
//Output panel for the results
JPanel outpanel = new JPanel();
outpanel.setBackground(Color.blue);
outpanel.setVisible(true);
outpanel.setLayout(new BoxLayout(outpanel, BoxLayout.PAGE_AXIS));
//Scroll pane
JScrollPane scrollPane = new JScrollPane(outpanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setBounds(0,0,780,400);
add(scrollPane);
//result panel
//creating and adding panels in to the array list
while(ctr<=4){
JPanel label1 = new JPanel();
label1.setPreferredSize(new Dimension(600,100));
o_panels.add(label1);
outpanel.add(o_panels.get(ctr));
ctr++;
}
}
public void runGui(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(800,600);
this.setVisible(true);
this.setResizable(false);
//i call this on the other class
}
}
There is, not much, wrong with your code, the problem is, you've not established any means by which you can see what you've been adding
Have a look at this...
while (ctr <= 4) {
JPanel label1 = new JPanel();
label1.setPreferredSize(new Dimension(600, 100));
o_panels.add(label1);
outpanel.add(o_panels.get(ctr));
ctr++;
}
All the panels are the same color and you've added nothing to them, so how could you possible know if they were been added or layout correctly...
I simple added label1.setBorder(new LineBorder(Color.RED)); and got this result...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class Test extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
Test test = new Test();
test.runGui();
}
});
}
int ctr = 0, top = 5;
public List<JPanel> o_panels = new ArrayList<JPanel>(); //Your List
public Test() {
super("MCC");
//Output panel for the results
JPanel outpanel = new JPanel();
outpanel.setBackground(Color.blue);
outpanel.setVisible(true);
outpanel.setLayout(new BoxLayout(outpanel, BoxLayout.PAGE_AXIS));
//Scroll pane
JScrollPane scrollPane = new JScrollPane(outpanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setBounds(0, 0, 780, 400);
add(scrollPane);
//result panel
//creating and adding panels in to the array list
while (ctr <= 4) {
JPanel label1 = new JPanel();
label1.setPreferredSize(new Dimension(600, 100));
label1.setBorder(new LineBorder(Color.RED));
o_panels.add(label1);
outpanel.add(o_panels.get(ctr));
ctr++;
}
}
public void runGui() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
this.setVisible(true);
// this.setResizable(false);
setLocationRelativeTo(null);
}
}
Also have a look at Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
And you really should avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
I am relatively new to programming, so I am sorry if this question is stupid. I am creating a Java program that involves one JButton inside a JPanel, and the JPanel is in a JFrame. Another button is outside the JPanel but still in the JFrame. I set the layout to a BoxLayout. My problem is that the the panel, which I made black, is taking up the whole JFrame except for where the second button is. How do I make the JPanel so it is only taking up the area right around the first button?
public class alt {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton button1 = new JButton("button 1");
JButton button2 = new JButton("button 2");
public alt(){
frame.setVisible(true);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
panel.setBackground(Color.black);
frame.setTitle("test");
frame.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
panel.add(button1);
frame.add(panel);
frame.add(button2);
button2.setAlignmentX(Component.CENTER_ALIGNMENT);
}
}
You could make use of a different layout manager, one which gives you more control over deciding how space is allocated and filling is handled, for example, GridBagLayout...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SampleLayout {
public static void main(String[] args) {
new SampleLayout();
}
public SampleLayout() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JPanel panel = new JPanel();
JButton button1 = new JButton("button 1");
JButton button2 = new JButton("button 2");
panel.setBackground(Color.black);
panel.add(button1);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
frame.add(panel, gbc);
frame.add(button2, gbc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Take a look at Laying Out Components Within a Container for more details
The reason why your panel takes up the bulk of the frame's content pane lies
in the way the BoxLayout manager works with the minimum, preferred,
and maximum values of components. It takes the maximum value of a component
into account. And since the maximum value of a JPanel is huge, it takes
all the space available. The solution is to change the maximum value
of a panel. However, this is bad practice. I do not recommend to use
the BoxLayout manager -- it is very weak and leads to poor code.
I recommend to use either the MigLayout manager or the GroupLayout manager.
I provide three solutions: a corrected BoxLayout solution, a MigLayout solution,
and a GroupLayout solution.
BoxLayout solution
We determine the maximum size of the button and change the panel's size
to be a bit larger than the button's.
package com.zetcode;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BoxLayoutPanel extends JFrame {
public BoxLayoutPanel() {
initUI();
}
private void initUI() {
JPanel cpane = (JPanel) getContentPane();
cpane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
cpane.setLayout(new BoxLayout(cpane,
BoxLayout.Y_AXIS));
JPanel pnl = new JPanel();
JButton btn1 = new JButton("Button 1");
JButton btn2 = new JButton("Button 2");
Dimension dm = btn1.getMaximumSize();
dm.height += 15;
dm.width += 15;
pnl.setMaximumSize(dm);
pnl.setBackground(Color.black);
add(pnl);
add(Box.createVerticalStrut(10));
pnl.add(btn1);
btn2.setAlignmentX(Component.CENTER_ALIGNMENT);
add(btn2);
setTitle("BoxLayout solution");
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
BoxLayoutPanel ex = new BoxLayoutPanel();
ex.setVisible(true);
}
});
}
}
This is not a clean solution. Generally, we should avoid calling the getMaximumSize() and
the setMaximumSize() in the application code -- this is the layout manager's job. Also in three occasions, we use fixed pixel widths: when we define an empty border, a vertical strut, and a maximum panel's size. This code is however not portable.
Pixel widths change when the resolution of the screen changes. This is a
shortcoming of the BoxLayout manager.
MigLayout solution
This solution is much cleaner and more portable. MigLayout is a third-party
manager, so we need to download additional libraries.
package com.zetcode;
import java.awt.Color;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public class MigLayoutPanel extends JFrame {
public MigLayoutPanel(){
initUI();
setTitle("MigLayout solution");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void initUI() {
JPanel main = new JPanel(new MigLayout("center"));
JPanel pnl2 = new JPanel();
JButton btn1 = new JButton("Button 1");
JButton btn2 = new JButton("Button 2");
pnl2.setBackground(Color.black);
pnl2.add(btn1);
main.add(pnl2, "wrap");
main.add(btn2, "alignx center");
add(main);
pack();
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MigLayoutPanel ex = new MigLayoutPanel();
ex.setVisible(true);
}
});
}
}
GroupLayout solution
GroupLayout is a built-in layout manager. With MigLayout, they are the most
portable and flexible layout managers.
package com.zetcode;
import java.awt.Color;
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.GroupLayout;
import static javax.swing.GroupLayout.Alignment.CENTER;
import static javax.swing.GroupLayout.DEFAULT_SIZE;
import static javax.swing.GroupLayout.PREFERRED_SIZE;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
public class GroupLayoutPanel extends JFrame {
public GroupLayoutPanel(){
initUI();
setTitle("GroupLayout solution");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
private void initUI() {
Container pane = getContentPane();
GroupLayout gl = new GroupLayout(pane);
pane.setLayout(gl);
JPanel pnl = new JPanel();
JButton btn1 = new JButton("Button 1");
pnl.add(btn1);
JButton btn2 = new JButton("Button 2");
pnl.setBackground(Color.black);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createSequentialGroup()
.addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE)
.addGroup(gl.createParallelGroup(CENTER)
.addComponent(pnl, DEFAULT_SIZE, DEFAULT_SIZE,
PREFERRED_SIZE)
.addComponent(btn2))
.addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE)
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addContainerGap()
.addComponent(pnl, DEFAULT_SIZE, DEFAULT_SIZE,
PREFERRED_SIZE)
.addPreferredGap(RELATED)
.addComponent(btn2)
.addContainerGap()
);
pack();
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GroupLayoutPanel ex = new GroupLayoutPanel();
ex.setVisible(true);
}
});
}
}
I'm a complete noobie with swing. I'm trying to set a few JPanels and TextAreas to show up but after spending 2 days reading the APIs and trying to add panels to frames and textareas to panels and nothing is showing up.. I'm utterly confused. If anyone could explain how is the best way to do this I would be very grateful
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class GUI {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setLayout(new FlowLayout()); // J FRAME
JPanel panel = new JPanel(); // first panel on the left
panel.setLayout(new BoxLayout(panel, 1));
// frame.getContentPane().setBackground(Color.red);
frame.add(panel);
JLabel surname = new JLabel();
JLabel initial = new JLabel();
JLabel ext = new JLabel();
surname.setOpaque(true);
initial.setOpaque(true);
ext.setOpaque(true);
frame.add(surname);
panel.add(initial);
panel.add(ext);
JTextArea table = new JTextArea();
table.setEditable(false);
panel.add(table);
table.setVisible(true);
You're adding stuff to the JFrame after it's already visible. If you do that, you need to revalidate your JFrame so it knows to redo its layout.
You could also just wait to show your JFrame until after you've added everything.
Edit: Here is an example program that shows what I'm talking about. Try running this, then take out the call to revalidate() to see the difference.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Test {
public static void main(String[] args) {
final JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JButton show = new JButton("Show");
show.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent showE) {
frame.add(new JLabel("Test"), BorderLayout.SOUTH);
frame.revalidate(); //tell the JFrame to redo its layout!
}
});
frame.add(show);
frame.setSize(200, 200);
frame.setVisible(true);
}
}
You are adding an empty elements like:
JLabel surname = new JLabel();
Your elements is already added but have nothing to be display.
Try :
JLabel surname = new JLabel("UserName");
JLabel initial = new JLabel("Iinitial");
JLabel ext = new JLabel("Ext");
JTextArea table = new JTextArea(10, 5);
I searched a little bit and did not find a good answer to my problem.
I am working on a gui that has to be resizable. It contains a status JTextArea that is inside a JScrollPane. And this is my problem. As long as I don't manually resize my JFrame, the "initial" layout is kept and everything looks fine. As soon as I manually resize (if the JTextArea is already in scrolled mode), the layout gets messed up.
Here is a SSCCE (I got rid of most of the parts while keeping the structure of the code. I hope it's more readable that way):
import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import net.miginfocom.swing.MigLayout;
public class Tab extends JFrame {
private static final long serialVersionUID = 1L;
private JTextArea messageTextArea;
private JPanel optionPanel, messagePanel;
private JTabbedPane plotTabPane;
public static void main(String[] args) {
final Tab tab = new Tab();
tab.setSize(1000, 600);
tab.setVisible(true);
new Thread(new Runnable() {
#Override
public void run() {
int count = 0;
tab.printRawMessage("start");
while (true) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {}
tab.printRawMessage("\ntestMessage" + count++);
}
}
}).start();
}
public Tab() {
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents();
}
private void initComponents() {
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new MigLayout("insets 0", "", ""));
mainPanel.add(getLeftTopPanel(), "shrinky, top, w 450!");
mainPanel.add(getRightPanel(), "spany 5, wrap, grow, pushx, wmin 400");
mainPanel.add(getMessagePanel(), "pushy, growy, w 450!");
JScrollPane contentScrollPane = new JScrollPane(mainPanel);
contentScrollPane.setBorder(BorderFactory.createEmptyBorder());
setContentPane(contentScrollPane);
}
protected JPanel getLeftTopPanel() {
if (optionPanel == null) {
optionPanel = new JPanel();
optionPanel.setBorder(BorderFactory.createTitledBorder(null, "Configuration", TitledBorder.LEFT, TitledBorder.TOP, new Font("null", Font.BOLD, 12), Color.BLUE));
optionPanel.setLayout(new MigLayout("insets 0", "", "top, align 50%"));
JLabel label = new JLabel("Choose");
label.setHorizontalAlignment(JLabel.RIGHT);
optionPanel.add(label, "w 65!");
optionPanel.add(new JSeparator(JSeparator.VERTICAL), "spany 5, growy, w 2!");
optionPanel.add(new JComboBox(new String[] {"option1", "option2", "option3"}), "span, growx, wrap");
optionPanel.add(new JLabel("Type"), "right");
optionPanel.add(new JTextField("3"), "w 65!, split 2");
optionPanel.add(new JLabel("Unit"), "wrap");
optionPanel.add(new JLabel("Slide"), "right");
optionPanel.add(new JSlider(0, 100), "span, growx, wrap");
}
return optionPanel;
}
protected JTabbedPane getRightPanel() {
if (plotTabPane == null) {
plotTabPane = new JTabbedPane();
plotTabPane.add("Tab1", new JPanel());
plotTabPane.add("Tab2", new JPanel());
}
return plotTabPane;
}
protected JPanel getMessagePanel() {
if (messagePanel == null) {
messagePanel = new JPanel();
messagePanel.setBorder(BorderFactory.createTitledBorder(null, "Status Console", TitledBorder.LEFT, TitledBorder.TOP, new Font("null", Font.BOLD, 12), Color.BLUE));
messagePanel.setLayout(new MigLayout("insets 0", "", "top, align 50%"));
messagePanel.add(new JScrollPane(getMessageTextArea()), "push, grow");
}
return messagePanel;
}
protected JTextArea getMessageTextArea() {
if (messageTextArea == null) {
messageTextArea = new JTextArea();
messageTextArea.setEditable(false);
messageTextArea.setFont(new Font(null, Font.PLAIN, 20));
messageTextArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
}
return messageTextArea;
}
public void printRawMessage(String rawMessage) {
getMessageTextArea().append(rawMessage);
getMessageTextArea().setCaretPosition(getMessageTextArea().getText().length());
}
}
The layout stuff basically happens in initComponents().
To see the problem:
Start the Application (I used miglayout-4.0-swing.jar).
Wait a bit (don't resize the window), until there are enough messages to create the scrollbar in the status text area.
Now this is what I want. The JTextArea goes all the way to the bottom of the JFrame and is scrolled if neccessary.
Now resize the window. As you can see, everything gets messed up. It will only be fine, if the window is maximized.
Here are two screenshots. The first one is how I want it to be:
The second one is after resizing:
My question: Can somebody tell me, how I keep the layout the way it is before resizing? I want to have the JTextArea go all the way down to the bottom of the window. And if neccessary, the scrollbar should appear. The only way, the status panel can go below the bottom of the window is, if the window is too small (because the configuration panel has a fixed height).
I hope I made myself clear. If not, please ask. ;)
EDIT: You can see the behaviour I want, if you remove the top JScrollPanel (the one that holds all the components). Just change
JScrollPane contentScrollPane = new JScrollPane(mainPanel);
contentScrollPane.setBorder(BorderFactory.createEmptyBorder());
setContentPane(contentScrollPane);
to
setContentPane(mainPanel);
to see what I mean. Unfortunately, this way I loose the scrollbars if the window is very small.
Focusing on your status area and using nested layouts produces the result shown below. Note in particular,
Use invokeLater() to construct the GUI on the EDT.
Use javax.swing.Timer to update the GUI on the EDT.
Use pack() to make the window fit the preferred size and layouts of its subcomponents.
Use the update policy of DefaultCaret to control scrolling.
Avoid needless lazy instantiation in public accessors.
Avoid setXxxSize(); override getXxxSize() judiciously.
Critically examine the decision to extend JFrame.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.border.TitledBorder;
import javax.swing.text.DefaultCaret;
public class Tab extends JFrame {
private JTextArea messageTextArea;
private JPanel optionPanel, messagePanel;
private JTabbedPane plotTabPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
final Tab tab = new Tab();
tab.setVisible(true);
Timer t = new Timer(200, new ActionListener() {
int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
tab.printRawMessage("testMessage" + count++);
}
});
t.start();
}
});
}
public Tab() {
initComponents();
}
private void initComponents() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel(new GridLayout(1, 0));
Box leftPanel = new Box(BoxLayout.Y_AXIS);
leftPanel.add(getLeftTopPanel());
leftPanel.add(getMessagePanel());
mainPanel.add(leftPanel);
mainPanel.add(getRightPanel());
this.add(mainPanel);
this.pack();
this.setLocationRelativeTo(null);
}
protected JPanel getLeftTopPanel() {
optionPanel = new JPanel();
optionPanel.setBorder(BorderFactory.createTitledBorder(null,
"Configuration", TitledBorder.LEFT, TitledBorder.TOP,
new Font("null", Font.BOLD, 12), Color.BLUE));
JLabel label = new JLabel("Choose");
label.setHorizontalAlignment(JLabel.RIGHT);
optionPanel.add(label);
optionPanel.add(new JSeparator(JSeparator.VERTICAL));
optionPanel.add(new JComboBox(
new String[]{"option1", "option2", "option3"}));
optionPanel.add(new JLabel("Type"));
optionPanel.add(new JTextField("3"));
return optionPanel;
}
protected JTabbedPane getRightPanel() {
plotTabPane = new JTabbedPane();
plotTabPane.add("Tab1", new JPanel());
plotTabPane.add("Tab2", new JPanel());
return plotTabPane;
}
protected JPanel getMessagePanel() {
messagePanel = new JPanel(new GridLayout());
messagePanel.setBorder(BorderFactory.createTitledBorder(null,
"Status Console", TitledBorder.LEFT, TitledBorder.TOP,
new Font("null", Font.BOLD, 12), Color.BLUE));
final JScrollPane sp = new JScrollPane(getMessageTextArea());
sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
messagePanel.add(sp);
return messagePanel;
}
protected JTextArea getMessageTextArea() {
messageTextArea = new JTextArea("", 10, 19);
messageTextArea.setEditable(false);
messageTextArea.setFont(new Font(null, Font.PLAIN, 20));
messageTextArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
DefaultCaret caret = (DefaultCaret) messageTextArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
return messageTextArea;
}
public void printRawMessage(String rawMessage) {
messageTextArea.append(rawMessage + "\n");
}
}
Add size constraints to your mainPanel in the initComponents method. For instance :
mainPanel.setMinimumSize(new Dimension(400, 400));
mainPanel.setPreferredSize(new Dimension(400, 400));
mainPanel.setMaximumSize(new Dimension(400, 400));