I am trying to make a JFrame for displaying the data that my beta-testers are to send me (this part I have already done), and a JTabbedPane for the charts (for their use). //I am hoping for something that, ignoring stuff like the look-and-feel (and coloring; I was too lazy to color the picture all the way), would look something like this:
. //I would have to use CardLayout for the JComboBox to display a different table and a different JTabbedPane for the newly-selected mode; that means two tables and two JTabbedPanes.
I have tried to make a small (extremely-simplified!) example of this setup, a JFrame with only a (very simple!) JTabbedPane and a small JTable. The example works if I give my JPanel (that houses both components) a BorderLayout, but as soon as I give it a GridLayout (or a GridBagLayout //the very Layout I will end up using), only the last component displays no matter what I try! Why is this?
If it would help any (it might, given how much of a newbie I am to this), here is the example code:
JPanelLayoutTest.java
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
public class JPanelLayoutTest extends JFrame {
private JPanel aPanel;
private JTabbedPane tabbedPane;
private JTable someTable;
public JPanelLayoutTest(String title) throws HeadlessException {
super(title);
aPanel = setupPanel();
}
private JPanel setupPanel()
{
GridBagLayout gridBag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
//making the panel have two columns and one row
JPanel panel = new JPanel(gridBag);
//add someTable to top
someTable = new JTable(new SampleTableModel());
/*JPanel somePanel = new JPanel();
somePanel.add(new JScrollPane(someTable));*/
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridheight = 1;
constraints.gridwidth = 1;
gridBag.setConstraints(new JScrollPane(someTable), constraints);
add(new JScrollPane(someTable));
//add tabbedPane to bottom
tabbedPane = new JTabbedPane();
tabbedPane.addTab("some component", new JLabel("some text"));
/*JPanel someOtherPanel = new JPanel();
someOtherPanel.add(tabbedPane);*/
constraints.gridy = 1;
gridBag.setConstraints(tabbedPane, constraints);
add(tabbedPane);
//add(tabbedPane);
return panel;
}
public void turnOnWindow()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
//setSize(400,200);
pack();
}
public static void main(String[] args) {
JPanelLayoutTest frame = new JPanelLayoutTest("JPanel Layout Test");
frame.turnOnWindow();
}
}
//Pardon the indentation; I wish this forum had support for the [code][/code] tag
SampleTableModel.java
import javax.swing.table.AbstractTableModel;
public class SampleTableModel extends AbstractTableModel {
//declaring the static arrays that will be the data for the table
private final String[] columnNames = {"Account", "Full Name", "Balance"};
private final int[] acctNumbers = {1000443749, 190238420, 928355};
private final String[] fullNames = {"Mike Warren", "Jack Smith", "Sarah Brown"};
private final double[] acctBalances = {193.38, 289.28, 21034.29};
public SampleTableModel()
{
// I do nothing in here; there is no reason to.
}
#Override
public int getColumnCount() { return columnNames.length; }
#Override
public int getRowCount() { return acctNumbers.length; }
#Override
public Object getValueAt(int row, int column) {
switch (column)
{
case 0:
return new Integer(acctNumbers[row]);
case 1:
return fullNames[row];
case 2:
return new Double(acctBalances[row]);
default:
return null; //there should be no way the code would ever reach here
}
}
#Override
public String getColumnName(int index) { return columnNames[index]; }
//Again, forgive my horrible indentation near the end here...
}
Instead of:
private JPanel setupPanel()
{
GridBagLayout gridBag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
//making the panel have two columns and one row
JPanel panel = new JPanel(gridBag);
//add someTable to top
someTable = new JTable(new SampleTableModel());
/*JPanel somePanel = new JPanel();
somePanel.add(new JScrollPane(someTable));*/
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridheight = 1;
constraints.gridwidth = 1;
gridBag.setConstraints(new JScrollPane(someTable), constraints);
add(new JScrollPane(someTable));
//add tabbedPane to bottom
tabbedPane = new JTabbedPane();
tabbedPane.addTab("some component", new JLabel("some text"));
/*JPanel someOtherPanel = new JPanel();
someOtherPanel.add(tabbedPane);*/
constraints.gridy = 1;
gridBag.setConstraints(tabbedPane, constraints);
add(tabbedPane);
//add(tabbedPane);
return panel;
}
I think you will have better luck building your panel as an extention to the JPanel:
EDIT: Forgot to mention that this code is very reusable and allows to to pass in other objects into the JPanel...save you a ton of work as it allows you to overload constructors for different scenarios.
//You can call new objects such as:
new MyPanel(data, otherComponent);
public class MyPanel extends JPanel{
String data;
public MyPanel(String someData, SomeOtherClass aClass){
this.data = someData;
setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
JPanel panel = new JPanel(gridBag);
//New addition
constraints.gridx = 0;
constraints.gridy = 0;
//add components to the JPanel with your grid (constraints)
add(new JScrollPane(someTable), constraints)
//New addition
constraints.gridx = 0;
constraints.gridy = 1;
//add components to the JPanel with your grid (constraints)
add(tabbedPane , constraints);
}
public String getData(){
return this.data;
}
}
This follows the MVC pattern (which i recommend looking into if you plan on designing more swing). I know it really helped me!
Your Problem
When using a GridBagLayout (and many other layouts), you need to pass the constraints you wish to use for a particular component, otherwise the layout manager will use its default values.
For example, you are doing...
gridBag.setConstraints(new JScrollPane(someTable), constraints);
add(new JScrollPane(someTable));
But the component you are adding is not the component your applied the constraints to. Instead, do something more like...
add(new JScrollPane(someTable), constraints);
A (possible) better solution
Break your UI down into sections. Each section has its own unique requirements (I see three sections).
I see a GridLayout as the base layout, onto which you want to add three additional panels, which represent three sections of your UI.
This commonly known as compound layouts as you building a series of layouts to produce your final result
Related
i have a problem with refreshing the values of my gridlayout.
So, i have a JPanel in a JFrame and in that JPanel , once i entered two values(one for rows and one for columns) and then by clicking on validate, i get a GridLayout with the previous values of JButtons.
So for exemple if I enter (2,2) i get a GridLayout of 4 JButtons and in each JButton i have an image.
So my problem here is, every time i wanna refresh the GridLayout by changing the values, it doesn’t work, the GridLayout doesn’t change, or if it change, the JButtons are inclickable.
I feel like every time i click on Validate, a new GridLayout is created on my JPanel, but the first one is still there.
I will upload two pictures, one with the normal functioning (entering values first time), and the second with the bug (entering new values).
Thanks guys.
First values
Second values
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class PagePrincipal extends JFrame implements ActionListener {
JButton Valider;
JTextField Columns;
JTextField Rows;
ArrayList<JButton> butt;
public PagePrincipal(){
getContentPane().setLayout(null); //this is not the panel that contains the GridLayout
Columns = new JTextField();
Columns.setBounds(219, 35, 197, 57);
getContentPane().add(Columns);
Columns.setColumns(10);
Rows = new JTextField();
Rows.setBounds(451, 35, 226, 57);
getContentPane().add(Rows);
Rows.setColumns(10);
Valider = new JButton();
Valider.setBackground(new Color(65, 179, 163));
Valider.setForeground(Color.WHITE);
Valider.setFont(new Font("Bookman Old Style", Font.BOLD, 20));
Valider.setBounds(704, 15, 268, 81);
Valider.setText("Validation");
Valider.addActionListener(this);
this.add(Valider);
this.setResizable(true);
this.setVisible(true);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() == Valider) {
int NbRows= Integer.parseInt(Rows.getText());
int NbColumns=Integer.parseInt(Columns.getText());
JButton button[] = new JButton[NbRows*NbColumns];
butt = new ArrayList<>();
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel botPanel = new JPanel(); //this is the panel that contains the GridLayout
botPanel.setBounds(100, 200, 1000, 400);
this.add(botPanel);
botPanel.setLayout(new GridLayout(NbRows,NbColumns));
for (int i=0; i<NbRows*NbColumns; i++){
button[i]=new JButton();
botPanel.add(button[i]);
butt.add(button[i]);
}
this.setVisible(true);
}
}
}
Again, avoid null layouts if at all possible, since they force you to create rigid, inflexible, hard to maintain GUI's that might work on one platform only. Instead, nest JPanels, each using its own layout to help create GUI's that look good, are flexible, extendable and that work.
Also, when changing components held within a container, call revalidate() and repaint() on the container after making the changes. For example, the following GUI:
Is created with the following code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class PagePrincipal2 extends JPanel {
public static final int MAX_ROWS = 40;
public static final int MAX_COLS = 12;
private JButton validatorButton = new JButton("Validate");
private JSpinner columnsSpinner = new JSpinner(new SpinnerNumberModel(2, 1, MAX_COLS, 1));
private JSpinner rowsSpinner = new JSpinner(new SpinnerNumberModel(2, 1, MAX_ROWS, 1));
private List<JButton> buttonsList = new ArrayList<>();
private JPanel gridPanel = new JPanel();
public PagePrincipal2() {
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Columns:"));
topPanel.add(columnsSpinner);
topPanel.add(Box.createHorizontalStrut(10));
topPanel.add(new JLabel("Rows:"));
topPanel.add(rowsSpinner);
topPanel.add(Box.createHorizontalStrut(10));
topPanel.add(validatorButton);
JScrollPane scrollPane = new JScrollPane(gridPanel);
int gridWidth = 1000;
int gridHeight = 600;
scrollPane.setPreferredSize(new Dimension(gridWidth, gridHeight));
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
validatorButton.addActionListener(e -> validateGrid());
}
private void validateGrid() {
int nbRows = (int) rowsSpinner.getValue();
int nbColumns = (int) columnsSpinner.getValue();
gridPanel.removeAll();
buttonsList.clear();
gridPanel.setLayout(new GridLayout(nbRows, nbColumns));
for (int i = 0; i < nbRows * nbColumns; i++) {
int column = i % nbColumns;
int row = i / nbColumns;
String text = String.format("[%02d, %02d]", column, row);
JButton button = new JButton(text);
button.addActionListener(e -> gridButtonAction(column, row));
buttonsList.add(button);
gridPanel.add(button);
}
gridPanel.revalidate();
gridPanel.repaint();
}
private void gridButtonAction(int column, int row) {
String message = String.format("Button pressed: [%02d, %02d]", column, row);
String title = "Grid Button Press";
int type = JOptionPane.INFORMATION_MESSAGE;
JOptionPane.showMessageDialog(this, message, title, type);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
PagePrincipal2 mainPanel = new PagePrincipal2();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Note that the gridPanel, the one holding the buttons, is placed into a JScrollPane:
JScrollPane scrollPane = new JScrollPane(gridPanel);
Note that the main JPanel that holds everything is given a BorderLayout, and then 2 components are added, a topPanel JPanel that holds labels, buttons and fields for data input, added at the BorderLayout.PAGE_START, the top position, and the JScrollPane is added to the main JPanel at the BorderLayout.CENTER position:
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
When the old buttons are removed from the gridPanel, and then new buttons are added, I will call revalidate() and repaint() on the gridPanel, the first method to get the layout managers to layout the new components, and the second method call to remove any dirty pixels that may be present:
private void validateGrid() {
int nbRows = (int) rowsSpinner.getValue();
int nbColumns = (int) columnsSpinner.getValue();
gridPanel.removeAll();
buttonsList.clear();
gridPanel.setLayout(new GridLayout(nbRows, nbColumns));
for (int i = 0; i < nbRows * nbColumns; i++) {
int column = i % nbColumns;
int row = i / nbColumns;
String text = String.format("[%02d, %02d]", column, row);
JButton button = new JButton(text);
button.addActionListener(e -> gridButtonAction(column, row));
buttonsList.add(button);
gridPanel.add(button);
}
gridPanel.revalidate();
gridPanel.repaint();
}
I have been trying for a while to make this work.
I have looked at many tutorials and examples on-line, nothing seems to help.
The combo box and the label are both centered right under each other in the middle of the frame.
I know that there needs to be a GridBagConstraints and you need to set the gbc every time you add a new component.
I am doing that so I am not sure why it's not working.
Also why would it be in the center?
Shouldn't it be in the top left if anything?
public class Application {
ArrayList listOfTickers = new ArrayList();
JFrame frame;
JPanel panel;
JLabel jLabel;
GridBagLayout layout;
GridBagConstraints gbc;
JComboBox comboBox;
Application(ArrayList listOfTickers) throws BadLocationException {
this.listOfTickers = listOfTickers;
setLabels();
setComboBox(listOfTickers);
setFrameAndPanel();
addComponents();
closeFrame();
}
private void addComponents() {
addobjects(jLabel, panel, layout, gbc, 1, 3, 4, 2);
addobjects(comboBox, panel, layout, gbc, 3, 0, 2, 1);
}
private void setLabels() {
jLabel = new JLabel("test");
}
private void closeFrame() {
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private void setComboBox(ArrayList listOfTickers) throws BadLocationException {
comboBox = new JComboBox(listOfTickers.toArray());
comboBox.addActionListener(e -> {
String ticker = comboBox.getSelectedItem().toString();
});
}
private void setFrameAndPanel() {
frame = new JFrame("JFrame Example");
panel = new JPanel();
layout = new GridBagLayout();
panel.setLayout(layout);
frame.getContentPane().setLayout(layout);
gbc = new GridBagConstraints();
frame.add(panel);
frame.setSize(600, 600);
}
public void addobjects(Component component, Container panel, GridBagLayout layout, GridBagConstraints gbc, int gridx, int gridy, int gridwidth, int gridheight) {
gbc.gridx = gridx;
gbc.gridy = gridy;
gbc.gridwidth = gridwidth;
gbc.gridheight = gridheight;
layout.setConstraints(component, gbc);
panel.add(component, gbc);
}
}
Also why would it be in the center? Shouldnt it be in the top left if anything?
No, this is how GridBagLayout works, GridBagLayout will layout it's components around the centre of the container. You can play around with the weightx/y properties to change how much of the remaining space is allocated to a given cell.
I would also recommend avoid gridwidth and gridheight until you have a better understanding of what these do, as they are effecting the position of your components
The following will move the components to the top/left point of the container...
private void addComponents() {
gbc.gridx = 1;
gbc.gridy = 3;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.NORTHWEST;
panel.add(jLabel, gbc);
gbc.gridx = 3;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.weightx = 1;
gbc.weighty = 0;
panel.add(comboBox, gbc);
}
Although, since you're applying a GridBagLayout to the JFrame itself, I'd be tempted to change the weightx/y and anchor properties for the panel itself when you add it to the frame, it will make it much simpler
so if I wanted to let say build a calculator with positioning the buttons in specific areas, essentially transforming the entire panel into one big grid and plotting my containers inside that grid what would be the best layout to use and how would I do that?
That depends ultimately on what you want the UI to look like. If all you want is for all the buttons to evenly occupy the available space, the maybe GridLayout would be a better choice...
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Application {
public static void main(String[] args) {
new Application();
}
public Application() {
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 GridLayout(4, 3));
String labels[] = new String[] {"7", "8", "9", "4", "5", "6", "1", "2", "3", "", "0", ""};
for (String label : labels) {
if (label.trim().isEmpty()) {
add(new JLabel());
} else {
add(new JButton(label));
}
}
}
}
}
Note, I had to allow for two empty spaces around the 0 button, this is a requirement of GridLayout, as you don't get control over which cells the components actually appear, they are simply laid out in a linear fashion.
However, if you would prefer something that looks more like most calculators, then you'll need GridBagLayout...
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 Application {
public static void main(String[] args) {
new Application();
}
public Application() {
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());
String labels[][] = new String[][]{{"7", "8", "9"}, {"4", "5", "6"}, {"1", "2", "3"}};
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
for (String[] row : labels) {
gbc.gridx = 0;
for (String col : row) {
add(new JButton(col), gbc);
gbc.gridx++;
}
gbc.gridy++;
}
gbc.gridx = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = 3;
add(new JButton("0"), gbc);
}
}
}
Needs drive musts, you need to decide on the need of flexibility over uniformity (in this case). GridBagLayout is very powerful and flexible, but with it comes complexity.
Note: I could have done the second layout using two panels and two GridLayouts as well - which is an important concept, you're not stuck to a single layout manager, you can use multiple panels, each using different layout managers and "compound" them together to produce a rich interface
If you're going to be doing much UI building, it would greatly benefit you to download the Netbeans IDE and learn to use its GUI builder. It's an excellent GUI builder, and the visual feedback you get as you tweak layout parameters is incredibly helpful for getting the hang of UI building, and especially for getting the hang of how the various GridBagLayout parameters work. It's much more efficient for experimentation than a "write-run-tweak-rerun" loop.
Manually writing UI code is my least favorite thing. Don't inflict it on yourself if possible!
I am creating something like inspector panel for my program, so I can easily change some variables at runtime.
When you run the code bellow, you should see something like this:
inspector screenshot
There are several things that bothers me and I still cannot make them to work properly.
Items are verticaly aligned to the center... I have tried setting anchor to the North but it remains the same. In the example code bellow, there is one line commented out that can fix this: inspector.finish() but the solution seems kinda hacky to me. It works by adding empty JPanel as last item on the panel, acting as some kind of vertical glue to expand the lower area and push the components up. I don't like this, because with this solution I no longer can add more items to the inspector later at runtime.
If you add more items to the inspector (you can do this by changing n variable that fills the inspector with some test data) the scroll bar will not show up, and all the lower items are out of screen even it's all wrapped inside JScrollPane... I have tried several hacks to fix this but none of them works correctly. One of them was setting preferredSize of JPanel to some hard-coded dimensions but I don't like this because I don't know exact size of the panel.
When you push some of the spinner buttons and then click on the combobox (titled as "choose me"), the options are hidden behind the button bellow. This looks like some kind of z-ordering issue. Maybe this is bug in swing, dunno.
When you resizing the window vertically to smaller size, the items on the top will begin to shrink instead of staying the same size. Is there any option to set them constant height at all times?
Maybe I am using the wrong layout manager, but I don't know which other one to choose. I was thinking about simple grid layout, but this does not allow me to do things like having some rows with one item and others with multiple items in such a way that they will always use the whole width "capacity" of the row.
example code:
import java.awt.*;
import javax.swing.*;
public class Test {
// Setup test scenario
public Test() {
// Create window
JFrame f = new JFrame();
f.setLayout(new BorderLayout());
f.setSize(800, 600);
f.setTitle("Inspector");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// Create panel which will be used for the inspector
JPanel p = new JPanel();
p.setPreferredSize(new Dimension(200, 0));
// Encapsulate it to the scroll panel so it can grow vertically
JScrollPane sp = new JScrollPane();
sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
sp.setViewportView(p);
// Create the inspector inside panel p
Inspector inspector = new Inspector(p);
// Fill the inspector with some test data
int n = 3;
for (int i = 0; i < n; ++i) {
inspector.addTitle("Title");
inspector.addButton("push me");
inspector.addCheckBox("check me");
inspector.addSpinner("Spin me");
inspector.addTextField("Edit me", "here");
inspector.addComboBox("Choose one", new String[]{"A", "B", "C"});
inspector.addSeparator();
}
//inspector.finish();
// The inspector will be on the right side of the window
f.getContentPane().add(sp, BorderLayout.LINE_END);
// Show the window
f.setVisible(true);
}
// Main method
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
// Inspector class itself
private class Inspector {
private final JPanel panel;
private final GridBagConstraints constraints;
private int itemsCount = 0;
public Inspector(JPanel p) {
panel = p;
panel.setLayout(new GridBagLayout());
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 0.5;
}
// Adds component which will span across whole row
private void addComponent(Component component) {
constraints.gridx = 0;
constraints.gridwidth = 2;
constraints.gridy = itemsCount;
panel.add(component, constraints);
itemsCount++;
}
// Adds descriptive label on the left and component on the right side of the row
private void addNamedComponent(String description, Component component) {
constraints.gridx = 0;
constraints.gridy = itemsCount;
constraints.gridwidth = 1;
panel.add(new JLabel(description), constraints);
constraints.gridx = 1;
constraints.gridy = itemsCount;
constraints.gridwidth = 1;
panel.add(component, constraints);
itemsCount++;
}
public void addSeparator() {
// (little hacky)
addComponent(new JPanel());
}
public void addTitle(String title) {
addComponent(new JLabel(title));
}
public void addButton(String actionName) {
addComponent(new Button(actionName));
}
public void addCheckBox(String description) {
addNamedComponent(description, new JCheckBox());
}
public void addSpinner(String description) {
addNamedComponent(description, new JSpinner());
}
public void addTextField(String description, String text) {
addNamedComponent(description, new JTextField(text));
}
public void addComboBox(String description, String[] options) {
JComboBox<String> comboBox = new JComboBox<>();
ComboBoxModel<String> model = new DefaultComboBoxModel<>(options);
comboBox.setModel(model);
addNamedComponent(description, comboBox);
}
public void finish() {
constraints.gridx = 0;
constraints.gridy = itemsCount;
constraints.gridwidth = 2;
constraints.weighty = 1.0;
panel.add(new JPanel(), constraints);
}
}
}
The Reason that you can't scroll up or down is because you are setting the preferred size of sp to 200x0. You need to remove this line.
p.setPreferredSize(new Dimension(200, 0));
As for the issue with everything being centered instead of at the top. I would prefer to leave p with the default FlowLayout and give each "section" it's own panel, and the make that panel a GridBagLayout. By doing this you probably will not need addSeparator() anymore.
public class Test {
// Setup test scenario
public Test() {
// Create window
JFrame f = new JFrame();
f.setLayout(new BorderLayout());
f.setSize(800, 600);
f.setTitle("Inspector");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// Create panel which will be used for the inspector
JPanel p = new JPanel();
// Encapsulate it to the scroll panel so it can grow vertically
JScrollPane sp = new JScrollPane();
sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
sp.setViewportView(p);
// Create the inspector inside panel p
Inspector inspector = new Inspector(p);
// Fill the inspector with some test data
int n = 2;
for (int i = 0; i < n; ++i) {
inspector.addTitle("Title");
inspector.addButton("push me");
inspector.addCheckBox("check me");
inspector.addSpinner("Spin me");
inspector.addTextField("Edit me", "here");
inspector.addComboBox("Choose one", new String[]{"A", "B", "C"});
}
// The inspector will be on the right side of the window
f.getContentPane().add(sp, BorderLayout.LINE_END);
// Show the window
f.setVisible(true);
}
// Main method
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
// Inspector class itself
private class Inspector {
private final JPanel panel;
private final GridBagConstraints constraints;
private int itemsCount = 0;
public Inspector(JPanel p) {
panel = new JPanel();
p.add(panel);
panel.setLayout(new GridBagLayout());
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1.0;
}
// Adds component which will span across whole row
private void addComponent(Component component) {
constraints.gridx = 0;
constraints.gridwidth = 2;
constraints.gridy = itemsCount;
panel.add(component, constraints);
itemsCount++;
}
// Adds descriptive label on the left and component on the right side of the row
private void addNamedComponent(String description, Component component) {
constraints.gridx = 0;
constraints.gridy = itemsCount;
constraints.gridwidth = 1;
panel.add(new JLabel(description), constraints);
constraints.gridx = 1;
constraints.gridy = itemsCount;
constraints.gridwidth = 1;
panel.add(component, constraints);
itemsCount++;
}
public void addSeparator() {
// (little hacky)
addComponent(new JPanel());
}
public void addTitle(String title) {
addComponent(new JLabel(title));
}
public void addButton(String actionName) {
addComponent(new Button(actionName));
}
public void addCheckBox(String description) {
addNamedComponent(description, new JCheckBox());
}
public void addSpinner(String description) {
addNamedComponent(description, new JSpinner());
}
public void addTextField(String description, String text) {
addNamedComponent(description, new JTextField(text));
}
public void addComboBox(String description, String[] options) {
JComboBox<String> comboBox = new JComboBox<>();
ComboBoxModel<String> model = new DefaultComboBoxModel<>(options);
comboBox.setModel(model);
addNamedComponent(description, comboBox);
}
public void finish() {
constraints.gridx = 0;
constraints.gridy = itemsCount;
constraints.gridwidth = 2;
constraints.weighty = 1.0;
panel.add(new JPanel(), constraints);
}
}
}
I made a small GUI application that only has the presentation tier right now. It constructs the basic GUI, (but no logic added yet). I'm having trouble with laying out controls/components such as text fields and Buttons.
Here's the code:
Main.java
public class Main {
public static void main(String[] args) {
// Make a new Client (TempConverter application)
Client client = new Client();
}
}
Client.java
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Client extends JFrame{
private JPanel panel;
private JTextField inputTextBox;
private JTextField outputTextBox;
private JButton convertButton;
public Client(){
panel = new JPanel();
inputTextBox = new JTextField(6);
outputTextBox = new JTextField(6);
convertButton = new JButton("Convert!");
ConstructGUI();
}
private void ConstructGUI(){
this.setTitle("Temerature Converter");
this.setSize(300, 400);
PanelLayout();
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void PanelLayout(){
this.add(panel);
panel.add(inputTextBox);
panel.add(outputTextBox);
panel.add(convertButton);
}
}
The components all appear next to each other, and it's not that I expected otherwise, but no matter what layout I tried (unless I did it wrong), it doesn't change.
Do I have to override something maybe?
You can use BoxLayout to have them stacked on top of each other.
private void PanelLayout(){
this.add(panel);
//next three lines aligning the components horizontally
inputTextBox.setAlignmentX(Component.CENTER_ALIGNMENT);
outputTextBox.setAlignmentX(Component.CENTER_ALIGNMENT);
convertButton.setAlignmentX(Component.CENTER_ALIGNMENT);
//aligning horizontally end. If you don't want the align them horizontally just remove these three lines.
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(Box.createVerticalGlue());//remove this line if you don't want to center them vertically
panel.add(inputTextBox);
panel.add(outputTextBox);
panel.add(convertButton);
panel.add(Box.createVerticalGlue());//remove this line if you don't want to center them vertically
}
You could use a GridBagLayout or GridLayout depending on what you want to achieve...
public class Client extends JFrame {
private JPanel panel;
private JTextField inputTextBox;
private JTextField outputTextBox;
private JButton convertButton;
public Client() {
panel = new JPanel(new GridBagLayout());
inputTextBox = new JTextField(6);
outputTextBox = new JTextField(6);
convertButton = new JButton("Convert!");
ConstructGUI();
}
private void ConstructGUI() {
this.setTitle("Temerature Converter");
PanelLayout();
}
private void PanelLayout() {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
this.add(panel);
panel.add(inputTextBox, gbc);
panel.add(outputTextBox, gbc);
panel.add(convertButton, gbc);
}
}
Have a look at Laying Out Components Within a Container for more details.
I'd also discourage you from extending directly from JFrame, but instead, start by extending from JPanel instead, it will decouple your code and provide better re-usability, among other things
I'm trying to create a Panel which contains N rows and 2 columns, where the first column is a label with a variable length but within certain limits (it varies length according to Language), while the second column contains fields with possibly very long text.
I've tried with GridBagLayout using NetBeans however the results are messy when scaling the panel or adding very long fields..
below a screenshot of the JPanel to see what I mean:
I'd like the left column to be spaced from the left border, the first column to never resize to a smaller value than it's longest label (labels must always be readable), while the second column should visualize up to its available horizontal space and then show dots (and not crop the text).
What's also boring is that although I defined NorthWest for orientation the Panel shows vertically aligned in the center
EDIT: I have used TableColumnAdjuster to use table rows instead of labels for my values so that I can select the values with the mouse.
I still have the rows however fit the length of the text and not of the containing Panel:
public class TestPanel extends JPanel
{
private static ArrayList<String> columnNames = new ArrayList<>();
static JTable table;
MyTableModel myTableModel = new MyTableModel();
static class MyTableModel extends AbstractTableModel
{
private String[] columnNames =
{
"Name", "Value"
};
private Object[][] data =
{
{ "Subject", "very very very very very very very very very very very very very very very very long subject"},
{ "Date", "" },
{ "Location", "" },
{ "Status", "" },
{ "Notes", "" }
};
#Override
public int getColumnCount()
{ return columnNames.length; }
#Override
public int getRowCount()
{ return data.length; }
#Override
public String getColumnName(int col)
{ return columnNames[col]; }
#Override
public Object getValueAt(int row, int col)
{ return data[row][col]; }
#Override
public Class getColumnClass(int c)
{ return getValueAt(0, c).getClass(); }
#Override
public boolean isCellEditable(int row, int col)
{ return false; }
#Override
public void setValueAt(Object value, int row, int col)
{ }
}
public TestPanel()
{
table = new JTable(myTableModel);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumnAdjuster tca = new TableColumnAdjuster(table);
tca.adjustColumns();
JPanel panel = new JPanel(new GridBagLayout());
panel.add(table);
GridBagConstraints constraints = new GridBagConstraints();
constraints.weighty = 1.0;
constraints.fill = GridBagConstraints.VERTICAL;
panel.add(Box.createGlue(), constraints);
add(panel);
}
public static void main(String args[])
{
JFrame frame = new JFrame();
frame.setContentPane(new TestPanel());
frame.setSize(300, 800);
frame.setVisible(true);
}
}
while TableColumnAdjuster is from TableColumnAdjuster.. what I get is still as below
You probably have an incorrect usage of the GridBagLayout.
To push all the content to the top, either wrap the GridBagLayout panel into another panel (like a BorderLayout panel in the NORTH position) or add a component at the bottom which takes all the extra space
For all components in the first column, set weightx to 0 while on the second one, set it to something bigger than 0.
To add some space between the left border and the labels, simply use insets
Here is an example illustrating this:
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.Random;
import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Gridbag {
public void initUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridBagLayout());
Random random = new Random();
for (int i = 0; i < 10; i++) {
String label = "Label " + (i + 1);
StringBuilder fieldValue = new StringBuilder();
int r = 1 + random.nextInt(4);
for (int j = 0; j < r; j++) {
fieldValue.append("Some long value that may be very long in some cases");
}
addField(panel, label, fieldValue.toString());
}
GridBagConstraints constraints = new GridBagConstraints();
constraints.weighty = 1.0;
constraints.fill = GridBagConstraints.VERTICAL;
panel.add(Box.createGlue(), constraints);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
private void addField(JPanel panel, String label, String fieldValue) {
GridBagConstraints labelGBC = new GridBagConstraints();
labelGBC.insets = new Insets(0, 5, 0, 5);
labelGBC.anchor = GridBagConstraints.EAST;
GridBagConstraints fieldGBC = new GridBagConstraints();
fieldGBC.gridwidth = GridBagConstraints.REMAINDER;
fieldGBC.weightx = 1.0;
fieldGBC.anchor = GridBagConstraints.WEST;
panel.add(new JLabel(label), labelGBC);
panel.add(new JLabel(fieldValue), fieldGBC);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Gridbag().initUI();
}
});
}
}
One way to achieve it is to use 3 panels in a 'nested layout'. The panels would be laid out as follows.
Uses BorderLayout for the outer panel, it contains panels 2 & 3.
Uses a single column GridLayout - put in BorderLayout.LINE_START
Uses a single column GridLayout - put in BorderLayout.CENTER
You could use a JTable with two columns and a TableColumnAdjuster or a TableCellRenderer in order to adjust the width of the columns to the longest content.