Center JFrame in combination with pack() - java

I'm trying to center a JFrame i used to pack(), and I got it, but I think it's not the clean way.
This is how I'm doing it atm:
JFrame window = new JFrame();
//filling
//window
//with
//stuff
window.pack();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int x = (dim.width - window.getPreferredSize().width) / 2, y = (dim.height - window.getPreferredSize().height) / 2;
window.setBounds(x, y, window.getPreferredSize().width, window.getPreferredSize().height);
I pack it after filling it to get the final PreferredSizes, so I can use those values in the setBounds method. But I don't like rebounding it after packing it.
Any better ideas?

To center a window in the screen you need to call window.setLocationRelativeTo(null) right after pack() call and before making your window visible:
JFrame window = new JFrame();
...
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
As per Window#setLocationRelativeTo(Component c) docs:
public void setLocationRelativeTo(Component c)
Sets the location of the window relative to the specified component
according to the following scenarios.
The target screen mentioned below is a screen to which the window
should be placed after the setLocationRelativeTo method is called.
If the component is null, or the GraphicsConfiguration associated with this
component is null, the window is placed in the center of the
screen. The center point can be obtained with the
GraphicsEnvironment.getCenterPoint method.
On the other hand
Some developers might advice you to use Window#setLocationByPlatform(boolean flag) instead of setLocationRelativeTo(...) in order to honor the default location for the native windowing system of the platform where your desktop application is running. This makes sense since your application must be designed to run in different platforms with different windowing systems and PLAFs.

Related

Swing layering - transparent component ignores underlying AWT element

First, to get it out of the way, I absolutely NEED to use heavyweight AWT component with a swing application. I need features from both of them.
The task is simple - render a heavyweight AWT Canvas (or any other element), render OpenGL scene directly onto it, then display Swing buttons above it for the UI.
My problem is that it works half-way.
I don't seem to have problems with Z-ordering. I am using jLayeredPanes for it, and I can move Canvas between layers and it actually works, popping on top or below other elements.
The problems are with transparency.
The thing is, Swing elements have Opaque parameter, and when it's set to false (non-opaque) - it should basically be transparent and you should see the next element below it. In my case, however, the AWT Canvas gets ignored, and you instead only see the next underlying SWING element.
Here are a couple of screenshots. They are taken from a standalone test project of mine. The canvas is stretched to the size of the frame, and in the upper left there is a JLayeredPane dummy element that is a simplified version of the menu.
On the first screenshot, the JLayeredPane's Opaque setting is set to true, and you can see that it's background property is set to Blue color.
On the second screenshot, everything is exactly the same but Opaque is set to false. Instead of displaying whatever is on the Canvas - what gets drawn in empty grey jFrame background.
Lastly, on the third screenshot I have put Canvas into a jPanel instead of leaving it on its own. As you can see, the Panel's orange color is seen through the transparent jLayeredPane, but the Canvas is yet again hidden.
Here's the code for the Frame layout. I would not post my rendering/context code right now
frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(width, height));
canvas = new Canvas();
canvas.setSize(width,height);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(width,height);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
Could anyone please explain me what is going on and how to do what I need to do.
I will repeat again - I have to use heavyweight component as render target. I am aware of solutions like JOGL's GLPanel which is a lightweight Swing-compatible component. But I tried that method and the performance is really slow, because instead of directly rendering onto it as a context target - it reads FrameBuffer from memory, flips it, and then paints it as BufferedImage. This path is not fitting for the limited resources of an embedded system that I'll be running on.
c0der said: Please post minimal reproducible example
Errrrm.... Didn't I?
Here, you can have it in complete java class form if you want, but I literally changed some variables for constants.
import javax.swing.*;
import java.awt.*;
public class Main
{
public static void main(String[] args)
{
JFrame frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(500, 500));
Canvas canvas = new Canvas();
canvas.setSize(500,500);
canvas.setBackground(Color.RED);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(500,500);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
}
}
A little update:
I initially suspected that because Swing elements delegate all their drawing to the underlying heavyweight element (In my case JFrame), then what happens is that the frame generates a single frameBuffer for itself and then displays on top of Canvas. Canvas itself is not handled in this generation and thus the frame "covers" over the canvas.
That doesn't seem to be the case. I tried making the JFrame undecorated, all panels non-opaque, and display the picture. The result - canvas is still "cut", and through the hole you can see the underlying IDE menu.
This makes me think that somewhere during Drawing, the Canvas itself detects that it is obscured by another element, and that it doesn't need to draw that area. So it "optimizes" itself and doesn't update these pixels.
Maybe I'm wrong. But here's another screenshot. This is the same example as before, but I took out 3d rendering and simply trying to display Canvas with background set to Red.
Once again, going to reply to my own question.
It turned out that I need to do
setComponentMixingCutoutShape(paneMenu, new Rectangle());
for the menu pane that lies underneath the button. That essentially tells java not to cut out the element from the heavyweight underlying component.

How to Remove Black Rectangle on Window Resize?

I am developing a JFrame window with Swing and AWT, and when I resize the window, it looks like this:
Window resize
(I apologize for the low frame rate, stackoverflow wouldn't accept a larger file size)
As you can see, a lot of times when I resize the window, it shows a black rectangle where it is being resized and it doesn't go away until you pause for a moment. Additionally, the circle doesn't always update accurately with my resize event:
frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent componentEvent) {
width = frame.getWidth();
height = frame.getHeight();
springLayout.putConstraint(SpringLayout.SOUTH, panel, height, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, panel, width, SpringLayout.WEST, frame.getContentPane());
panel.repaint();
}
});
That's beside the point (though it is possible the two problems are linked). I have only ever encountered this problem when using Swing. JavaFX has never given me this problem. Is there any way I can remove the black rectangle when the window is resized?
I have only tested this on Windows 10.
This apparently has to do with the native window decorations of the window that hosts the JFrame. When disabling the native window decorations, you can remove the stuttering resizes and the black background bleeding through. See the documentation for JFrame.setDefaultLookAndFeelDecorated(boolean):
Provides a hint as to whether or not newly created JFrames should have their Window decorations (such as borders, widgets to close the window, title...) provided by the current look and feel. If defaultLookAndFeelDecorated is true, the current LookAndFeel supports providing window decorations, and the current window manager supports undecorated windows, then newly created JFrames will have their Window decorations provided by the current LookAndFeel. Otherwise, newly created JFrames will have their Window decorations provided by the current window manager.
Thus, you have two options. Either set the property once before you create your JFrame:
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame();
Or do it after the creation of the JFrame:
JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setWindowDecorationStyle(JRootPane.FRAME);
Note that in either case, you are bound to use Swing's Look and Feel for the window decorations. The title bar and handles of the window are therefore going to look different than before.

Set resolution manually in Java Swing

I need a way to manually control/stretch the size of my whole GUI.
I have a 1400 x 1050 Desktop (native resolution) and I want to scale the resolution manually to 1024 x 1050 inside the code, because my application was written on/for a 1024 x 768 Desktop. So in general the whole frame and all buttons etc should be stretched / bigger as a whole and the relation should stay the same.
I can´t do it via Windows properties because the resolution in general needs to be 1400 x 1050 because of another application.
My approach was something like:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
screenHeight = screenSize.height;
screenWidth = screenSize.width;
..and then change the screen size via setSize()? But I don´t understand how.
Java Tookit Screen Resolution - How can I use it?
..unfortunately the link in the answer here does not work anymore.
How to set resolution manually in Java Swing?
As mentioned in the comments, the best approach here is to fix the underlying code to not mandate a 1024 x 768 display. This is usually a very distinct code smell of front-end Java code. Proper use of Layout Managers can almost always get your display to function in a more flexible manner.
However, in industry, sometimes refactoring to get legacy components functioning properly is not a feasible effort. In such a case, I would propose that you treat the inflexible GUI you can't refactor as a component of a larger GUI that you can control.
Consider the following example code:
public static void main(String[] args) {
// Initialize the frame
JFrame myApp = new JFrame("App");
myApp.setSize(1400, 1050);
// Create container for the GUI
JPanel container = new JPanel(new BorderLayout());
container.setPreferredSize(new Dimension(1024, 768));
// Load the GUI into the container
JComponent myGui = new JPanel(); // Replace this with actual GUI.
myGui.setBackground(Color.RED); // Remove this once using actual GUI.
container.add(myGui, BorderLayout.CENTER);
// Create the frame's content pane
JPanel content = new JPanel(new FlowLayout(FlowLayout.CENTER));
content.setBackground(Color.BLUE); // Also remove this in production.
// Add GUI to content pane
content.add(container);
// Add content pane to frame, show frame
myApp.setContentPane(content);
myApp.setVisible(true);
}
This works because we've added the GUI to the CENTER of a BorderLayout panel (which will stretch the GUI to occupy the entire size of the panel in the absence of any components on the NORTH/SOUTH/EAST/WEST). We set the preferred size of that BorderLayout panel to be 1024 x 768 (IE: the size that the GUI is specified to work for), and then feed that panel into a FlowLayout panel (which will preserve the preferred size).
The result is that our 1400 x 1050 application contains a 1024 x 768 rendering of our GUI component (you can easily change this to 1024 x 1050 by modifying the preferred size of the panel containing the BorderLayout).
As an exercise to the user, you'll notice that the GUI code isn't centered vertically if you run this. This can be tackled by modifying the layout of the content panel.

Any way to center the center of my JFrame?

I have my own main Frame and two JDialogs that pop up when someone chooses menu items.What i'd like is to have a Help dialog with some rules,suggestions etc.
setLayout(null) and the Toolkit/Dimension do NOT help of course because they center the upper left corner of the frame/dialog in the screen .
How could have the center of both the frame and dialog centered in a screen?
Thanks in advance !
Do these in order:
1) Call pack() or setSize(...) on the frame/dialog to be shown (it should already have its components added at this point).
2) Then, call setLocationRelativeTo(null) on that container. Alternatively, you could call setLocationRelativeTo(parent) if the parent is offset and you want the dialog to be centered within the parent at its current location.
3) Then, call setVisible(true)
setLocationRelativeTo(null) will only center the upper left corner if you Dialog has no size yet, e.g. if you call the .pack() method after the setLocationRelativeTo.
Small example:
JFrame testFrame = new JFrame();
JButton testButton = new JButton();
testButton.setPreferredSize(new Dimension(500, 500));
testFrame.add(testButton);
testFrame.pack();
testFrame.setLocationRelativeTo(null);
testFrame.setVisible(true);
This will show an Frame which center is centered on the screen.
I think that will help:
frame.setLocationRelativeTo(null);
Usually we put a null as an argument, but you can put an argument of type Component, I prefer JOptionPane as it's always shows in center of the screen.
frame.setLocationRelativeTo(new JOptionPane());
//OR
frame.setLocationRelativeTo(JOptionPane.getRootFrame());
And one hint for your, don't use null layout(absolute
positioning), always use LayoutManagers.
Use this helper method to center any JFrame or JDialog:
public static Window centerFrame(Window win) {
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
win.setLocation((int) ((dim.getWidth() - win.getWidth()) / 2), (int) ((dim.getHeight() - win.getHeight()) / 2));
return win;
}
Try to add this to your code:
frameName.setLocationRelativeTo(null);
This will center the frame in your screen. One thing, make sure that you declare the size of your frame BEFORE this line of code. This line should be right before your setVisible declaration

Get Resource Image and Use As JFrame Background [duplicate]

This question has been asked a lot but everywhere the answers fall short. I can get a JFrame to display a background image just fine by extending JPanel and overriding paintComponent, like so:
class BackgroundPanel extends JPanel {
private ImageIcon imageIcon;
public BackgroundPanel() {
this.imageIcon = Icons.getIcon("foo");
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(imageIcon.getImage(), 0,0,imageIcon.getIconWidth(),imageIcon.getIconHeight(),this);
}
}
But now, how do you add a component on top of that background?
When I go
JFrame w = new JFrame() ;
Container cp = w.getContentPane();
cp.setLayout(null);
BackgroundPanel bg = new BackgroundPanel();
cp.add(bg);
JPanel b = new JPanel();
b.setSize(new Dimension(30, 40));
b.setBackground(Color.red);
cp.add(b);
w.pack()
w.setVisible(true)
It shows the little red square (or any other component) and not the background, but when I remove cp.setLayout(null);, the background shows up but not my other component. I'm guessing this has something to do with the paintComponent not being called by the null LayoutManager, but I'm not at all familiar with how LayoutManagers work (this is a project for college and the assignment specifically says not to use a LayoutManager).
When i make the image the background has to display null (and so, transparant (??)) the red square shows up so it might be that the background is actually above my other components.
Does anyone anyone have any ideas?
Thanks
When using null layout (and you almost never should) you have to supply a bounds for every component, otherwise it defaults to (0 x,0 y,0 width,0 height) and the component won't display.
BackgroundPanel bg = new BackgroundPanel();
cp.add(bg);
isn't supplying a bounds. You'll need to do something like:
BackgroundPanel bg = new BackgroundPanel();
bg.setBounds(100, 100, 100, 100);
cp.add(bg);
Which would make bg size 100 x 100 and place it at 100 x, 100 y on the frame.
Look in the documentation on the Root Pane for all the information you need. Note the availability of the layered pane and the glass pane as well as the content pane.
By default all components have a 0 size. Just because you do some painting on a component doesn't give the component a size. You are still responsible for setting the size. That is why you should always use a layout manager. It looks after all this size stuff for you so you don't have to worry.
I don't know why newbies always think they can't use a layout manager. Yes it takes a couple of more minutes to learn, but it saves you a lot of grief in the long run.
Background Panel shows a couple of approaches. Again they both assume you use a layout manager, so you may need to set the size manually.

Categories

Resources