JFrame displaying white bars around content - java

So I've made a class WindowDisp which extends JFrame:
public class WindowDisp extends JFrame{
private static final long serialVersionUID = 3245091489595286109L;
private int height, width;
private JPanel panel;
private JLabel mainPane;
public WindowDisp(int a, int b, int pw, int ph){
height = a;
width = b;
Container c = getContentPane();
c.setPreferredSize(new Dimension(width, height));
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
mainPane = new JLabel();
mainPane.setPreferredSize(new Dimension(pw, ph));
mainPane.setFont(new Font("Monospaced", Font.PLAIN, 10));
panel = new JPanel();
panel.setPreferredSize(getSize());
panel.add(mainPane);
add(panel);
setVisible(true);
pack();
}
public static void main(String[] args){
WindowDisp win = new WindowDisp(400, 400, 400, 400);
}
}
From my Main class, I declare a WindowDisp where its height and width are equal to its ph and pw. The problem, however, is that, upon running my program, white 'bars' appear around the default background colored JPanel in the frame. They appear to be padding the panel from the right and the bottom, as though there is space in the frame that the panel is not occupying, although, if my coding is correct, the panel should be the same size as the frame's ContentPane, should it not?
I've found that removing either of the two pack(); commands does not remove these bars, although removing the first one changes them to black, and removing the second widens the one on the right. Of course, removing both of them causes the frame not to be the same size as its ContentPane. Furthermore, removing the add(panel); altogether has no effect.
I can't figure out what in this code is causing that seemingly empty space to appear in my frame, and, again, in my program, all four values being passed to the Window constructor are equal. What seems really strange is that, even if I just remove the add(panel);, nothing at all changes visa vi the white padding. In fact, I can comment out everything from mainPane =... to add(panel); and that doesn't affect it at all.

I can't seem to replicate the issue exactly, but I think I can replicate the desired results...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class WindowDisp extends JFrame {
private static final long serialVersionUID = 3245091489595286109L;
private JPanel panel;
private JLabel mainPane;
public WindowDisp(int width, int height) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
mainPane = new JLabel();
mainPane.setBorder(new LineBorder(Color.BLUE));
mainPane.setFont(new Font("Monospaced", Font.PLAIN, 10));
panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
};
panel.setLayout(new BorderLayout());
panel.setBorder(new EmptyBorder(4, 4, 4, 4));
panel.add(mainPane);
add(panel);
pack();
setLocationRelativeTo(null);
setVisible(true);
System.out.println(getSize());
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
WindowDisp win = new WindowDisp(400, 400);
}
});
}
}
Things that jump out at me (as been of issue)...
panel.setPreferredSize(getSize()); - The size of the window is generally larger than the window size, this is because the window has decorations which are painted WITHIN the frame boundaries. pack uses the layout information (and preferredSize of the components indirectly) to ensure that the content has the amount of space that it asks for, it then sizes the window around this to accommodate the frame decorations. By calling getContentPane().setPreferredSize you are superseding any information that the layout manager might provide and ignoring the requirements of the other components. This is one of the (many) reasons why we recommend that you NEVER call setPreferredSize, ever...
To reiterate...
Container c = getContentPane();
c.setPreferredSize(new Dimension(width, height));
Forces the viewable space of the window to be set to the width/height values (400x400). This container will no longer be able to react to changes to it's content and will ALWAYS prefer to be 400x400
mainPane.setPreferredSize(new Dimension(pw, ph));
Sets the preferred size of the mainPane to 400x400 (based on your example). Again, it will ALWAYS prefer to be 400x400, but the simple fact of setting the content pane, means that this value is actually ignored...
panel.setPreferredSize(getSize());
Now, the nail in the coffin. This sets the panel to be the same size of the frame, BUT, the frame is larger than the contentPane (400x400 + frame decorations), it is also offset within frame (it won't be positioned at 0x0, but will be offset so that it appears below the frame's title and right border), but could expand beyond the frames boundaries
This combination of issues are all working against you. Instead of worrying about the frame size, worry about the size needs/requirements of the what the frame displays.
Each OS uses different frame decorations, so the actual, physical, frame size will be different on different OSs, but if you focus on the requirements of the content, you won't care

See Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? (Yes.)
Instead use layout padding and borders for white space. Finally, call pack() to ensure the frame is exactly as large as (the smallest size) it needs to be in order to display the components and white space.
import java.awt.Font;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class WindowDisp extends JFrame {
private JPanel panel;
private JLabel mainLabel;
public WindowDisp(int t, int l, int b, int r, String title) {
super(title);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setResizable(false);
setLocationByPlatform(true);
mainLabel = new JLabel("Hello Padding!");
mainLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10));
mainLabel.setBorder(new EmptyBorder(t, l, b, r));
panel = new JPanel();
panel.add(mainLabel);
add(panel);
pack();
setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new WindowDisp(50, 150, 50, 150, "Window 1");
new WindowDisp(50, 100, 50, 100, "Window 2");
}
};
SwingUtilities.invokeLater(r);
}
}

So far the best patch for this annoying issue is the following. Doesn't matter where you call the setResizable(false) method. Just add this piece of code after you setVisible(true).
private void sizeBugPatch() {
while (frame.getWidth() > yourWidth) {
frame.pack();
}
}
Where yourWidth is the width you've set in any of the possible ways, either manually or by overriding setPreferredSize methods. The explanation is quite easy, frame.pack() seems to reset frame.setResizable(boolean b) somehow. You could use an if instead of the while loop but I prefer while to exclude the case the window would still be extra-sized even after a second pack().

One thing I have learned from swing days is to never mix setPreferred... with pack(). You either use one or the other.
Try this code:
import javax.swing.*;
import java.awt.*;
public class WindowDisp extends JFrame {
private int height, width;
private JPanel panel;
private JLabel mainPane;
public WindowDisp(int a, int b, int pw, int ph){
height = a;
width = b;
Container c = getContentPane();
// c.setPreferredSize(new Dimension(width, height));
mainPane = new JLabel("Hello from Pane");
mainPane.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10));
panel = new JPanel();
// panel.setPreferredSize(new Dimension(pw, ph));
panel.add(mainPane);
c.add(panel, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
pack();
setVisible(true);
}
public static void main(String[] args){
WindowDisp win = new WindowDisp(400, 400, 400, 400);
}
}
Output:

Related

JScrollPane not appearing on JTextArea

I'm trying to add the JScrollPane to my JTextArea, but somehow, it won't appear.
I've tried resizing it according to the dimension of the JTextArea, but it doesn't seem to work. Also, notice that I'm using the null layout because I want the full-on flexibility of displaying certain buttons and panels at a pinpoint location.
import java.awt.Dimension;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.JTextArea;
public class PaneTester{
private static JFrame frame;
private static JPanel panel;
private static JScrollPane scrollPane;
private static JTextArea notificationBox;
public static void main (String [] args){
stage1();
stage2();
}
private static void stage1(){
createFrame();
createPanel();
frame.getContentPane().add(panel);
panel.setVisible(true);
frame.setVisible(true);
}
private static void stage2(){
generateNotificationBox();
}
private static void createFrame(){
frame = new JFrame();
frame.setSize(new Dimension(900,700));
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
}
private static void createPanel(){
panel = new JPanel();
panel.setLayout(null);
generateGridButtons();
}
private static void generateGridButtons(){
short y = 0;
for(short i=0;i<4;i++){
y += 60;
short x = 500;
for(short j=0;j<5;j++){
JButton gridButton = new JButton();
gridButton.setBounds(x, y,120,60);
panel.add(gridButton);
x += 140;
}
}
}
public static void generateNotificationBox(){
notificationBox = new JTextArea(10,10);
notificationBox.setBounds(25, 25, 200, 400);
scrollPane = new JScrollPane(notificationBox, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS );
Dimension d = new Dimension(notificationBox.getPreferredSize());
scrollPane.getViewport().setPreferredSize(d);
scrollPane.getViewport().add(notificationBox);
panel.add(notificationBox);
panel.repaint();
}
}
Stop mucking with setBounds and setPreferredSize, you're just making live more difficult for your self.
If you want to affect the size of JTextArea (and the viewable area of the JScrollPane) have a look at the JTextArea constructor JTextArea(int rows, int columns), which will allow you to specify the number of rows/columns you want the JTextArea to default to, and which will allow the JTextArea to calculate it's preferredSize based on the current font's metrics in more stable cross platform way
Your core problem, however, is right here...
scrollPane.getViewport().add(notificationBox);
panel.add(notificationBox);
You add the notificationBox to the JScrollPanes JViewport, which is good, but then you add notificationBox to the panel, which will remove it from the JScrollPane's JViewport, which is bad
Instead, add the JScrollPane to the panel
scrollPane.getViewport().add(notificationBox);
panel.add(scrollPane);
You're also making overuse of static. I'd highly recommend you take the time to reduce static down to it's absolute minimum required usage, this will probably mean that rather then constructing the UI in the main method, you have a "main" class which you can insatiate (from main) which will perform the initial setup - IMHO
I've tried that. I think someone else suggested that from another post, but when I tried that, it just took away the JTextArea completely from the panel
Get rid of panel.setLayout(null); and start making use of appropriate layout managers and compound layouts. Start by having look at Laying Out Components Within a Container for more details

[Miglayout]adjust the size of the container to fit the total size of all components contained, with insets and gap

How can I use MigLayout so that after pack() I can see a JFrame with a proper size to hold all its children components, with borders, insets and gaps?? Now I see some elements cut off, leaving half of the size visible but half cut off.
I just figured out how to guarantee the proper size of a Container according to the sum of size of all the contained components without hardcoding anything.
Create a JPanel panel as your working panel, instead of touching the contentPane. Just add it back to the contentPane. Don't touch the contentPane, it is the key.
Set the layout of panel without hardcoded row height, column width, etc. This may ruin the layout, because your hardcoded height may be lesser or more of it is needed, leaving some line with wrong size, and leave your last line/column half cut off.
Add your elements into panel. When adding them you can specify sizes.
Add panel back to contentPane: getContentPane().add(panel); We don't need to set the layout of contentPane.
At last, pack(), setVisible(true) as you wish. No need to setSize(), setBounds(), etc. The insets and gaps will be handled automatically by MigLayout. Viola!
A SSCCE:
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
public class InsetsAndBorder extends JFrame {
public InsetsAndBorder() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(new MigLayout("insets 2 2 2 2, fillx, debug", "3[]3[]3[]3", "5[]5[]5[]5"));
JLabel label1 = new JLabel("1");
JLabel label2 = new JLabel("2");
JButton button = new JButton("No way!");
panel.add(label1, "cell 1 2, grow");
panel.add(label2, "cell 2 2, grow");
panel.add(button, "cell 0 1, grow");
getContentPane().add(panel);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
InsetsAndBorder frame = new InsetsAndBorder();
}
});
}
}

Why doesn't my button position properly on my JFrame no layout?

So I've got a JFrame which uses setLayout(null) so I can position my elements by hand.
However, when accessing the content pane and getting the size for the frame, it says its height is 1.0.
Does anyone know how I can fix this?
Here is the code:
import javax.swing.*;
import java.awt.*;
public class Launcher extends JFrame
{
public Launcher(String title) {
super(title);
setLayout(null);
pack();
setSize(new Dimension(LauncherUtil.LAUNCHER_WIDTH, LauncherUtil.LAUNCHER_HEIGHT));
setLocationRelativeTo(null);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
displayComponents();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Launcher launch = new Launcher(LauncherUtil.LAUNCHER_TITLE);
launch.setVisible(true);
}
});
}
private void displayComponents() {
Dimension size = getContentPane().getSize();
JButton launchButton = new JButton("Launch Game");
System.out.println(size.getHeight());
launchButton.setBounds(0, (int)size.getHeight() - 60, (int)size.getWidth(), 60);
add(launchButton);
}
}
may be if you try to get the screen size by using Toolkit would be work like you want
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
it brings you the screen size where java program is running. Hope it helps.
The content pane is a JPanel whose default size is 1x1px. Since you did not put any components into the content pane and have not set a preferred size for the panel, the content pane's size remains 1x1px.
One way to fix this is to call these three methods in the following order:
setLayout(null);
setPreferredSize(new Dimension(300, 400));
pack();
However, you should be using a layout manager instead of managing
the size and position of your components by hand.

Java setResizable issue

package data;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JFrame implements Runnable {
private JPanel contentPane;
private JPanel pp = new JPanel();
Thread page = new Thread();
public void run() {
while (!Thread.interrupted()) {
}
}
public Main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 640 + 16, 480 + 39);
contentPane = new JPanel();
contentPane.setBounds(0, 0, 640, 480);
contentPane.setLayout(null);
setContentPane(contentPane);
pp.setBounds(0, 0, 640, 480);
pp.setBackground(Color.BLACK);
contentPane.add(pp);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main frame = new Main();
frame.setVisible(true);
frame.setResizable(false);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
The above code works but setResizable causes an issue: http://i.stack.imgur.com/hQxPU.png
If I were to remove the setResizable then the grey at the bottom and right would be black like it's meant to. How can I disable resizing without causing this issue?
You're using an absolute layout (no LayoutManager set), and the black panel has fixed bounds. And that's exactly the reason the black panel won't fill its parent's bounds when the parent is resized.
Solution: use a LayoutManager which automatically recalculates the bounds of your content so it fills the available space.
// BorderLayout is your friend
contentPane.setLayout(new BorderLayout());
...
// delete this line, no need to set fixed bounds
// pp.setBounds(0, 0, 640, 480);
More on how to use layout managers in AWT/Swing:
The Java™ Tutorials - Using Layout Managers
Layout Managers have two purposes:
calculate the min/max/preferred size of a container
layout components by setting their bounds within the container.
If you want the black panel to have a size of 640x480, and the window to be non-resizable, you can set the preferred size and then pack the window, causing its size to become appropriate for the content's preferred dimensions:
pp.setPreferredSize(new Dimension(640, 480));
...
pack();

JScrollPane usage

I have a JScrollPane and JPanel inside it(I want to draw on JPanel). Also I have a method that draws lines with parametre of length of the line. I want my JScrollPane to scroll if the length of the line is more then the size(height or width) of my JPanel that is on JScrollPane.
How can I do this?
P.S. I've tried jScrollPane.scrollRectToVisible but it doesn't work
Here is an example implementing what you want
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class DrawLine {
JFrame j;
JPanel p;
JScrollPane sp;
public DrawLine() {
j = new JFrame();
p = new JPanel() {
public void paintComponent(Graphics g) {
g.drawLine(20, 20, 250, 250);
}
};
p.setPreferredSize(new Dimension(300, 300));
sp = new JScrollPane(p);
j.getContentPane().add(sp);
j.setSize(300, 300);
j.setVisible(true);
}
public static void main(String[] args) {
new DrawLine();
}
}
Note the line p.setPreferredSize(new Dimension(300, 300)); which sets the preferred size of the panel.
You need to define the following method in the LinePanel class:
public Dimension getPreferredSize() {
return new Dimension(myLine.getWidth(), myLine.getHeight());
}
Make sure that myLine object is available to the above method. And, you will get the scrollbars as needed.
P.S.: I assume LinePanel extends JPanel and is the panel on which the line is drawn.
You need to associate the JScrollPanel with the JPanel that it is scrolling. That is, you need
JScrollPane lineScrollPane = new JScrollPane(linePanel);
This will create a JScrollPane that automatically sizes itself to scroll over whatever linePanel's size is.
It's important to make sure then, that the panel containing your line actually IS big enough to fit entire line. That is, you might have something like
linePanel.setSize(myLine.getWidth(), myLine.getHeight());
or whatever your associated line size is. Note that something like
lineScrollPanel.setSize(new Dimension(600, 400));
will resize the scroll panel but not the thing inside--that is, it will be a 600x400 scroll pane but the inside panel may be bigger smaller, and it will be scrolled.

Categories