How I can create a JPanel with a lot of buttons aligned float left and with vertical scrollbars only?
The buttons should be sorted like below.
1 2 3 4
5 6 7 8
9 10 11 12
If you use GridLayout, then you will not be able to add a scrollpane since it will resize automatically to fit all the components inside of it. An easier approach is to use a FlowLayout and setPreferredSize(...) to set the size of your panel. Though it is not advised to set the size of panels, you still need to have the scrollbar put into use somehow. Here is a MCVE:
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Example extends JFrame {
private final int BUTTON_WIDTH = 100;
private final int BUTTON_HEIGHT = 50;
private final int BUTTON_ROWS = 3;
private final int BUTTON_COLUMNS = 4;
private final int OFFSET = 20;// the width of the actual scroll bar in pixels (approximately).
private final int PANEL_WIDTH = BUTTON_WIDTH * BUTTON_COLUMNS + OFFSET;
private final int PANEL_HEIGHT = BUTTON_HEIGHT * BUTTON_ROWS + OFFSET;
private final int SCROLL_HEIGHT = 100;//or whatever you would like...
private final JButton[] buttons = new JButton[BUTTON_ROWS * BUTTON_COLUMNS];
public Example() {
JPanel panel = new JPanel(new FlowLayout());
JScrollPane scroll = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
panel.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
scroll.setPreferredSize(new Dimension(PANEL_WIDTH + OFFSET, SCROLL_HEIGHT));
for (int i = 0; i < buttons.length; i++) {
JButton button = new JButton((i + 1) + "");
buttons[i] = button;
button.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT));
panel.add(button);
}
//if you want the panel to resize when window is stretched.
//setLayout(new FlowLayout(FlowLayout.CENTER));
add(scroll);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
new Example();
}
}
Add the buttons to a (panel with a) grid layout to arrange them in rows and columns. Add that panel to a scroll pane, then add the scroll pane to the line start constraint of a border layout, and they will appear on the left.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class LeftAlignedButtonGrid {
private JComponent ui = null;
LeftAlignedButtonGrid() {
initUI();
}
public void initUI() {
if (ui!=null) return;
/* BorderLayout offers a LINE_START constraint that will put a
single child component on the left hand side of the GUI (in any
locale that uses left-to-right text orientation) */
ui = new JPanel(new BorderLayout(4,4));
JPanel buttonPanel = new JPanel(new GridLayout(0,4,2,2));
for (int ii=1; ii<13; ii++) {
buttonPanel.add(new JButton("" + ii));
}
ui.add(new JScrollPane(buttonPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),
BorderLayout.LINE_START);
ui.setBorder(new EmptyBorder(4,4,4,4));
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
LeftAlignedButtonGrid o = new LeftAlignedButtonGrid();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
// comment this out to allow the height of the GUI to be reduced,
// thus making the vertical scroll bar to have a purpose!
//f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
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 a problem with adding a specific number of buttons from my for-loop to my JPanel, i know how to add all oof them, but i want to add only 1-10 (i havent decided yet, lets go with 10).'
this is my class where i just declare what objects i want to have.
private static int cID;
private static Deck[] card;
static ArrayList<JButton> buttonList = new ArrayList<JButton>();
private JFrame f;
private JPanel p1;
private JButton button;
public boolean isEmpty() {
return cID == 0;
}
public static void main(String[] args) {
CustomDecks c = new CustomDecks();
c.deckCreator();
}```
this is my for-loop where i create 420 buttons and give them names "card" + i where i is 0 - 419, yet when i try to add card0 to my panel, it fails, why?
private void deckCreator() {
card = new Deck[25];
new ArrayList<Cards> (cSet.cards);
for(int i = 0; i < 420; i++) {
button = new JButton();
buttonList.add(button);
button.setName("card" + i);
f.add(button);
p1.add(card0);
}
f.add(p1);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setVisible(true);
f.setExtendedState(Frame.MAXIMIZED_BOTH);
f.setUndecorated(true);
}
}
}
I'm not sure you can create a JPanel large enough to hold 420 JButtons.
Here's an example of a JButton GUI.
[
Generally, you create an application model and view separately. The model is made up of one or more plain Java classes. The view reads from the application model but doesn't update the model.
Your controller classes (ActionListener classes) update the application model and update / repaint the view.
This pattern is called the model / view / controller (MVC) pattern.
You can see in the example code below that the model is created in the view class constructor. Generally, you create the application model first, then you create the application view.
And here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
public class JButtonScrollGUI {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new JButtonScrollGUI();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private String[] greekAlphabet;
public JButtonScrollGUI() {
this.greekAlphabet = new String[] { "alpha", "beta", "gamma", "epsilon", "zeta" };
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setTitle("Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createScrollPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createScrollPanel() {
JPanel panel = new JPanel(new BorderLayout());
JPanel innerPanel = createButtonPanel();
Dimension d = innerPanel.getPreferredSize();
d.width += 50;
d.height /= 2;
panel.setPreferredSize(d);
JScrollPane scrollPane = new JScrollPane(innerPanel);
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new GridLayout(0, 3, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
for (int i = 0; i < 20; i++) {
JButton button1 = new JButton("Previous " + i);
panel.add(button1);
JComboBox<String> selectorBox = new JComboBox<>(greekAlphabet);
panel.add(selectorBox);
JButton button2 = new JButton("Next " + i);
button2.setPreferredSize(button1.getPreferredSize());
panel.add(button2);
}
return panel;
}
}
Hereunder is a minimal example of a bug I can't fix in a Java program.
The bug consists is that the lines are randomly shown in the JFrame: sometimes I see the board at runtime, sometimes not.
Any hint greatly appreciated.
import java.awt.Graphics;
import javax.swing.JFrame;
public class EssaiDrawLine extends JFrame{
public static void main(String[] args) {
EssaiDrawLine mafenetre = new EssaiDrawLine();
mafenetre.setVisible(true);
}
// constructeur
public EssaiDrawLine() {
setTitle("mon titre");
setSize(600, 500);
}
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i <= 8; i++) {
g.drawLine(50 * i + 10, 40, 50*i + 10, 440);
g.drawLine(10, 50 * i + 40, 410, 50 * i + 40);
}
g.drawString("Cliquer pour obtenir la prochaine solution", 20, 470);
}
}
Another option: don't draw the lines but instead create a grid of components, such as a 2-D array of JPanels held in another JPanel, one that uses GridLayout, and then give the JPanels a MouseListener so that they can respond to mouse events. For example, a simple version of this could look something like so (please read the comments in the code):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial") // get rid of warning
public class EssaiDrawLine3 extends JPanel {
private static final int SIDE = 8;
private static final int SQR_LENGTH = 50;
private static final int GAP = 5;
private static final Color CLICK_COLOR = Color.RED;
private static final Color SQR_COLOR = Color.LIGHT_GRAY;
private static final String CLICK_TEXT = "Cliquer pour obtenir la prochaine solution";
// our grid of JPanel
private JPanel[][] grid = new JPanel[SIDE][SIDE];
public EssaiDrawLine3() {
// JPanel to hold the grid
// give it an 8x8 GridLayout with 1 pixel gap for lines to show
JPanel gridHolder = new JPanel(new GridLayout(SIDE, SIDE, 1, 1));
// background color that shows through as lines in gaps in grid
gridHolder.setBackground(Color.BLACK);
gridHolder.setBorder(BorderFactory.createLineBorder(Color.black));
// mouse listener to add to each JPanel cell
MyMouse myMouse = new MyMouse();
// nested for loop to create our grid of JPanels
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
// create a single cell
JPanel cellPanel = new JPanel();
// size it 50x50 pixels
cellPanel.setPreferredSize(new Dimension(SQR_LENGTH, SQR_LENGTH));
// add the mouse listener to it
cellPanel.addMouseListener(myMouse);
// give it a default background color
cellPanel.setBackground(SQR_COLOR);
// place it into the 2-D array of JPanel
grid[row][col] = cellPanel;
// place it into the grid layout-using JPanel
gridHolder.add(cellPanel);
}
}
// display text at the bottom
JLabel label = new JLabel(CLICK_TEXT, SwingConstants.CENTER);
// allow gaps around the main JPanel
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
// give the main JPanel a BorderLayout
setLayout(new BorderLayout(GAP, GAP));
add(gridHolder); // add the grid to the CENTER position
// add the JLabel to the bottom position
add(label, BorderLayout.PAGE_END);
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
// get the JPanel cell that was clicked
JComponent comp = (JComponent) e.getSource();
// get its color and change it
Color color = comp.getBackground();
if (color.equals(CLICK_COLOR)) {
comp.setBackground(SQR_COLOR);
} else {
comp.setBackground(CLICK_COLOR);
}
}
}
private static void createAndShowGui() {
EssaiDrawLine3 mainPanel = new EssaiDrawLine3();
JFrame frame = new JFrame("Essai Draw Line3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Never draw directly in the JFrame but instead within a JPanel's paintComponent method, and then display that JPanel in your JFrame. This is all well explained in the standard tutorials: : Lesson: Performing Custom Painting
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
#SuppressWarnings("serial")
public class EssaiDrawLine2 extends JPanel {
private static final int PS_WIDTH = 600;
private static final int PS_HEIGHT = 500;
public EssaiDrawLine2() {
setPreferredSize(new Dimension(PS_WIDTH, PS_HEIGHT));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i <= 8; i++) {
g.drawLine(50 * i + 10, 40, 50 * i + 10, 440);
g.drawLine(10, 50 * i + 40, 410, 50 * i + 40);
}
g.drawString("Cliquer pour obtenir la prochaine solution", 20, 470);
}
private static void createAndShowGui() {
EssaiDrawLine2 mainPanel = new EssaiDrawLine2();
JFrame frame = new JFrame("Essai Draw Line");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
For some reason my borders aren't showing for my panels and i am unsure why, is there something i'm missing?
I have a main class which runs the frame class as well as other classes separate to the GUI
This is the code from my frame class:
import javax.swing.*;
import java.awt.*;
public class Frame
{
public static int xsize;
public static int ysize;
public static void main()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new MainFrame("Warlock of Firetop Mountain");
//Implementing Toolkit to allow computer to get dimensions of screen and assign them to two int values
Toolkit tk = Toolkit.getDefaultToolkit();
Frame.xsize = (int) tk.getScreenSize().getWidth();
Frame.ysize = (int) tk.getScreenSize().getHeight();
frame.setTitle("Warlock of Firetop Mountain");
frame.setSize(new Dimension(xsize, ysize));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
The frame.java takes its panels from MainFrame.java:
import javax.swing.*;
import java.awt.*;
public class MainFrame extends JFrame
{
private Panel1 storyPanel;
private Panel2 statsPanel;
private Panel3 commandsPanel;
public MainFrame(String title)
{
super(title);
// Setting Layout
setLayout(new BorderLayout());
storyPanel = new Panel1();
statsPanel = new Panel2();
commandsPanel = new Panel3();
Container p = getContentPane();
p.add(storyPanel, BorderLayout.WEST);
p.add(statsPanel, BorderLayout.EAST);
p.add(commandsPanel, BorderLayout.SOUTH);
}
}
This calls up my three panels which look like this:
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Dimension;
import java.awt.Color;
public class Panel1 extends JPanel
{
public Panel1()
{
//Set size of Panel1
int xsizeP1 = (Frame.xsize / 2);
int ysizeP1 = (Frame.ysize / 3 * 2);
setPreferredSize(new Dimension(xsizeP1, ysizeP1));
setBorder(BorderFactory.createLineBorder(Color.black));
}
}
when the code runs the window launches as full screen but no borders or possibly panels are visible.
Thanks for any help, sorry if my questions are tedious, i'm relatively new to programming.
This is roughly what i want my panels to look like, eventually ill add in components to the panel and use GridBagConstraints to control the formatting
// this creates the JPanels and sets their preferred sizes
JFrame frame = new MainFrame("Warlock of Firetop Mountain");
//this sets your size static contents -- after the above's been done!
Toolkit tk = Toolkit.getDefaultToolkit();
Frame.xsize = (int) tk.getScreenSize().getWidth();
Frame.ysize = (int) tk.getScreenSize().getHeight();
You're setting preferred sizes of all your JPanels to 0, 0, and so you're not seeing any borders. Your sizing is being created after you've created your JPanels, and this method of sizing looks dangerous to me.
OK, thanks for posting an image of the desired GUI. My recommendations are:
First and foremost, don't try setting sizes as you're doing.
Instead, let the components and their layout managers size themselves.
Nest JPanels, each using its own layout manager to allow you to simply create complex GUI's.
When displaying images / ImageIcons, let them set the sizes of things as well.
If your GUI starts up with no icons displaying, consider creating a blank ImageIcon with a blank image of the right size as a placeholder icon.
For example, something like this:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class TomGuiPanel extends JPanel {
// rows and cols for jtextarea
private static final int CURRENT_AREA_ROWS = 20;
private static final int CURRENT_AREA_COLS = 40;
// columns for command jtextfied
private static final int COMMANDS_FIELD_COLS = 50;
// size of GUI component gaps
private static final int EB_GAP = 3;
private static final int NUMBER_OF_OPTIONS = 5;
// number if ImageIcons displayed within the user image char JList
private static final int CHAR_IMG_VISIBLE_ROWS = 5;
// a guess of the width of the largest image icon in the JList
// You'd use a different number
private static final int USER_IMG_CHAR_IMG_WIDTH = 70;
private JTextArea currentTextArea = new JTextArea(CURRENT_AREA_ROWS, CURRENT_AREA_COLS);
private JTextField commandsField = new JTextField(COMMANDS_FIELD_COLS);
private EnterAction enterAction = new EnterAction("Enter");
private DefaultListModel<Icon> charImgListModel = new DefaultListModel<>();
private JList<Icon> charImgList = new JList<>(charImgListModel);
public TomGuiPanel() {
JPanel topBtnPanel = new JPanel(new GridLayout(1, 0, EB_GAP, EB_GAP));
String[] btnTexts = { "Inventory", "Options", "Save", "Load" };
for (String txt : btnTexts) {
topBtnPanel.add(new JButton(txt));
}
JPanel characteristicsPanel = new JPanel(new GridBagLayout());
addCharacteristics(characteristicsPanel, "HP", 20, 0);
addCharacteristics(characteristicsPanel, "Attack", 12, 1);
addCharacteristics(characteristicsPanel, "Defence", 8, 2);
addCharacteristics(characteristicsPanel, "Agility", 9, 3);
addCharacteristics(characteristicsPanel, "Luck", 2, 4);
JScrollPane imgListPane = new JScrollPane(charImgList);
imgListPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
charImgList.setVisibleRowCount(CHAR_IMG_VISIBLE_ROWS);
charImgList.setPrototypeCellValue(createProtoType());
JPanel rightPanel = new JPanel(new BorderLayout(EB_GAP, EB_GAP));
rightPanel.add(topBtnPanel, BorderLayout.PAGE_START);
rightPanel.add(imgListPane, BorderLayout.CENTER);
rightPanel.add(characteristicsPanel, BorderLayout.LINE_END);
JPanel optionsPanel = new JPanel(new GridLayout(1, 0));
for (int i = 0; i < NUMBER_OF_OPTIONS; i++) {
String text = "Option " + (i + 1);
optionsPanel.add(new JCheckBox(text));
}
currentTextArea.setWrapStyleWord(true);
currentTextArea.setLineWrap(true);
currentTextArea.setFocusable(false);
JScrollPane taScrollPane = new JScrollPane(currentTextArea);
taScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.add(taScrollPane, BorderLayout.CENTER);
centerPanel.add(rightPanel, BorderLayout.LINE_END);
centerPanel.add(optionsPanel, BorderLayout.PAGE_END);
JPanel commandsPanel = new JPanel();
commandsPanel.setLayout(new BoxLayout(commandsPanel, BoxLayout.LINE_AXIS));
commandsPanel.add(commandsField);
commandsPanel.add(Box.createHorizontalStrut(EB_GAP));
commandsPanel.add(new JButton(enterAction));
commandsPanel.add(Box.createHorizontalStrut(EB_GAP));
commandsPanel.add(new JButton(new ExitAction("Exit", KeyEvent.VK_X)));
commandsField.setAction(enterAction); // use same action for button and
// text field
setBorder(BorderFactory.createEmptyBorder(EB_GAP, EB_GAP, EB_GAP, EB_GAP));
setLayout(new BorderLayout(EB_GAP, EB_GAP));
add(centerPanel, BorderLayout.CENTER);
add(commandsPanel, BorderLayout.PAGE_END);
}
private void addCharacteristics(JPanel cPanel, String text, int value, int row) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = row;
gbc.insets = new Insets(5, 5, 5, 5);
gbc.weightx = 1.0;
gbc.weighty = 0.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
cPanel.add(new JLabel(text), gbc);
gbc.insets.left = 20;
gbc.anchor = GridBagConstraints.EAST;
gbc.gridx = 1;
cPanel.add(new JLabel(String.valueOf(value)), gbc);
}
private Icon createProtoType() {
int w = USER_IMG_CHAR_IMG_WIDTH;
int h = w;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Icon icon = new ImageIcon(img);
return icon;
}
private class EnterAction extends AbstractAction {
public EnterAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
String text = commandsField.getText();
currentTextArea.append(text + "\n");
commandsField.selectAll();
}
}
private class ExitAction extends AbstractAction {
public ExitAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Component source = (Component) e.getSource();
Window win = SwingUtilities.getWindowAncestor(source);
win.dispose();
}
}
private static void createAndShowGUI() {
TomGuiPanel mainPanel = new TomGuiPanel();
JFrame frame = new JFrame("Tom's GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Would create this realizable GUI:
Note that the GUI is roughly made, has no functionality other than the enter and exit buttons.
I have a JTabbedPane with 2 JPanels set to GridLayout(13, 11). The first JPanel has enough of the cells filled out that it leaves the empty cells.
The second JPanel has significantly fewer cells filled and this results in each button getting stretched to fill an entire row.
Is there any way to get GridLayout to honor the empty cells, so the buttons in both JPanels are the same size?
Is there any way to get GridLayout to honor the empty cells, so the buttons in both JPanels are the same size?
It is certainly doable with GridLayout, simply 'fill' the blank squares with a JLabel that has no text.
E.G. Here are two grid layouts, both padded to 3 rows.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.LineBorder;
class FillGridLayout {
public static final JComponent getPaddedGrid(
ArrayList<BufferedImage> images, int width, int height) {
JPanel p = new JPanel(new GridLayout(height, width, 2, 2));
p.setBorder(new LineBorder(Color.RED));
int count = 0;
for (BufferedImage bi : images) {
p.add(new JButton(new ImageIcon(bi)));
count++;
}
for (int ii=count; ii<width*height; ii++ ) {
// add invisible component
p.add(new JLabel());
}
return p;
}
public static void main(String[] args) {
final ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
int s = 16;
for (int ii = s/4; ii < s; ii+=s/4) {
images.add(new BufferedImage(ii, s, BufferedImage.TYPE_INT_RGB));
images.add(new BufferedImage(s, ii, BufferedImage.TYPE_INT_RGB));
}
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new BorderLayout(3,3));
gui.add(getPaddedGrid(images, 3, 3), BorderLayout.LINE_START);
gui.add(getPaddedGrid(images, 4, 3), BorderLayout.LINE_END);
JOptionPane.showMessageDialog(null, gui);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Use nested layouts to get your desired result. Some layouts respect the preferred size of components and some don't. GridLayout is one of the ones that don't. Have a look at this answer to see which one's do and which one's don't.
For example, you could nest the 13 buttons in a GridLayout nested in another JPanel with a FlowLayout
JPanel p1 = new JPanel(new FlowLayout(FlowLayout.LEADING));
JPanel p2 = new JPanel(new GridLayout(13, 1));
for (int i = 0; i < 13; i++) {
p2.add(new JButton("Button " + i));
}
p1.add(p2);
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test6 {
public Test6() {
JPanel p1 = new JPanel(new FlowLayout(FlowLayout.LEADING));
JPanel p2 = new JPanel(new GridLayout(13, 1));
for (int i = 0; i < 13; i++) {
p2.add(new JButton("Button " + i));
}
p1.add(p2);
JFrame frame = new JFrame("Test Card");
frame.add(p1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Test6 test = new Test6();
}
});
}
}
add empty JLabel to the empty cell
content.add(new JLabel(""));