My question have 3 parts.
I would like to know, how can I set JFrame window to be resizable, but can not go under a minim size so the user cant make it smaller then the minimum size.
I would like to know, how can I set JFrame window to be resizable, but still keep the aspect ratio (16:9) even if the user is resizing it.
I would like to know, how can I make the program become really full screen after he clicks a button so there are no borders and runs like a game or something (and if there is any specific issues how can I revert it back safely).
Thanks for your help and patient for my not perfect English and not perfect Java knowledge.
The provided answer is useful and answers part of my question, but still most of it still blank for me.
The part of the code:
private void createDisplay() {
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
frame = new JFrame(title); //setting the title of the window
frame.setSize(width, height); //setting the width and height of the window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //make sure program closes down properly
frame.setResizable(true);
frame.setLocationRelativeTo(null); //the window will apparel on the center
frame.setVisible(true);
frame.setIconImage(Assets.ImageMap.get("icon"));
canvas = new Canvas();
canvas.setPreferredSize(new Dimension(width, height));
canvas.setMaximumSize(new Dimension(width, height));
canvas.setMinimumSize(new Dimension(gd.getDisplayMode().getWidth() / 2, gd.getDisplayMode().getHeight() / 2));
canvas.setFocusable(false);
frame.add(canvas);
frame.pack(); //resize the window a little bit so able to see all of canvas
}
Set the frame's minimum size (setMinimumSize)
Is more difficult, as you are notified of the size change after the fact, so you'll always be fighting the system as it changes (and then you run into problems with who generated the size change event). It might be better to focus on keeping the ratio of the contents of the frame based on the available space within the frame (VLC is a good example of this)
Full screen exclusive mode
Related
I am creating a JFrame, using a custom look and feel named FlatLaf. But I am experiencing weird behavior when I resize the window. I can replicate the exact same problem with this snippet:
import com.formdev.flatlaf.intellijthemes.FlatOneDarkIJTheme;
import javax.swing.*;
import java.awt.*;
public class Main{
public static void main(String[] args){
try{ UIManager.setLookAndFeel(new FlatOneDarkIJTheme()); }
catch(Exception e){}
JFrame frame = new JFrame();
frame.setSize(1280, 720);
frame.setMinimumSize(new Dimension(750, 400));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
frame.setVisible(true);
}
}
With that I can get what I need, this:
JFrame with Look and Feel
To achieve that I used the instructions frame.setUndecorated(true); and
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME); (Because as I said, I don't want the default system border, like this: Frame with default system border)
The problem is when I resize from the left and the top of the frame, it starts to move in that direction, and I haven't find a solution, here is a gif showing that: JFrame with weird resize behavior.
That problem is obviously solved when I remove the lines:
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
Because I get back the system border, but that's not what I want.
How can I fix this?, Is there a better way to do it (Or different)?.
The problem is when I resize from the left and the top of the frame, it starts to move in that direction
This effect happens because you are setting the minimum size of the JFrame, together with the fact that you clear the frame decorations. So don't set the minimum size, or just leave the decorations enabled. If you leave the decorations enabled and still set the minimum size, then the user will be able to resize the frame until it reaches the minimum corresponding dimension where, instead of moving, the frame will stay in place.
As for the title bar, in case you need all of its good characteristics (such as the 3 buttons on the right, the title and icon of the frame on the left and the bar in the middle which can be actually dragged by the user to move the frame), but you also want it to have a specific color, then don't undecorate it and only change the color of the title bar component, which can be done like so:
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatLaf;
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(final String[] args) {
final FlatDarkLaf laf = new FlatDarkLaf();
FlatLaf.install(laf);
final Color controlColor = laf.getDefaults().getColor("control"); //Obtains the background color of FlatDarkLaf.
//Change the title bar active and inactive color...
UIManager.put("TitlePane.inactiveBackground", controlColor); //This is the color of the title bar when the frame doesn't have focus.
UIManager.put("TitlePane.background", controlColor); //This is the color of the title bar when the frame has focus.
final JFrame frame = new JFrame("Title");
frame.setSize(1280, 720);
frame.setMinimumSize(new Dimension(750, 400));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
In this case, you have to leave the decorations enabled. But then that seems ok because you want the characteristics of the title bar, just with a different background color in it.
I found out about the properties/keys "TitlePane.inactiveBackground" and "TitlePane.background" in the documentation of the source code of the class FlatTitlePane which as someone can see (in the source code of FlatRootPaneUI) this is the class being installed on the root pane of the frame as its title bar.
I've seen about the laf.getDefaults().getColor("control"); part in the source code of FlatLaf (in the getDefaults method).
According to some instructions of the FlatLaf's web page, you should better set those properties after installing the LAF and before creating the components.
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.
I have had this problem a few times before, and I finally attempted to solve it, but I cannot fond the right solution. I have a simple JFrame program. The basic JFrame code looks like this:
JFrame frame = new JFrame("halp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setResizable(false);
frame.setVisible(true);
Here's an image of what I'm trying to accomplish:
As you can see, there's space taken up by the top bar and the borders of the window. One way to fix this is to increase the size of the window, but that isn't neat at all, and also not every system has the same border sizes of the window.
I've done research and tried a few things I found on earlier asked questions:
frame.pack(). This method only set the window to minimal size, as if I didn't set the size at all.
frame.getContentPane().setSize(500,500);. The window still has the borders counted in the frame size.
frame.setPreferredSize(new Dimension(500, 500)); and JPanel.setPreferredSize(new Dimension(500, 500));. The frame went into minimum size, as with frame.pack().
I tried combining these methods, but nothing seemed to work.
What am I missing? Is there another aspect of the JFrame I need to add? Is it an incompatibility issue of BlueJ?
frame.getContentPane().setPreferredSize(new Dimension(500,500));
followed by a
frame.pack();
should solve your problem.
I have a frame of 100x100 and an image of 400x400, I have a layered pane on the entire frame and the image inside the layered pane. As you can notice the image is bigger than the frame so only a part of the image i.e. the top-left part of the image will be shown. Suppose there is a man in the image at X=32 and y=40 and width = 10 and height = 10 (basically a rectangle) w.r.t. the frame.
Questions
the image is 400x400 right? That means at runtime I can only see 100x100 part of the image and the part of the image that has been shown is the top-left part right? But suppose I want to show a building that is situated at 350x350 w.r.t. the image, then how do I do it (since it is located in the bottom part of the image so it wont be shown)?
suppose the first question is solved, then assume there is a building at 350x350 with width = 20 and height = 20 w.r.t the 'frame'. Now when at runtime I show the image (now the bottom part of the image is visible, assuming question 1 is solved) the co-ordinates of the building w.r.t. frame should not change i.e. they should remain 350x350 w.r.t. the frame.
Point 1) I'm guessing you're adding the image in a JPanel and then into the JFrame or the image into a JPanel which goes into a JScrollPane which finally lands inside the JFrame. JPanels and JScrollPanes both support programmatic scrolling through the use of #scrollRectToVisible.
Here's a (kind of) working sample of the scrollRectToVisible
JPanel panel = new JPanel();
JPanel panel2 = new JPanel();
panel2.setPreferredSize(new Dimension(200, 11000));
SpringLayout layout = new SpringLayout();
panel2.setLayout(layout);
for (int i = 0; i < 100; i++) {
JLabel textField = new JLabel();
textField.setText("textfield number " + i);
textField.setPreferredSize(new Dimension(150, 100));
layout.putConstraint(SpringLayout.NORTH, textField, i * 110, SpringLayout.NORTH, panel2);
panel2.add(textField);
}
JScrollPane scrollPane = new JScrollPane(panel2, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(200, 500));
panel2.scrollRectToVisible(new Rectangle(0, 800, 150, 100));
panel.add(scrollPane);
Add the panel variable to a JFrame and you're good to run this code. Also, please note that this is meant to be sample code, not the actual way code should be written to add components to panels :P
Point 2) I still don't fully understand this query so I'll give you an answer in 2 parts.
a) I don't see why relative calculations matter? Can you try the sample I provided and see if it does what it needs to? If not, read option b
b) the Rectangle object has a setLocation(int, int) (Reference: Java documentation) that you should play around with. I can't give you a more accurate answer or a code sample since I don't full understand what you're trying to do and I don't have a code sample that I can play with.
I'm a new user on Swing, and I have problem with drawing components by coordinates. Please, look at this code:
JFrame frame=new JFrame();
frame.setBounds(new Rectangle(0, 0, 700, 600));
frame.getContentPane().setBackground(Color.yellow);
frame.setVisible(true);
JPanel graph=new JPanel();
graph.setBounds(new Rectangle(0, 0, 700, 300));
graph.setBackground(Color.white);
graph.setOpaque(true);
frame.getContentPane().add(graph);
I need that JPanel closes 50% from JFrame, but now it closes 100%, and JFrame has white color for background. How should I fix the mistake? Also, is there any mean for setting width and height using percents? For example, 50% for width. Or may be exists any containers for my question? Thank you
don't use whatever#setBound(), use Standard LayoutManager, in this case is GridLayout(2, 0) best of ways how to do it, your JFrame and its JPanels will be resizable on both directions
If you really want absolute positioning you could try disabling the default layout manager, as shown in Doing Without a Layout Manager. However, most of the time it's best to (depending what your container is intended to show) choose an appropriate layout manager.