I have a JPanel with one JLabel, two JButtons and a GridBagLayout as the layout manager. The problem that I now have is that when I add all the components to the panel (using the GridBagConstraints of course) only the label is fully displayed and all of the buttons are cut off.
Half of the text on the buttons is missing and they're way smaller than they're supposed to be. Even when I try to specify the size of the buttons inside my code with
.setSize(new Dimension(width / 2, 100))
it still does not work.
Here's the relevant code of my panel:
public class StartPanel extends JPanel {
private int width = 32 * 32;
private int height = 32 * 18;
// Buttons
private JButton[] buttons;
private JButton startButton;
private JButton exitButton;
// Label
private JLabel titel;
// Andere Objekte
private PassivThread passivThread;
private GridBagLayout gbl;
private GridBagConstraints gbc;
private Fenster fenster;
public StartPanel(PassivThread passivThread) {
this.passivThread = passivThread;
setPreferredSize(new Dimension(width, height));
setBackground(Color.black);
gbl = new GridBagLayout();
gbc = new GridBagConstraints();
setLayout(gbl);
loadObjects();
buttonEvents();
setVisible(true);
}
// Hier werden die Objekte platziert
private void loadObjects() {
startButton = new RoundedBorder("Spiel starten", 75);
exitButton = new RoundedBorder("Zurück zum Desktop", 75);
buttons = new JButton[2];
buttons[0] = startButton;
buttons[1] = exitButton;
Font font = new Font("Monospaced", Font.BOLD, 40);
titel = new JLabel("Jump 'n' Run");
titel.setFont(font);
titel.setForeground(Color.red);
titel.setVisible(true);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridheight = 2;
gbc.insets = new Insets(20, 0, 20, 0);
add(titel, gbc);
for (int i = 0; i < buttons.length; i++) {
buttons[i].setForeground(Color.white);
buttons[i].setBackground(null);
buttons[i].setFont(font);
buttons[i].setBorder(null);
buttons[i].setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
buttons[i].setSize(new Dimension(width / 2, 50));
gbc.gridheight = 1;
gbc.gridy = 2 + i;
add(buttons[i], gbc);
}
}
}
And an example of what I mean:
2 Answers 2
This happens when you assume a particular size of rendered text.
Your code contains this:
Font font = new Font("Monospaced", Font.BOLD, 40);
What does that 40 mean, exactly? It means you are creating a 40-point font.
And what is a 40-point font? What does "40 points" mean? A point is a typographic measurement. It has a very specific size in print media, but when it comes to screen graphics, it means exactly 1⁄72 inch. A 40 point font is a font where one line of text is 40⁄72 inch high.
How many pixels does it take to display characters of a font that size? How many pixels are in one inch?
The answer depends on the video mode of the desktop where the program is running, and the dot pitch of the monitor where the program’s window is displayed. (And probably the desktop system’s display scaling, as well, at least in Windows.)
That’s a nontrivial bit of math, isn’t it? You could do all that math yourself... but you don’t have to. Swing already does it for you. All of that math is already done, and is made available in the default preferred size of any component which displays text, including JButtons and JLabels.
Furthermore, most layout managers will respect those preferred sizes, and will set a container’s preferred size based on the child components’ preferred sizes.
Assuming an exact pixel size for any of these computations is basically guessing. As you have seen in the comments, it works for some people, but not for others. Your hard-coded dimensions are correct on some monitors and desktops, but incorrect on others.
To take advantage of the correct computed size provided by Swing, all a program has to do is... avoid overriding the default preferred size of each component. Normally, a program does this by never calling setSize or setPreferredSize, and instead calling pack() on the containing window.
Here is a short demo version that creates a panel similar to yours:
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.Color;
import java.awt.Font;
import java.awt.Cursor;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JFrame;
public class StartPanel {
private JPanel panel;
private JButton startButton;
private JButton exitButton;
public StartPanel() {
startButton = new JButton("Spiel starten");
exitButton = new JButton("Zurück zum Desktop");
JButton[] buttons = { startButton, exitButton };
Font font = new Font("Monospaced", Font.BOLD, 40);
JLabel titel = new JLabel("Jump 'n' Run");
titel.setFont(font);
titel.setForeground(Color.red);
panel = new JPanel(new GridBagLayout());
panel.setOpaque(true);
panel.setBackground(Color.BLACK);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridheight = 1;
gbc.insets = new Insets(20, 0, 20, 0);
panel.add(titel, gbc);
for (JButton button : buttons) {
button.setForeground(Color.white);
button.setBackground(null);
button.setFont(font);
button.setBorder(null);
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
gbc.gridy++;
panel.add(button, gbc);
}
}
public void showInWindow() {
JFrame frame = new JFrame("Start");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new StartPanel().showInWindow());
}
}
Notice there are no calls to setSize or setPreferredSize. This allows Swing to accommodate the size of the text, for any and all displays it may run on.
Some other notes:
- setVisible is not needed. All components are visible when they are created, except for instances of Window and its subclasses.
- GridBagLayout uses grid-based alignment, but its grid is flexible. It is not a regular grid. The rectangular cells of the grid are not all the same size. So, when you say
gbc.gridheight = 2, you are not laying out components so they will be twice as high as components with gridheight=1. GridBagLayout makes each grid cell as wide and as high as necessary to accommodate its child components, nothing more. - It is not a good idea to subclass Swing or AWT component classes unless necessary. Usually the only reason to do so is to override the paintComponent method, which obviously is not necessary here. Inheritance should be used only when necessary, not whenever possible.
Comments
I have solved the porblem myself; I used a roundedBorder button, instead of a normal one. I switched the buttons and now it's working.
3 Comments
buttons[i].setSize(new Dimension(width / 2, 50)); this isn't great practice. The component should know it's own size so the layout manager can work.
PassivThread,FensterandRoundedBorder. Specifically, I replacedRoundedBorderwithJButtonso the problem could be in classRoundedBorder.getBorderInsets()methods needs to be implemented correctly.