I want to have 2 JPanels in my app, side by side. One that will have some info about my custom board on the right and one about painting that custom board on the left. The first JPanel is a classic, but the second is a custom panel. It seems that im having problems with putting my custom panel into the frame.
I've created a class named BoardPanel within my gui class to draw my custom board. I don't know if this is the best approach. Should i create a separate class instead?
This is the code of the gui class:
public class BattleshipGUI extends JFrame {
private BoardPanel mainPanel;
///////////////////////////////////////////////////////////////////////////////////////////////
// Create my frame
///////////////////////////////////////////////////////////////////////////////////////////////
public BattleshipGUI() {
JPanel container = new JPanel(new BorderLayout()); //the container panel that contains the 2 other panels
mainPanel = new BoardPanel(); //main panel with my custom painting
JPanel detailsPanel = new JPanel(new BorderLayout()); //secondary panel with various details about the game
container.add(mainPanel, BorderLayout.CENTER); //add the 2 panels in the container
container.add(detailsPanel, BorderLayout.EAST);
this.add(container); //add container to my frame
//this.setContentPane(container);
this.setIconImage(Toolkit.getDefaultToolkit().getImage(BattleshipGUI.class.getResource("/resources/battleship_128.png")));
this.setTitle("My Battleship Game");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//this.setBounds(100, 100, 850, 700);
//this.pack();
this.setSize(850, 600);
this.setVisible(true);
}
And this is the code of the inner class for the custom painting
class BoardPanel extends JPanel {
private static final int ROWS = 20;
private static final int COLUMNS = 20;
public void paintComponent(Graphics g) {
super.paintComponent(g);
int sqSize = this.getHeight()/ROWS;
for(int i=0; i<ROWS; i++) {
for(int j=0; j<COLUMNS; j++) {
int x = j * sqSize;
int y = i * sqSize;
g.drawRect(x, y, sqSize, sqSize);
}
}
}
}
Aside from all these, i have a question. If i want to have a custom painting, is it possible to work along side with the WindowsBuilderPro? I begun using that tool at first. But, i saw that i cant draw something custom with the tool and i had to write code to do that. Is it possible to write code for a custom paint AND use the tool at the same time for different purposes, like adding a simple text label, or even to edit that custon paint? The expected result that i want to see, appears when i run the program. My frame with the two panels. But when i open the WindowsBuilderPro, my custom panel does not appear and the result is a bit wrong. Thit is the reason why i have a question about my approach and if i can write code and use the tool at the same time. Thank you and sorry for the long text guys. I am too confused about this.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
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.fill = gbc.BOTH;
gbc.gridx = 0;
gbc.gridy = 0;
JPanel filler = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 300);
}
};
filler.setBackground(Color.BLUE);
add(filler, gbc);
gbc.gridx++;
add(new BoardPanel(), gbc);
}
}
class BoardPanel extends JPanel {
private static final int ROWS = 20;
private static final int COLUMNS = 20;
private int sqSize = 20;
#Override
public Dimension getPreferredSize() {
return new Dimension(COLUMNS * sqSize, ROWS * sqSize);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLUMNS; j++) {
int x = j * sqSize;
int y = i * sqSize;
g.drawRect(x, y, sqSize, sqSize);
}
}
}
}
}
Take the time to read through Laying Out Components Within a Container to get a better understanding how the layout management API works
Related
I am trying generate a JLabel grid in Java, but on every start the generated JLabels vary, on one start of the program there can be 25 visual JLabels, and without changing the code and restarting there can be 27 visual JLabels, im so confused, I have no idea what might be causing this. In theory there should be 169 JLabels visual on the screen (13 x 13 grid).
public class GUI {
JFrame frame;
JPanel panel;
int matrixLength = 13;
JLabel label[] = new JLabel[matrixLength * matrixLength];
public GUI() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setVisible(true);
frame.setResizable(false);
panel = (JPanel) frame.getContentPane();
panel.setLayout(null);
for (JLabel labels : label) {
labels = new JLabel();
labels.setVisible(true);
}
}
public void drawLabels() {
int xInc = 50;
int yInc = 50;
int xStart = 50;
int yStart = 0;
int y = yStart;
int x = 50;
for (int i = 0; i < label.length; i++) {
label[i] = new JLabel(" ");
label[i].setOpaque(true);
label[i].setVisible(true);
label[i].setBackground(Color.black);
if (i % (matrixLength) == 0) {
y += yInc;
x = xStart;
} else {
x += xInc;
}
System.out.println("i: " + i + " | y: " + y + " | x: " + x);
label[i].setBounds(x, y, 40, 40);
panel.add(label[i]);
}
}
}
I created the following GUI from your code.
Here are the changes I made.
I added a main method so I could start the application. In the main method, I called the SwingUtilities invokeLater method to ensure that the Swing components are created and executed on the Event Dispatch Thread.
I used two Swing layout managers to create the GUI. The JFrame has a default BorderLayout. The JPanel I created to hold the JLabels uses a GridLayout.
I added a text value to each of the JLabels so you could see them in the grid.
Here's the complete runnable code I used to create the GUI.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LabelGridGUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LabelGridGUI();
}
});
}
JFrame frame;
int matrixLength = 13;
JLabel label[] = new JLabel[matrixLength * matrixLength];
public LabelGridGUI() {
frame = new JFrame("Label Grid");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createLabelPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setResizable(false);
frame.setVisible(true);
}
public JPanel createLabelPanel() {
JPanel panel = new JPanel(new GridLayout(0, matrixLength));
for (int i = 0; i < label.length; i++) {
label[i] = new JLabel("X");
label[i].setBackground(Color.black);
label[i].setForeground(Color.yellow);
label[i].setOpaque(true);
panel.add(label[i]);
}
return panel;
}
}
JTextField, JSlider, JComboBox, etc added to a JComponent are not displayed in the JFrame containing the JComponent. It seems only drawing by the Graphics parameter allows painting. The included test program compares using JPanel to JComponent in my efforts to discover how to display components added to a JComponent. Is there any way to get such components displayed?
public class TestPaints {
public static void main(String[] args) {
new TestPaints();
}
JTextField _text1;
JLabel _label1 = new JLabel("Text1");
JTextField _text2;
JLabel _label2 = new JLabel("Text2");
TestPaints() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Paint a Widget");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(3, 2));
GridBagConstraints grid = new GridBagConstraints();
frame.add(new JLabel("TextField in JComponent "));
grid.gridx = 2;
frame.add(new JLabel("TextField in JPanel"), grid);
grid.gridy = 2;
grid.gridx = 1;
frame.add(new TestJComponent(), grid);
grid.gridx = 2;
frame.add(new TestJPanel(), grid);
grid.gridy = 3;
grid.gridx = 1;
/* tabbing between the two TextFields shows that keystrokes are seen */
frame.add(_label1, grid);
grid.gridx = 2;
frame.add(_label2, grid);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestJComponent extends JComponent {
public TestJComponent() {
setPreferredSize(new Dimension(100, 30));
_text1 = new JTextField(6);
_text1.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
_label1.setText(_text1.getText());
_label1.repaint();
}
});
_text1.setOpaque(true);
_text1.setVisible(true);
add(_text1);
/* This doesn't work
JPanel panel = new JPanel();
panel.add(_text1);
add(panel); */
setOpaque(true);
setVisible(true);
setBackground(Color.green);
}
public void paint(Graphics g) {
super.paint(g); // did not do background. Rectangle r = g.getClipBounds(); // needs this
g.setColor(getBackground());
g.fillRect(r.x, r.y, r.width, r.height);
/* Variations such as these don't work */
_text1.setOpaque(true);
_text1.setVisible(true);
_text1.paintComponents(g);
}
}
class TestJPanel extends JPanel {
TestJPanel() {
_text2 = new JTextField(6);
_text2.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
_label2.setText(_text2.getText());
_label2.repaint();
}
});
add(_text2);
setBackground(Color.blue);
}
}
}
Edit: you need to give your JComponent a layout such as FlowLayout for components to show properly since it does not have a default layout like JPanel has. So add setLayout(new FlowLayout()) into your JComponent's constructor
You have:
frame.setLayout(new GridLayout(3, 2));
and then try to add components to the JFrame's contentPane using GridBagConstraints, and this doesn't make sense. If you want to use these constraints, then the container needs to use GridBagLayout, not GridLayout.
Also this is dangerous code:
public void paint(Graphics g) {
super.paint(g); // did not do background. Rectangle r = g.getClipBounds(); // needs this
g.setColor(getBackground());
g.fillRect(r.x, r.y, r.width, r.height);
/* Variations such as these don't work */
_text1.setOpaque(true);
_text1.setVisible(true);
_text1.paintComponents(g);
}
You should be overriding JComponent's paintComponent method, not its paint method (call super.paintComponent) and should not be setting component visibility or calling a component's paintComponents method directly within any painting method.
Another issue: don't use KeyListeners within Swing text components but rather add a DocumentListener to the component's Document. Otherwise you risk breaking some of the functionality of the text component, and also your listener won't work for copy/paste, while the DocumentListener will.
And another issue, your main issue: you need to give the JComponent a layout. It does not default to FlowLayout like a JPanel does. This is why the added components are not showing within it.
For example:
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class TestPaints2 {
private JTextField textField1 = new JTextField(8);
private JTextField textField2 = new JTextField(8);
private JLabel label1 = new JLabel("Text1");
private JLabel label2 = new JLabel("Text2");
public TestPaints2() {
textField1.getDocument().addDocumentListener(new MyDocListener(label1));
textField2.getDocument().addDocumentListener(new MyDocListener(label2));
TestJComponent2 jComponent = new TestJComponent2();
jComponent.add(textField1);
TestJPanel2 jPanel = new TestJPanel2();
jPanel.add(textField2);
JPanel mainPanel = new JPanel(new GridLayout(0, 2));
mainPanel.add(new JLabel("JComponent"));
mainPanel.add(new JLabel("JPanel"));
mainPanel.add(jComponent);
mainPanel.add(jPanel);
mainPanel.add(label1);
mainPanel.add(label2);
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class MyDocListener implements DocumentListener {
private JLabel label;
public MyDocListener(JLabel label) {
this.label = label;
}
#Override
public void changedUpdate(DocumentEvent e) {
updateLabel(e);
}
#Override
public void insertUpdate(DocumentEvent e) {
updateLabel(e);
}
#Override
public void removeUpdate(DocumentEvent e) {
updateLabel(e);
}
private void updateLabel(DocumentEvent e) {
Document doc = e.getDocument();
int offset = doc.getLength();
try {
String text = doc.getText(0, offset);
label.setText(text);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new TestPaints2());
}
}
class TestJComponent2 extends JComponent {
private static final Color BG = Color.GREEN;
private static final int GAP = 5;
public TestJComponent2() {
setOpaque(true);
setBackground(BG);
setLayout(new FlowLayout());
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
class TestJPanel2 extends JPanel {
private static final Color BG = Color.BLUE;
private static final int GAP = 5;
public TestJPanel2() {
setBackground(BG);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
}
}
I'm trying to create a binary clock in Java using swing.
I encountered a problem on very beginning, where last element of label array is always appearing on the 0 x axis position and in the middle of y axis.
I don't want to use any layout menager since i want few circle images on my Panel.
Here's my code so far:
import javax.swing.*;
#SuppressWarnings("serial")
public class MainFrame extends JFrame {
public MainFrame()
{
setLayout(null);
JFrame frame = new JFrame("Binary Clock");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(600,400);
frame.setResizable(false);
JLabel label[] = new JLabel[5];
for(int i = 0; i < 5; i++)
{
label[i] = new JLabel();
label[i].setLocation(i * 10, i * 10);
label[i].setSize(30, 10);
label[i].setText("TEST");
frame.add(label[i]);
}
}
public static void main(String[] args)
{
new MainFrame();
}
}
i found similar question here:
setLocation of Label
but there is no answer how to do it without layout manager.
I Tried resizing and stuff, but only setText() can do any difference, nothing besides that works.
Thank you!
Pshemo's answer provides the answer as to why you're getting the odd behavior with the last label added, but I've marked up the code with a couple enhancements as well.
Remove the extends JFrame, this is rarely a good idea and you already saw why using an instance of a JFrame within a class that is a JFrame can cause...
Set the layout of the frame instance to null (rather than that of this) that you have originally. Note: Use of a layout manager is the preferred method to creating Swing UI's, and is generally discouraged to set this to null
You don't need the array of labels (unless you plan on using them later), you can just use the loop to add what you want.
Make the frame visible last
Then you have:
import javax.swing.*;
#SuppressWarnings("serial")
public class MainFrame {
public MainFrame()
{
JFrame frame = new JFrame("Binary Clock");
frame.setLayout(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600,400);
frame.setResizable(false);
for(int i = 0; i < 5; i++)
{
JLabel label = new JLabel();
label.setLocation(i * 10, i * 10);
label.setSize(30, 10);
label.setText("TEST");
frame.add(label);
}
frame.setVisible(true);
}
public static void main(String[] args)
{
new MainFrame();
}
}
I am not Swing specialist so I don't know what consequences will give you null layout manager but from what I remember it is nothing good so consider using proper layout manager instead of null.
Anyway it seems that your problem is caused by fact that you set your null layout in currently initialized by constructor this instance, rather than instance from form regerence.
So instead of
setLayout(null);// represents this.setLayout(..), not frame.setLayout(..)
JFrame frame = new JFrame("Binary Clock");
use
JFrame frame = new JFrame("Binary Clock");
frame.setLayout(null);
I am also not sure why your class extends JFrame. Your code doesn't actually look like it needs any of inherited features from JFrame.
I don't want to use any layout menager since i want few circle images on my Panel.
Swing was designed at the core to use layout managers, it how it's update process works and how it deals with the difference in rendering pipelines across multiple platforms. Sure, you can try and do without it, but in the end, you'd simply reinvent the wheel as you try and compensate for all the little niggly issues that are created while trying to do without it.
The following simply uses a GridBagLayout, which provides me with the ability to supply gridx/y positions to the components (and other nice things)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BinaryClock {
public static void main(String[] args) {
new BinaryClock();
}
public BinaryClock() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
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();
// Hour column
gbc.gridx = 0;
addColumn(2, gbc);
gbc.gridx = 1;
addColumn(4, gbc);
// Minute column
gbc.gridx = 3;
addColumn(3, gbc);
gbc.gridx = 4;
addColumn(4, gbc);
// Minute column
gbc.gridx = 6;
addColumn(3, gbc);
gbc.gridx = 7;
addColumn(4, gbc);
gbc.gridx = 2;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.gridheight = GridBagConstraints.REMAINDER;
add(new JSeparator(JSeparator.VERTICAL), gbc);
gbc.gridx = 5;
add(new JSeparator(JSeparator.VERTICAL), gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void addColumn(int count, GridBagConstraints gbc) {
for (int index = 0; index < count; index++) {
gbc.gridy = 3 - index;
add(new Dot(), gbc);
}
}
}
public class Dot extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setColor(Color.DARK_GRAY);
g2d.fillOval(0, 0, getWidth() - 1, getHeight() - 1);
g2d.dispose();
}
}
}
This now frees you up to worry about how to make the clock update without need to continuously try and fix issues in the layout
I am trying to make a GUI maze game, where as the computer tries to solve the maze, it changes the colors of the point in the maze it is on. The maze is made up of a JFrame with a JPanel (GridLayout). In the grid is the JPanels that I need to change their colors. I'm not sure how to even access them after I create them.
My code:
public Maze(int length) {
JFrame frame = new JFrame();
JPanel panel = new JPanel(new GridLayout(length, length, 5,5));
panel.setPreferredSize(new Dimension(500, 500));
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
JPanel p2 = new JPanel();
p2.setBackground(Color.red);
panel.add(p2);
}
}
frame.setDefaultCloseOperation(3);
frame.setTitle("Maze Game");
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
Is there a way to change the color of p2 in a different method? Or is there a better way to do it?
If your have the referee of JFrame then you can do it in this way.
int count = 0;
for (Component comp : frame.getContentPane().getComponents()) {
System.out.println(comp.getBackground());
if (count == 6) {
comp.setBackground(Color.GREEN);
}
count++;
}
Here 6 represent 2nd row and 3rd column as in the same order the JPanel are added in JFrame.
Complete Sample Code [EDITED]
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class Maze {
private JFrame frame = null;
public Maze(int length) {
frame = new JFrame();
JPanel panel = new JPanel(new GridLayout(length, length, 5, 5)) {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
};
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
JPanel p2 = new JPanel();
p2.setBackground(Color.red);
panel.add(p2);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Maze Game");
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void setPanelColor(int index) {
frame.getContentPane().getComponents()[index].setBackground(Color.GREEN);
}
public static void main(String[] a) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
Maze maze = new Maze(4);
maze.setPanelColor(6);
}
});
}
}
Edits:
EventQueue.invokeLater()
All GUI related things in java should always go through a single thread. The thread is our legendary AWT-EventQueue . Hence all GUI related actions should necessarily go through the AWT Event thread. If not so you may end up in a deadlock.
Read more Should we use EventQueue.invokeLater for any GUI update in a Java desktop application?
UIManager.setLookAndFeel()
UIManager manages the current look and feel, the set of available look and feels.
Read more How to Set the Look and Feel
JComponent#getPreferredSize()
Read more Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
The below code represents the problem. Since I have heights of the north and south panels set the rest of it goes to the center panel using GridLayout. I think that since it cannot share the leftover pixels equally among its rows it just leaves them. Therefore in the below code we have ugly white line over south panel.
My question here is: How to make sure that when the GridLayout is not taking the whole space it is at least centered?
Normally I would use TableLayout and situation is sorted, but since I was writing an answer I wanted to use only standard managers. Knowing this would be very useful for me thanks in advance.
Example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class AligningButonsTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
JFrame f = new JFrame();
f.setSize(800, 600);
double CONSTANT_FACTOR = .1;
int noOfRows = 5;
JPanel centerP = new JPanel(new GridLayout(noOfRows,1));
for(int i = 0; i < noOfRows; i++)
{
BoxPanel bP = new BoxPanel();
centerP.add(bP);
}
JPanel contentPane = new JPanel(new BorderLayout());
f.setContentPane(contentPane);
contentPane.add(centerP, BorderLayout.CENTER);
JPanel southP = new JPanel();
southP.setBackground(Color.RED.darker());//southP.setOpaque(false);
southP.setPreferredSize(new Dimension(1, (int)(CONSTANT_FACTOR* f.getHeight())));
contentPane.add(southP, BorderLayout.SOUTH);
JPanel northP = new JPanel();
northP.setBackground(Color.RED.darker());//northP.setOpaque(false);
northP.setPreferredSize(new Dimension(1, (int)(CONSTANT_FACTOR* f.getHeight())));
contentPane.add(northP, BorderLayout.NORTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
class BoxPanel extends JPanel
{
public BoxPanel()
{
setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.RED));
setBackground(Color.DARK_GRAY);
}
}
How to make sure that when the GridLayout is not taking the whole space it is at least centered?
JPanel wrapper = new JPanel( new GridBagLayout() );
wrapper.add( centerP );
contentPane.add(wrapper, BorderLayout.CENTER);
//contentPane.add(centerP, BorderLayout.CENTER);
BoxLayout does a pretty good job of distributing the space between components using Box.createVerticalGlue(). This example uses Box.createVerticalStrut(), top and bottom. The spacers are described in How to Use BoxLayout: Using Invisible Components as Filler.
Addendum: BoxTest2 is a variation that uses BoxLayout to create fixed-size edge panels and vertical glue to distribute the space more evenly. Box.Filler may also be used to control the "leftover" vertical space.
/** #see http://stackoverflow.com/questions/6072956 */
public class BoxTest2 {
private static final int WIDE = 480;
private static final int HIGH = WIDE / 8;
private static final int ROWS = 5;
private static final Box center = new Box(BoxLayout.Y_AXIS);
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
center.setOpaque(true);
center.setBackground(Color.lightGray);
center.add(Box.createVerticalGlue());
center.add(new EdgePanel());
for (int i = 0; i < ROWS; i++) {
center.add(new BoxPanel());
}
center.add(new EdgePanel());
center.add(Box.createVerticalGlue());
f.add(center, BorderLayout.CENTER);
f.pack();
f.setVisible(true);
}
});
}
private static class EdgePanel extends JPanel {
public EdgePanel() {
Dimension d = new Dimension(WIDE, 2 * HIGH / 3);
setPreferredSize(d);
setBackground(Color.red.darker());
}
}
private static class BoxPanel extends JPanel {
public BoxPanel() {
setPreferredSize(new Dimension(WIDE, HIGH));
setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.red));
setBackground(Color.darkGray);
}
}
}
Could you try perhaps nesting this center panel in either a BorderLayout.North or maybe even a FlowLayout.Center.
By this I mean:
JPanel holder = new JPanel(new BorderLayout());
holder.add(centerP,BorderLayout.NORTH);
contentPane.add(holder, BorderLayout.CENTER);
I cannot exactly visualize your problem so it is hard to write a solution.