I have a JPanel, that lists a set of Jlabels. I would like to make the width of each label wide as the panel's size. So it will be wide but but text stays in the left. I am using BoxLayout to list labels.
Here is the code:
public class JavaApplication78 {
JFrame frame;
JPanel panel, labelsPanel;
JLabel label;
ArrayList<String> names = new ArrayList<String>();
ArrayList<JLabel> labelsArray = new ArrayList<JLabel>();
Border paddingBorder = BorderFactory.createEmptyBorder(10,10,10,10);
Border border = BorderFactory.createLineBorder(Color.BLUE);
JScrollPane labelsScroll;
public JavaApplication78(){
frame = new JFrame();
panel = new JPanel(new BorderLayout());
names.add(".mp3");names.add(".html");names.add(".jpeg");names.add(".mp4");names.add(".pdf");
labelsPanel = new JPanel();
labelsPanel.setLayout(new BoxLayout(labelsPanel, BoxLayout.PAGE_AXIS));
labelsScroll = new JScrollPane(labelsPanel);
for(String format : names){
label = new JLabel(format);
//icon
labelsArray.add(label);
labelsPanel.add(label);
label.setBorder(BorderFactory.createCompoundBorder(border,paddingBorder));
}
panel.add(labelsScroll, BorderLayout.CENTER);
frame.add(panel);
frame.setSize(200, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JavaApplication78();
}
}
Currently I could give a border around each JLabel. The height of labels are ok, but width need to be as wide as the parent panel.
Any idea ?
You could use a GridBagLayout...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
public class JavaApplication78 {
JFrame frame;
JPanel panel, labelsPanel;
JLabel label;
ArrayList<String> names = new ArrayList<String>();
ArrayList<JLabel> labelsArray = new ArrayList<JLabel>();
Border paddingBorder = BorderFactory.createEmptyBorder(10, 10, 10, 10);
Border border = BorderFactory.createLineBorder(Color.BLUE);
JScrollPane labelsScroll;
public JavaApplication78() {
frame = new JFrame();
panel = new JPanel(new BorderLayout());
names.add(".mp3");
names.add(".html");
names.add(".jpeg");
names.add(".mp4");
names.add(".pdf");
labelsPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
labelsScroll = new JScrollPane(labelsPanel);
for (String format : names) {
label = new JLabel(format);
//icon
labelsArray.add(label);
labelsPanel.add(label, gbc);
label.setBorder(BorderFactory.createCompoundBorder(border, paddingBorder));
}
panel.add(labelsScroll, BorderLayout.CENTER);
frame.add(panel);
frame.setSize(200, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
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) {
ex.printStackTrace();
}
new JavaApplication78();
}
});
}
}
Or a JList, but in cases like this, I tend to like using the VerticalLayout from the SwingLabs SwingX library
BoxLayout respects the maximum size of the component and for a JLabel the maximum size is equal to the preferred size.
You can override the getMaximumSize() method:
label = new JLabel(format)
{
#Override
public Dimension getMaximumSize()
{
Dimension d = super.getMaximumSize();
d.width = Integer.MAX_VALUE;
return d;
}
};
label.setHorizontalAlignment(JLabel.CENTER);
This will then allow the label to expand horizontally to fill the width of the panel.
Or if you wanted you could use the Relative Layout. It functions much like the BoxLayout but has a few additional features. The basic code would be:
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true );
JPanel labelsPanel = new JPanel( rl );
Use PreferredSize to set the dimension of the parent JPanel for your JLabels :
JLabel label = new JLabel() {
public Dimension getPreferredSize() {
return labelsPanel.getSize();
};
};
Unless you are obliged to use BoxLayout, maybe it's better to use GridBagLayout as suggested by #MadProgrammer in his comment.
EDIT :
You could also take a look at MigLayout. Adding components with it is very simple :
labelsPanel.setLayout(new MigLayout());
labelsPanel.add(label, "span") // span to take the whole row width.
Related
I'm starting with swing, I have some questions about how to align elements within a GridBagLayout, I'm not sure either whether this is the correct approach, please advice.
I have the below code
import javax.swing.*;
import java.awt.*;
public class App {
public void start() {
JPanel mainPanel = new JPanel(new FlowLayout());
mainPanel.setBorder(BorderFactory.createLineBorder(Color.CYAN, 20));
//buttons for initial options
JButton button1 = new JButton("This is option A");
JButton button2 = new JButton("option B");
JButton button3 = new JButton("Another text");
JPanel second = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
second.setBackground(Color.GREEN);
second.add(button1, gbc);
second.add(button2, gbc);
second.add(button3, gbc);
mainPanel.add(second, BorderLayout.CENTER);
//frame configuration
JFrame frame = new JFrame();
frame.setContentPane(mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setResizable(false);
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
SwingUtilities.invokeLater(() -> new App().start());
}
}
My goal is to produce the following output:
So far I have tried with BoxLayout with vertical alignment and it works but the problem is that it overwrites the preferred sized of the buttons and I want them all to be the same width.
Also, I tried with GridLayout and BorderLayout adding the elements to NORTH, CENTER, and SOUTH but the sizes of the buttons change.
What is the recommended way to center the elements but keeping their dimensions?
I would nest layouts:
A JPanel that holds the buttons and uses a new GridLayout(0, 1, 0, vGap) -- a grid that holds one column and variable number of rows, with a vGap gap between buttons.
Add that JPanel into another JPanel that uses GridBagLayout, and add it in a default way (no GridBagConstraints) which will center the first JPanel into the second. This would obviously have to somehow be the size desired. This can be achieved by either
overriding getPreferredSize() in a sensible way
Calling setPreferredSize(new Dimension(someWidth, someHeight)) -- this isn't quite as "clean"
Giving this a border, specifically a BorderFactor.EmptyBorder(gap, gap, gap, gap) where gap is the size of the border around the JPanel...
Done.
Test code that uses the GridBagLayout:
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.*;
public class ButtonLayout extends JPanel {
public static final int MY_WIDTH = 750;
public static final int MY_HEIGHT = 500;
private static final float BTN_SIZE = 24f;
private String[] buttonTexts = {"This is Option A", "Option B",
"Something Else Entirely"};
public ButtonLayout() {
int colGap = 20;
JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 0, colGap));
for (String btnText : buttonTexts) {
JButton button = new JButton(btnText);
// set first letter of text as mnemonic (alt-char shortcut)
int mnemonic = (int) btnText.charAt(0);
button.setMnemonic(mnemonic);
// make button bigger by increasing its font
button.setFont(button.getFont().deriveFont(BTN_SIZE));
// add to the GridLayout-using JPanel
buttonPanel.add(button);
}
// set layout of main panel to GridBag
setLayout(new GridBagLayout());
// add the button panel in a "default" manner (no constraints)
// which centers this panel
add(buttonPanel);
}
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
int width = Math.max(MY_WIDTH, superSize.width);
int height = Math.max(MY_HEIGHT, superSize.height);
return new Dimension(width, height);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
ButtonLayout mainPanel = new ButtonLayout();
JFrame frame = new JFrame("ButtonLayout");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Example 2 that uses EmptyBorder:
import java.awt.GridLayout;
import javax.swing.*;
#SuppressWarnings("serial")
public class ButtonLayout extends JPanel {
public static final int MY_WIDTH = 750;
public static final int MY_HEIGHT = 500;
private static final float BTN_SIZE = 24f;
private String[] buttonTexts = {"This is Option A", "Option B",
"Something Else Entirely"};
public ButtonLayout() {
int colGap = 20;
JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 0, colGap));
for (String btnText : buttonTexts) {
JButton button = new JButton(btnText);
// set first letter of text as mnemonic (alt-char shortcut)
int mnemonic = (int) btnText.charAt(0);
button.setMnemonic(mnemonic);
// make button bigger by increasing its font
button.setFont(button.getFont().deriveFont(BTN_SIZE));
// add to the GridLayout-using JPanel
buttonPanel.add(button);
}
add(buttonPanel);
int top = 60;
int left = top;
int bottom = 2 * top;
int right = left;
setBorder(BorderFactory.createEmptyBorder(top, left, bottom, right));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
ButtonLayout mainPanel = new ButtonLayout();
JFrame frame = new JFrame("ButtonLayout");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I'm not sure I completely understand the issue, but if you want to vertically align the buttons, BUT allow them to keep their preferred size, just don't provide any kind of fill constraint, for example
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SoTest {
public static void main(String[] args) {
new SoTest();
}
public SoTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JButton("This is option A"), gbc);
add(new JButton("Option B"), gbc);
add(new JButton("Another button"), gbc);
}
}
}
Or, if you want them to have the same width, use a fill constraint
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SoTest {
public static void main(String[] args) {
new SoTest();
}
public SoTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.BOTH;
add(new JButton("This is option A"), gbc);
add(new JButton("Option B"), gbc);
add(new JButton("Another button"), gbc);
}
}
}
If you want to mix a more complex layout, then you should consider making use of compound layouts
But wait, there's no outline...
So, a number of ways you "might" be able to do this, for example, you could use a CompoundBorder....
setBorder(new CompoundBorder(new LineBorder(Color.CYAN, 16), new EmptyBorder(32, 32, 32, 32)));
But the devil is in the detail
I'm currently working on a media player for java, and with the power of VLCJ I was working on implementing an equalizer adjust window. There will be 11 vertical sliders with a JLabel underneath them indicating the hZ band and the dB level of the band. However, the slider keeps adding a huge gap between itself and the JLabel. I tried stacking just two JLabels on top of each other and there's barely a gap at all. My code is below. (The return equalizer stuff hasn't been implemented yet. I just want a basic UI working before I start adding in the functionality)
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import uk.co.caprica.vlcj.player.Equalizer;
public class VideoEQFrame {
public VideoEQFrame() {
//constructor
}
public Equalizer show() {
JFrame frame = new JFrame("Effects");
JPanel panel = new JPanel();
JPanel sliders= new JPanel();
JPanel gainObjects = new JPanel(new GridLayout(2, 0, 2, 0));
JSlider gainS = new JSlider(JSlider.VERTICAL, -12, 12, 0);
gainS.setMajorTickSpacing(2);
gainS.setPaintTicks(true);
gainS.setToolTipText("Adjust the gain");
JLabel gainL = new JLabel("Text");
gainObjects.add(gainS);
gainObjects.add(gainL);
sliders.add(gainObjects);
panel.add(sliders);
frame.add(panel);
frame.setSize(new Dimension(600, 300));
//frame.setResizable(false);
frame.setVisible(true);
Equalizer eq = new Equalizer(0);
return eq;
}
}
You are using GridLayout to lay the slider and the text label. That means that they will both occupy the same height. So because the slider has bigger height, the height of the label also adjusts to this height. Try using another LayoutManager like BorderLayout, like so:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
public class VideoEQFrame {
public VideoEQFrame() {
//constructor
}
public void show() {
JFrame frame = new JFrame("Effects");
JPanel panel = new JPanel();
JPanel sliders= new JPanel();
JPanel gainObjects = new JPanel(new BorderLayout());
JSlider gainS = new JSlider(JSlider.VERTICAL, -12, 12, 0);
gainS.setMajorTickSpacing(2);
gainS.setPaintTicks(true);
gainS.setToolTipText("Adjust the gain");
JLabel gainL = new JLabel("Text");
gainObjects.add(gainS, BorderLayout.CENTER);
gainObjects.add(gainL, BorderLayout.PAGE_END);
sliders.add(gainObjects);
panel.add(sliders);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
//frame.setResizable(false);
frame.setVisible(true);
// Equalizer eq = new Equalizer(0);
// return eq;
}
public static void main(final String[] args) {
new VideoEQFrame().show();
}
}
So for a school project I made a grade book in java. When creating the gui I used hardcoded values in the setBounds() methods. Now this worked when I had a 1024×768 screen resolution it looked alright, but when I got a new laptop and it had a 4k screen it looked super small when I ran the program.
So my question would be is there a way to dynamically change the size of the Jframe and all of the associated objects on the frame so it matches the resolution of the screen?
I know that you can get the screen resolution from this
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getHeight();
but I do not know what would be the best way to do this.
Taking your approach as example and taking this answer and this tutorial as base, here you have the clues:
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
public class Q1 extends JFrame {
public static void main(String[] args) {
Q1 frame = new Q1();
frame.setSize(300, 300);
frame.setMinimumSize(new Dimension(300, 300));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public Q1() {
this.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// This is only called when the user releases the mouse button.
System.out.println("componentResized");
}
});
}
#Override
public void validate() {
resize();
super.validate();
};
private void resize() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getHeight();
System.out.println(width + "," + height);
}
}
This will print the size of the screen when your resize the frame, so you just need to add an if/else in the resize method to make frame bigger
OUTPUT
1366.0,768.0
1366.0,768.0
componentResized
1366.0,768.0
1366.0,768.0
componentResized
1366.0,768.0
1366.0,768.0
componentResized
A layout manager is an object that implements the LayoutManager
interface* and determines the size and position of the components
within a container. Although components can provide size and alignment
hints, a container's layout manager has the final say on the size and
position of the components within the container.
See the example I found using layout managers.Hope you get some idea.THe original author is here Set a layout manager like BorderLayout and then define more specifically, where your panel should go: like
MainPanel mainPanel = new MainPanel();
JFrame mainFrame = new JFrame();
mainFrame.setLayout(new BorderLayout());
mainFrame.add(mainPanel, BorderLayout.CENTER);
mainFrame.pack();
mainFrame.setVisible(true);
puts the panel into the center area of the frame and lets it grow automatically when resizing the frame.See below example for full usage:
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestLayoutManagers {
private JPanel northFlowLayoutPanel;
private JPanel southBorderLayoutPanel;
private JPanel centerGridBagLayoutPanel;
private JPanel westBoxLayoutPanel;
private JPanel eastGridLayoutPanel;
private final JButton northButton = new JButton("North Button");
private final JButton southButton = new JButton("South Button");
private final JButton centerButton = new JButton("Center Button");
private final JButton eastButton = new JButton("East Button");
private final JButton westButton = new JButton("West Button");
public TestLayoutManagers() {
northFlowLayoutPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
southBorderLayoutPanel = new JPanel(new BorderLayout());
centerGridBagLayoutPanel = new JPanel(new GridBagLayout());
eastGridLayoutPanel = new JPanel(new GridLayout(1, 1));
Box box = Box.createHorizontalBox();
westBoxLayoutPanel = new JPanel();
northFlowLayoutPanel.add(northButton);
northFlowLayoutPanel.setBorder(BorderFactory.createTitledBorder("Flow Layout"));
southBorderLayoutPanel.add(southButton);
southBorderLayoutPanel.setBorder(BorderFactory.createTitledBorder("Border Layout"));
centerGridBagLayoutPanel.add(centerButton);
centerGridBagLayoutPanel.setBorder(BorderFactory.createTitledBorder("GridBag Layout"));
eastGridLayoutPanel.add(eastButton);
eastGridLayoutPanel.setBorder(BorderFactory.createTitledBorder("Grid Layout"));
box.add(westButton);
westBoxLayoutPanel.add(box);
westBoxLayoutPanel.setBorder(BorderFactory.createTitledBorder("Box Layout"));
JFrame frame = new JFrame("Test Layout Managers");
frame.setLayout(new BorderLayout()); // This is the deafault layout
frame.add(northFlowLayoutPanel, BorderLayout.PAGE_START);
frame.add(southBorderLayoutPanel, BorderLayout.PAGE_END);
frame.add(centerGridBagLayoutPanel, BorderLayout.CENTER);
frame.add(eastGridLayoutPanel, BorderLayout.LINE_END);
frame.add(westBoxLayoutPanel, BorderLayout.LINE_START);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestLayoutManagers testLayoutManagers
= new TestLayoutManagers();
}
});
}
}
I am creating a bespoke table that I want to scroll up and down, so I am using JScrollPane.
My problem is that each time I add another row in the table it is adding it to the side of the last one, rather than below the last line, which is where I want it to be. Eg it is adding columns to the table rather than rows.
The number of rows that need to be added is uncertain and depends on how many entries there are in the table.
I know I can use a table but for the purposes of this exercise I want to do it this way.
Below is an MCVE version of my code that demonstrates the problem. I left the colour change in so the fields are more visible:
public class Customers {
public static JFrame frame = new JFrame();
public static void frameGui(JPanel panel, String name){
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.setSize(1200,500);
frame.setVisible(true);
}
public static void ScrollCustomersGui(){
JPanel Table = new JPanel();
Table.add(customersTableHeadings(Table));
Table.add(customersTableHeadings(Table));
Table.add(customersTableHeadings(Table));
JScrollPane scroll = new JScrollPane(Table);
JPanel All = new JPanel(new BorderLayout());
All.add(scroll);
frameGui(All, "Customers");
}
public static JPanel customersTableHeadings(JPanel panel){
FlowLayout FlowCustTable = (FlowLayout)panel.getLayout();
FlowCustTable.setHgap(0);
JPanel customersTable = new JPanel(FlowCustTable);
JTextField custid = new JTextField("ONE");
custid.setPreferredSize(new Dimension(500, 50));
custid.setBackground(Color.CYAN);
customersTable.add(custid);
return customersTable;
}
}
Use vertical BoxLayout here is the modified code :
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
public class Customers
{
public static JFrame frame = new JFrame();
public static void frameGui(JPanel panel, String name)
{
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.setSize(1200, 500);
frame.setVisible(true);
}
public static void ScrollCustomersGui()
{
Box box = Box.createVerticalBox();
//JPanel Table = new JPanel();
//Table.setLayout(new BoxLayout(Table, BoxLayout.Y_AXIS));
box.add(customersTableHeadings());
box.add(customersTableHeadings());
box.add(customersTableHeadings());
JScrollPane scroll = new JScrollPane(box);
JPanel All = new JPanel(new BorderLayout());
All.add(scroll);
frameGui(All, "Customers");
}
public static JPanel customersTableHeadings()
{
// FlowLayout FlowCustTable = (FlowLayout) panel.getLayout();
// FlowCustTable.setHgap(0);
JPanel customersTable = new JPanel();
customersTable.setMaximumSize(new Dimension(550, 60));
JTextField custid = new JTextField("ONE");
custid.setPreferredSize(new Dimension(500, 50));
custid.setBackground(Color.CYAN);
customersTable.add(custid);
return customersTable;
}
public static void main(String[] args)
{
ScrollCustomersGui();
}
}
you are using a FlowLayout which will not work for your case you can use a BoxLayout check it here, you can also use a GridBagLayout but it is complex :)
I've found myself writing up quite a few programs recently which all need to display some collection of data. So far the best looking approach I've thought of is make small JPanels which contain data on each item in the collection and put them all in a big JPanel which I then put in a JScrollPane. It works and looks just as intended but there's one issue: I can't seem to get the smaller JPanels to start at the top of the bigger JPanel.
The problem is only apparent when I've got a small number of small JPanels (green) added into the bigger JPanel (red).
Described below is the method I used to produce the above and I'd like to know if there's a better way I could do it (where the list starts at the top like it should):
I created a class which extends JPanel and in it add all data I want to display. We'll call it "SmallPanel.java". I don't set the size of it (that comes later).
In my main window's class (which extends JFrame):
private JScrollPane scrollPane;
private JPanel panel;
...
scrollPane = new JScrollPane();
getContentPane().add(scrollPane);
panel = new JPanel();
panel.setLayout(new GridBagLayout());
scrollPane.setViewportView(panel);
...
private void addPanel()
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = panel.getComponentCount(); //The new JPanel's place in the list
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START; //I thought this would do it
gbc.ipady = 130; //Set the panel's height, the width will get set to that of the container JPanel (which is what I want since I'd like my JFrames to be resizable)
gbc.insets = new Insets(2, 0, 2, 0); //Separation between JPanels in the list
gbc.weightx = 1.0;
SmallPanel smallPanel = new SmallPanel();
panel.add(smallPanel, gbc);
panel.revalidate();
panel.invalidate();
panel.repaint(); //Better safe than peeved
}
Call the addPanel() method every time I want to add a panel.
EDIT
Final solution (based on MadProgrammer's answer below):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.BevelBorder;
public class ListPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private JPanel fillerPanel;
private ArrayList<JPanel> panels;
public ListPanel(List<JPanel> panels, int height)
{
this(panels, height, new Insets(2, 0, 2, 0));
}
public ListPanel(List<JPanel> panels, int height, Insets insets)
{
this();
for (JPanel panel : panels)
addPanel(panel, height, insets);
}
public ListPanel()
{
super();
this.fillerPanel = new JPanel();
this.fillerPanel.setMinimumSize(new Dimension(0, 0));
this.panels = new ArrayList<JPanel>();
setLayout(new GridBagLayout());
}
public void addPanel(JPanel p, int height)
{
addPanel(p, height, new Insets(2, 0, 2, 0));
}
public void addPanel(JPanel p, int height, Insets insets)
{
super.remove(fillerPanel);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START;
gbc.ipady = height;
gbc.insets = insets;
gbc.weightx = 1.0;
panels.add(p);
add(p, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1.0;
add(fillerPanel, gbc);
revalidate();
invalidate();
repaint();
}
public void removePanel(JPanel p)
{
removePanel(panels.indexOf(p));
}
public void removePanel(int i)
{
super.remove(i);
panels.remove(i);
revalidate();
invalidate();
repaint();
}
public ArrayList<JPanel> getPanels()
{
return this.panels;
}
public static void main(String[] args)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setMinimumSize(new Dimension(500, 500));
f.setLocationRelativeTo(null);
f.getContentPane().setLayout(new BorderLayout());
final ListPanel listPanel = new ListPanel();
for (int i = 1; i <= 10; i++)
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
JButton btnAdd = new JButton("Add");
btnAdd.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
}
});
JButton btnRemove = new JButton("Remove");
btnRemove.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.removePanel(0);
}
});
f.getContentPane().add(btnRemove, BorderLayout.NORTH);
f.getContentPane().add(btnAdd, BorderLayout.SOUTH);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setViewportView(listPanel);
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.setVisible(true);
}
public static JPanel getRandomJPanel()
{
JPanel panel = new JPanel();
panel.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
panel.add(new JLabel("This is a randomly sized JPanel"));
panel.setBackground(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat()));
return panel;
}
}
The best solution I've found is to use VerticalLayout from the SwingLabs SwingX (which can be downloaded from here) libraries.
You "could" use a GridBagLayout with an invisible component positioned at the end, whose weighty property is set to 1, but this is a lot more additional work to manage, as you need to keep updating the x/y positions of all the components to keep it in place...
Updated with GridBagLayout example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class VerticalLayoutExample {
public static void main(String[] args) {
new VerticalLayoutExample();
}
public VerticalLayoutExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
filler = new JPanel();
filler.setOpaque(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weighty = 1;
gbc.gridy = 0;
add(filler, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel("Hello"));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = y++;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 4, 4, 4);
add(panel, gbc);
GridBagLayout gbl = ((GridBagLayout)getLayout());
gbc = gbl.getConstraints(filler);
gbc.gridy = y++;
gbl.setConstraints(filler, gbc);
revalidate();
repaint();
}
}
}
This is just a concept. As camickr has pointed out, so long as you know the last component, you can adjust the GridBagConstraints of the component so that the last component which is in the list has the weighty of 1 instead...
As you can, you can override some of the things GridBagLayout does, for example, instead of using the preferred size of the panel, I've asked GridBagLayout to make it fill the HORIZONTAL width of the parent container...
You can use a vertical BoxLayout.
Just make sure the maximum size of the panel is equal to the preferred size so the panel doesn't grow.
Edit:
Since your class already has a custom panel all you need to do is override the getMaximumSize() method to return an appropriate value. Something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class VerticalLayoutExample2 {
public static void main(String[] args) {
new VerticalLayoutExample2();
}
public VerticalLayoutExample2() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder( new EmptyBorder(4, 4, 4, 4) );
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
SmallPanel panel = new SmallPanel();
panel.setLayout( new GridBagLayout() );
panel.add(new JLabel("Hello"));
add(panel);
add(Box.createVerticalStrut(4));
revalidate();
repaint();
}
}
static class SmallPanel extends JPanel
{
#Override
public Dimension getMaximumSize()
{
Dimension preferred = super.getPreferredSize();
Dimension maximum = super.getMaximumSize();
maximum.height = preferred.height;
return maximum;
}
}
}
I know you mentioned you don't want to use a lib, but you can also look at Relative Layout. It is only a single class. It can easily mimic a BoxLayout but is easier to use because you don't need to override the getMaximumSize() method or add a Box component to the panel to give the vertical spacing.
You would set it as the layout of your panel as follow:
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true ); // fills components horizontally
rl.setGap(4); // vertical gap between panels
yourPanel.setLayout(rl);
yourPanel.add( new SmallPanel(...) );
yourPanel.add( new SmallPanel(...) );