I have a JPanel using a FlowLayout layout manager and contains components with different sizes.
EDIT: I want to use the FlowLayout because it allows the components to wrap to a new line when the container is resized and they no longer fit next to each other.
The following image depicts the vertical alignment of the FlowLayout on the different components:
How can I modify the FlowLayout to align the top of components as depicted in the following image:
Here is a code example of the problem:
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
frame.getContentPane().add(flowPanel);
JButton firstComp = new JButton("First");
firstComp.setPreferredSize(new Dimension(200, 300));
flowPanel.add(firstComp);
JButton secondComp = new JButton("Second");
secondComp.setPreferredSize(new Dimension(160, 180));
flowPanel.add(secondComp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
FlowLayout does not by itself support alignment, so unless you actually need the multiple rows behaviour of it, it is better to use a layout manager that supports alignment (such as BoxLayout). It is possible to somewhat work around the issue though, by using the baseline alignment, that FlowLayout can do:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Align {
private static final int PREF_HEIGHT = 100;
Align() {
JFrame frame = new JFrame("Align test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel bg = new JPanel();
((FlowLayout) bg.getLayout()).setAlignOnBaseline(true);
frame.add(bg);
JPanel left = new JPanel();
left.setBackground(Color.BLUE);
left.setPreferredSize(new Dimension(100, PREF_HEIGHT));
bg.add(left);
JPanel right = new JPanel() {
#Override
public int getBaseline(int width, int height) {
return PREF_HEIGHT;
}
};
right.setBackground(Color.GREEN);
right.setPreferredSize(new Dimension(100, 50));
bg.add(right);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Align();
}
});
}
}
Results in:
This is, unfortunately, anything but flawless. There's firstly the semi magical way of getting the baseline, which depends on the height of the other components in the panel. Secondly, FlowLayout will reserve too much space for the component when it's moved to a row by it's own - it takes the same amount of space as if it was as high as the other panel. (This can be seen if you add more panels after right). At that point it might be easier to use nested layout for placing the smaller panel than messing with baselines.
Basically, you're better using some other layout manager unless you really can't avoid FlowLayout.
The FlowLayout is the only standard JDK layout manager that supports wrapping components to a new line. (There may be third party layout, like MigLayout that support this).
If you don't like the default functionality then you can customize the layout manager. Here is a simple example that lets the FlowLayout do the default layout and then it resets each component to the top of the line:
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class TopFlowLayout extends FlowLayout
{
#Override
public void layoutContainer(Container container)
{
super.layoutContainer(container);
Component c = container.getComponent(0);
int lineStart = getVgap();
int lineHeight = lineStart + c.getSize().height;
for (int i = 0; i < container.getComponentCount(); i++)
{
c = container.getComponent(i);
Point p = c.getLocation();
Dimension d = c.getSize();
if (p.y < lineHeight) // still on current line
{
p.y = lineStart;
lineHeight = Math.max(lineHeight, lineStart + d.height);
}
else // start a new line
{
lineStart = lineHeight + getVgap();
p.y = lineStart;
lineHeight = lineStart + d.height;
}
p.y = lineStart;
c.setLocation(p);
}
}
private static void createAndShowGUI()
{
TopFlowLayout layout = new TopFlowLayout();
layout.setAlignment(FlowLayout.LEFT);
JPanel flowPanel = new JPanel( layout );
Random random = new Random();
for (int i = 0; i < 10; i++)
{
flowPanel.add( createButton(i + "", random.nextInt(100), random.nextInt(100)) );
}
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( flowPanel );
frame.setLocationByPlatform( true );
frame.setSize(400, 400);
frame.setVisible( true );
}
private static JButton createButton(String text, int width, int height)
{
JButton button = new JButton(text);
Dimension size = new Dimension(width + 50, height + 50);
button.setPreferredSize(size);
return button;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
You may also want to consider extending the Wrap Layout which is also based on the FlowLayout but adds additional functionality.
Related
Task: Add some quantity of JLabel with some text to JPanel with BoxLayout and next add this panel to JScrollPane for scrolling down that text labels.
Problem:
I set text.setMaximumSize(..), but JScrollPane don't think so and add horizontal scrollbar. text.setPreferredSize limit perfectly, but i can't use it because need calculate height for each label (maybe use font size and getting label auto line breaker) and it's hard for me.
Here's code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
public class NewPanel {
private JFrame frame;
private JLabel text;
public NewPanel() {
int formWidth = 700;
int formHeight = 700;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++)
sb.append("Test String ");
String resStr = "<html>" + sb.toString() + "</html>";
text = new JLabel();
text.setText(resStr);
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(text);
JScrollPane scrollPane = new JScrollPane(
panel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
);
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setPreferredSize(new Dimension(formWidth, formHeight));
frame.add(scrollPane);
frame.pack();
//Change max width for label with form resize
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
super.componentResized(e);
resizePanels();
}
#Override
public void componentMoved(ComponentEvent e) {
super.componentMoved(e);
resizePanels();
}
});
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void resizePanels() {
int labelSize = frame.getWidth() - 200;
// - 200 For show that text limited by setMaximumSize
text.setMaximumSize(new Dimension(labelSize, Integer.MAX_VALUE));
//Integer.MAX_VALUE - for unlimited height (maybe exist better way?)
//text.setPreferredSize(new Dimension(labelSize, 1000));
//limit perfect and hide horizontal scroll, but don't think that it better way
//because i need get plane border "back to back" text border,
//so i must count label height (front height + maybe label auto line break place)
//that look like hard way
text.revalidate();
text.repaint();
}
}
I have a JPanel (cardLayoutPanel) which layout is CardLayout.
I also have another 3 different panels (firstPagePanel, secondPagePanel, thirdPagePanel)
firstPagePanel size is approximately around 450x400,
secondPagePanel size is approximately around 800x600
thirdPagePanel size is approximately around 1024x768
I cardLayoutPanel.add all 3 panels and I show firstPagePanel first as my view.
I want to let my program displayed the size of firstPagePanel first which is 450x400
and then if secondPagePanel is displayed, it will change the size to 800x600 and if thirdPagePanel is displayed,it will change the size to 1024,768
Instead of guessing what my 3 panel size should be, I used frame.getPreferredSize() but my first view will always take in the size of my thirdPagePanel which is 1024x768 instead of 450x400;
What can I do to resolve this issue?
public class MainFrame {
private CardLayout cardLayout = new CardLayout();
private JPanel cardLayoutPanel = new JPanel();
private FirstPagePanel firstPagePanel = new FirstPagePanel();
private SecondPagePanel secondPagePanel = new SecondPagePanel();
private ThirdPagePanel thirdPagePanel = new ThirdPagePanel();
private JFrame frame = new JFrame("Panel size test");
public MainFrame() {
cardLayoutPanel.setLayout(cardLayout);
cardLayoutPanel.add(firstPagePanel,"1");
cardLayoutPanel.add(secondPagePanel,"2");
cardLayoutPanel.add(thirdPagePanel,"3");
cardLayout.show(cardLayoutPanel,"1");
frame.add(cardLayoutPanel);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.getPreferredSize();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setResizable(false);
}
}
The easiest way is to:
Remove the current panel.
Add the new panel.
Pack the top level container.
E.G.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class ThreeSizeGui {
public static void swapComponentsAndResizeUI(
JComponent ui,
JComponent current,
JComponent next) {
ui.remove(current);
ui.add(next);
current = next;
Component c = ui.getTopLevelAncestor();
if (c instanceof Window) {
Window w = (Window) c;
w.pack();
}
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
final JPanel ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(6, 6, 6, 6));
JPanel controls = new JPanel(
new FlowLayout(FlowLayout.LEADING));
ui.add(controls, BorderLayout.PAGE_START);
int s = 100;
Dimension[] sizes = {
new Dimension(s * 4, s * 2),
new Dimension(s * 6, s * 3),
new Dimension(s * 8, s * 4)
};
final JComboBox cb = new JComboBox(sizes);
controls.add(cb);
final JPanel[] panels = new JPanel[sizes.length];
for (int ii = 0; ii < sizes.length; ii++) {
Dimension d = sizes[ii];
BufferedImage bi = new BufferedImage(
d.width, d.height, BufferedImage.TYPE_INT_RGB);
JPanel p = new JPanel(new GridLayout());
JLabel l = new JLabel(new ImageIcon(bi));
p.add(l);
panels[ii] = p;
}
ItemListener sizeListener = new ItemListener() {
JPanel current = panels[0];
#Override
public void itemStateChanged(ItemEvent e) {
JPanel next = panels[cb.getSelectedIndex()];
swapComponentsAndResizeUI(ui, current, next);
current = next;
}
};
cb.addItemListener(sizeListener);
ui.add(panels[0], BorderLayout.CENTER);
JFrame f = new JFrame("Three Sized Panels");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setContentPane(ui);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
I'm completely new to using the GUI in java, so I'm having a bit of trouble figuring out how to align everything that I need to. I have to panels in my JFrame that I need to align (One to the left, one to the right) and a few buttons in one of the panels that I need to be centered in the panel. Here is my code.
package application;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.io.*;
import java.nio.*;
import java.util.*;
public class Main extends JPanel
{
public static void main(String[] args)
{
//set the ui to the native OS
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}catch(ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e)
{
}
JFrame frame = new JFrame("Application Name");
Menu menu = new Menu();
JPanel iconPanel = new JPanel();
final JPanel grid = new JPanel(new FlowLayout());
JButton firewallButton = new JButton("Firewall");
JButton networkButton = new JButton("Network");
JButton printerButton = new JButton("Printer");
int iconPanelSizeX;
int iconPanelSizeY;
int gridSizeX;
int gridSizeY;
int gridPosition;
//frame setting
frame.setSize(800, 600);
frame.setMinimumSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
//add grid and iconPanel JPanels to the frame
frame.add(iconPanel);
iconPanel.add(firewallButton);
iconPanel.add(networkButton);
iconPanel.add(printerButton);
frame.add(grid);
//iconPanel settings
iconPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());
iconPanel.setBackground(Color.gray);
iconPanel.setLayout(new FlowLayout());
iconPanel.setSize(new Dimension(100, 600));
iconPanel.setVisible(true);
//grid setting
grid.setBackground(Color.red);
grid.setSize(new Dimension(700, 600));
grid.setVisible(true);
//this is for resizing components when the user resizes the window
int counter = 0;
while(counter == 0)
{
firewallButton.setSize(new Dimension(iconPanel.getWidth(), 50));
networkButton.setSize(new Dimension(iconPanel.getWidth(), 50));
printerButton.setSize(new Dimension(iconPanel.getWidth(), 50));
iconPanelSizeX = frame.getWidth() / 10;
iconPanelSizeY = frame.getHeight();
gridSizeX = (frame.getWidth() / 10) * 9;
gridSizeY = frame.getHeight();
iconPanel.setSize(new Dimension(iconPanelSizeX, iconPanelSizeY));
grid.setSize(new Dimension(gridSizeX, gridSizeY));
}
}
}
As you can see, the second JPanel (grid) doesn't line up with the right side of the frame, and the buttons inside iconTray don't center either. I realize these are both probably simple layout fixes, but I have no clue where to start.
For simple splitting of JFrame you can use GridLayout with 1 row and 2 colums.
frame.setLayout(new GridLayout(1,2,3,3)); //3,3 are gaps
frame.add(grid);
frame.add(iconPanel);
For centering components in panels you can use FlowLayout which is by default set on JPanels:
Doing it manualy:
grid.setLayout(new FlowLayout()); //Centered components
grid.setLayout(new FlowLayout(FlowLayout.LEFT,3,3)); //Components aligned to left
grid.setLayout(new FlowLayout(FlowLayout.RIGHT,3,3)); //Components aligned to right
This is how it looks:
Also, few observations:
Never call setXXXSize() methods for your components;
Try to avoid calling setSize(); for JFrame, call pack(); instead;
Call setVisible(true); in the end of code;
All your huge code can be "stripped" to this:
import javax.swing.*;
import java.awt.*;
public class Main extends JPanel
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Application Name");
JPanel iconPanel = new JPanel();
JPanel grid = new JPanel(new FlowLayout());
JButton firewallButton = new JButton("Firewall");
JButton networkButton = new JButton("Network");
JButton printerButton = new JButton("Printer");
frame.add(iconPanel);
iconPanel.add(firewallButton);
iconPanel.add(networkButton);
iconPanel.add(printerButton);
grid.setBackground(Color.GREEN);
frame.setLayout(new GridLayout(1,2,3,3));
frame.add(grid);
frame.add(iconPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
how to align buttons vertically?
This example uses a vertical Box in the WEST area of the frame's default BorderLayout:
import java.awt.*;
import javax.swing.*;
/** #see http://stackoverflow.com/a/14927280/230513 */
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
display();
}
});
}
private static void display() throws HeadlessException {
JFrame frame = new JFrame("Application Name");
JButton firewallButton = new JButton("Firewall");
JButton networkButton = new JButton("Network");
JButton printerButton = new JButton("Printer");
//iconPanel settings
Box iconPanel = new Box(BoxLayout.Y_AXIS);
iconPanel.add(firewallButton);
iconPanel.add(networkButton);
iconPanel.add(printerButton);
iconPanel.setBackground(Color.gray);
iconPanel.setVisible(true);
frame.add(iconPanel, BorderLayout.WEST);
//grid setting
JPanel grid = new JPanel() {
#Override
// arbitrary placeholder size
public Dimension getPreferredSize() {
return new Dimension(320, 230);
}
};
grid.setBackground(Color.red);
frame.add(grid, BorderLayout.CENTER);
//frame setting
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I suggest you take some time going through A Visual Guide to Layout Managers. This will help you become familiar with the layout managers which are available with the Standard API. It takes some experience and hard work to figure out which of these is the right tool to get the exact look you want. Once you become comfortable with what is available from the Standard API, you should also look around for third-party Layout Manager APIs which provide other options.
I have to panels in my JFrame that I need to align (One to the left,
one to the right) and a few buttons in one of the panels that I need
to be centered in the panel. Here is my code.
I realize these are both probably simple layout fixes, but I have no
clue where to start.
Use more complex layout than simple FlowLayout which you actually using. I suggest to you use
GridBagLayout
BoxLayout
Check references here
In the code below, by calling setAlignmentX with Component.LEFT_ALIGNMENT I expected to get a left aligned label over a centered slider. For some reason the label is also centered, seemingly regardless of what value is passed to setAlignmentX.
What value must I pass to setAlignmentX to get it left aligned?
package myjava;
import java.awt.Component;
import java.awt.Container;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
public class LayoutTest {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("BoxLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create left aligned label over centered column
Container contentPane = frame.getContentPane();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
JLabel label = new JLabel("test");
label.setAlignmentX(Component.LEFT_ALIGNMENT);
contentPane.add(label);
contentPane.add(new JSlider());
frame.pack();
frame.setVisible(true);
}
});
}
}
Basically, you can't have different alignments in BoxLayout, from How To Use BoxLayout
In general, all the components controlled by a top-to-bottom BoxLayout
object should have the same X alignment.
Edit
Typically, it's not documented which default alignment a component type has (JSlider is centered by default, me incorrectly thought that a JLabel were centered while it is left-aligned ;-) One option is to keep a list somewhere (dooooh...), another is to simply force them all to the same alignment on adding.
Or use a third-party layoutManager, which doesn't have this rather unintuitve (for me) mix-in of layout and alignment.
BoxLayout have strange behavior. Try to use GridBagLayout instead:
https://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html
public class Aligment {
public static void main(String[] args) {
final JPanel root = new JPanel(new GridBagLayout());
root.setPreferredSize(new Dimension(500, 400));
root.add(new JLabel("LEFT"), new GridBagConstraints() {{
gridx = 0;
gridy = 0;
anchor = PAGE_START;
}});
root.add(new JLabel("CENTER"), new GridBagConstraints() {{
gridx = 1;
gridy = 1;
anchor = CENTER;
weightx = 1.0; // fill Width
}});
root.add(new JLabel("RIGHT"), new GridBagConstraints() {{
gridx = 2;
gridy = 2;
anchor = LINE_END;
}});
// hack: Push all rows to Top
root.add(Box.createVerticalGlue(), new GridBagConstraints() {{
gridx = 0;
gridy = 3;
weighty = 1.0; // fill Height
}});
new JFrame() {
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setContentPane(root);
pack();
setLocationRelativeTo(null);;
}
}.setVisible(true);
}
}
I have a JScrollPane with a very high JPanel inside, that is changed dynamically, items being appended at its end. What I want, is to scroll to the bottom of aforementioned JScrollPane in order for the newly appended items to be visible instantly on addition (they are not appended to the scroll pane directly, but to its JPanel, and are private objects, so cannot be referenced.
How can I simply have that scroll pane scroll to the very bottom?
Thanks in advance!
JComponent.scrollRectToVisible(Rectangle). Call that on the JPanel instance.
E.G.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ScrollToNewLabel {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JPanel gui = new JPanel(new BorderLayout(3,3));
final JPanel panel = new JPanel(new GridLayout(0,1));
JScrollPane scroll = new JScrollPane(panel);
scroll.setPreferredSize(new Dimension(80,100));
gui.add(scroll, BorderLayout.CENTER);
JButton addLabel = new JButton("Add Label");
gui.add(addLabel, BorderLayout.NORTH);
ActionListener listener = new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent ae) {
panel.add(new JLabel("Label " + ++counter));
panel.revalidate();
int height = (int)panel.getPreferredSize().getHeight();
Rectangle rect = new Rectangle(0,height,10,10);
panel.scrollRectToVisible(rect);
}
};
addLabel.addActionListener(listener);
JOptionPane.showMessageDialog(null, gui);
}
});
}
}
Screen shot
E.G. 2
This e.g. is based on Vincent's answer, to use JScrollPane.getVerticalScrollBar().setValue(height). Where height is the preferred height of the panel in pixels.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ScrollToNewLabel {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JPanel gui = new JPanel(new BorderLayout(3,3));
final JPanel panel = new JPanel(new GridLayout(0,1));
final JScrollPane scroll = new JScrollPane(panel);
scroll.setPreferredSize(new Dimension(80,100));
gui.add(scroll, BorderLayout.CENTER);
JButton addLabel = new JButton("Add Label");
gui.add(addLabel, BorderLayout.NORTH);
ActionListener listener = new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent ae) {
panel.add(new JLabel("Label " + ++counter));
panel.revalidate();
int height = (int)panel.getPreferredSize().getHeight();
scroll.getVerticalScrollBar().setValue(height);
}
};
addLabel.addActionListener(listener);
JOptionPane.showMessageDialog(null, gui);
}
});
}
}
scrollRectToVisible(...) and scrollBar.setValue(...) are the general solutions.
You may be interested in Scrolling a Form which ensures that when you tab to a component the form will scroll automatically to make sure the the component will be visible in the scrollpane. Behind the scenes it uses scrollRectToVisible().
A simple way to move the scrollbar all the way to the bottom is to set its value to 100 like this:
scroll.getVerticalScrollBar().setValue(100);
This causes it to move to the bottom of the viewport. You can add this after the component is added to the panel.
This is how I scroll down programmatically.
I like how it scrolls to the bottom rather smoothly instead of jumping there immediately.
/**
* Scrolls a {#code scrollPane} to its bottom.
*
* #param scrollPane
* the scrollPane that we want to scroll all the way down
*
*/
private void scrollDown(JScrollPane scrollPane) {
JScrollBar verticalBar = scrollPane.getVerticalScrollBar();
int currentScrollValue = verticalBar.getValue();
int previousScrollValue = -1;
while (currentScrollValue != previousScrollValue) {
// Scroll down a bit
int downDirection = 1;
int amountToScroll = verticalBar.getUnitIncrement(downDirection);
verticalBar.setValue(currentScrollValue + amountToScroll);
previousScrollValue = currentScrollValue;
currentScrollValue = verticalBar.getValue();
}
}