I have window containing multiple panels. I don't have access to window code. (I can modify only panel’s code.)
I removed few components from panel. Window has shrunk its size. But window is too small to display everything correctly.
I added line setPreferredSize(getPreferredSize());. Now window have right size.
What are side effects of setPreferredSize?
Edit: BorderLayout is used. Which should ignore getXXXSize(). My panel is in CENTRE. Panel which doesn't fit the screen is on NORTH.
This is what is happening:
getPreferredSize() looks whether the size was set before. If not, the method asks the LayoutManager of the component itself (which is your JPanel) about the preferred size, which is then calculated from the components.
setPreferredSize(...) then sets this value on the JPanel, memorizing it for later.
Later you remove some components of the JPanel.
Even later, when the window tries to re-layout itself (or is told to do so), the window's (or contentpane's/RootPane's/...) Layoutmanager calls your JPanel's getPreferredSize() method again.
now getPreferredSize() does not ask the JPanel's LayoutManager, but simply returns the stored size previously set by setPreferredSize().
For width, the BorderLayout is ignoring the preferred width of the NORTH and south component, it only takes CENTER, EAST and WEST into account. (Similarly for height).
I just took a look at the implementation of BorderLayout.preferredLayoutSize (in 1.6.0_13 from Sun), and it works like this:
The width is calculated as
max( EAST.width + CENTER.width + WEST.width + h-gaps,
NORTH.width, SOUTH.width ) + insets
The height is calculated as
max( EAST.height, CENTER.height, WEST.height)
+ NORTH.height + SOUTH.height + v-gaps + insets
(Each of the width/height are the values of the preferredSize of these components.)
If some of the five components are missing, their height/width is not included, neither are the gaps.)
It works the same for minimalLayoutSize, while maximumLayoutSize simply returns Integer.MAX_VALUE.
So, in principle it should work out of the box.
But in general, if the layout of the window is not under your control, you should not have to worry about components not under your control being cut off :-)
Yes, it has a lot of effect. The effect depends on
the layout manager in use
component on which the preferred size is set.
The main reason is that once you call setPreferredSize() to set the size, the component will no more ask ui to get the prefered size. Ideally, only the ui delegate of your component knows the ideal pref size of the component. Once you set the pref size yourself, the ui will not be queried for pref size.
For clarity see the Jcomonent.getPreferredSize() code:
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
Dimension size = null;
if (ui != null) {
size = ui.getPreferredSize(this);
}
Now why does it depend on layout-manager, because only some layouts call(use) getPreferredSize() for computation. For e.g flowlayout uses pref size but border layout doesnt
Method setPreferredSize() is side effect free.
How getPreferredSize() works was described by Suraj Chandran.
Why setPreferredSize(getPreferredSize()) worked is explained by Paŭlo Ebermann.
Related
I'm trying to paint a keyboard with keys that are dynamically resizing when the JFrame is resized. Also, the keys can be in different size.
I have a panel which has its layout set to BorderLayout. In the center I put a panel which has its layout set to GridLayout(5,1).
Each row of the grid is a JPanel with FlowLayout. In each of the rows I'm calculating each button and set its preferred size by calling the setPreferredSize method.
When running the program I see only some buttons in a weird appearance. When I'm checking the row panels with getWidth()/getHeight() and with getPreferredSize().width/height, I'm getting different values: For getPrefereedSize().width , I'm getting the width that I really set, but for getWidth() I'm getting a significantly lower value.
Is there a difference between both methods, getWidth() and getPreferredSize().width?
(There is a counterpart of this question: Java: Difference between the setPreferredSize() and setSize() methods in components. And there are some other questions regarding getSize() and getPreferredSize(), but I didn't find one that could be considered as a duplicate. So here we go...)
A component in Swing usually has a preferred size. This is the size that is returned when calling the getPreferredSize method. It is the size that the component would "like" to have, in order to be displayed properly. For example, a JLabel with a certain text and font will have a preferred size that is just large enough to display the whole text.
But when a component is displayed on the screen, then it may not be possible to show it with its preferred size (some reasons will be explained below). So when calling getSize, the method will return the actual size that the component currently has on the screen. When the containing frame is resized, then the component may also be resized, and then return a different value again.
(Note that getSize() basically returns a single object that contains the same values as those returned by getWidth() and getHeight()).
Why it is not always possible make sure that the actual size matches the preferred size:
The layout in Swing is usually handled by layout managers. The details are explained in the tutorial about Laying Out Components Within a Container, but I'll try to summarize the part that is relevant for this question here.
Such layout manager will try to lay out components with their preferred size. One can imagine that this is difficult, and sometimes impossible. Imagine a panel with a GridLayout(1,2) that contains two other panels:
+---------------------+---------------------+
| | |
| Preferred Size: | Preferred Size |
| 400 x 300 | 100 x 200 |
| | |
+---------------------+---------------------+
In the GridLayout, all components must have the same size. But their preferred sizes are different. So the layout manager simply cannot make sure that the actual sizes of the panels are the same as their preferred sizes. So it displays them with a size that is a compromise between the two. (It may also take into account the minimum and maximum size, making it even more difficult...). In the end, the size of both panels may, for example, be (300,200) pixels, even though the preferred sizes have been (400,300) and (100,200) pixels.
Specifically regarding the layout that you described: Note that FlowLayout is a bit special here. It will lay out the components with their preferred size, if there is enough space. If there is not enough space, then some components may not be visible. So you might try changing the FlowLayout in your rows to be a GridLayout(1,n), where n is the number of keys in this row.
The component's getWidth/getHeight return the current sizes. The getPreferredSize returns what the component would like to get. For example, a button with text will return a preferred size so the text will completely visible.
A reasonable layout manager will ask the child components for their preferred sizes and give them these sizes. This will result in preferred size for the parent component. If the parent component is set a smaller size, it can't enforce the child component's preferred sizes any more and either hides some or makes the components smaller.
I am building a small java application that deals with taking screenshots.
I have a custom class that inherits from JPanel called ScreenshotPanel that holds the image, and it is inside a JScrollPane. The ScrollPane moves and resizes when the frame is resized.
All works well except the zoom part, I want to be able to control the size of the screenshotPanel inside the ScrollPane, however the screenshotPanel doesn't resize, it flickers at the right size for a moment but then it's being resized back to the ScrollPane size.
If I use the screenshotPanel without the ScrollPane it works exactly right, but I want it to automatically add scrollers so you can see move the image around while zoomed-in.
Here is the part of the code I use to resize:
double AR=((double)screenshotPanel.getImage().getHeight())/screenshotPanel.getImage().getWidth();
screenshotPanel.setSize((int)((getWidth()-180)*zoomFactor), (int)((AR*(getWidth()-180)-100)*zoomFactor));
scrollPane.setSize(getWidth()-180, (int)(AR*(getWidth()-180))-100);
the -180 and -100 are used to keep a space for buttons and zoom control componenets (which all move and resize perfectly), zoomFactor is a double that hold the amount to zoom.
How do I resize the screenshotPanel inside the ScrollPane without the ScrollPane forcing it's size back?
Changing the size of the component won't scale the image, unless you have code in your paint method to compensate for the change in size.
JViewPort is respecting your components preferred size, thus you get this flicker, as you change the size and the viewport resets it.
Add a scale method to your panel and override the getPreferredSize method.
When you call setScale, calculate the new scaled size. Make sure that the getPreferredSize method returns these values.
You will need to call invalidate and possibly repaint to make sure the change is updated through the container hierarchy.
Remember, the pane will not resize the image on it's own, you are responsible for taking this ;)
I have a JDialog and I want to have it a certain, given size:
JDialog dialog = new JDialog();
dialog.setSize(800, 600);
dialog.setResizable(false);
Then I add a component:
JLabel label = new JLabel("Test");
dialog.add(label);
Now I could make the dialog visible and check the size of the component
dialog.setVisible(true);
System.out.println(label.getSize());
The answer would be "[width=784,height=562]". Obviously the component was resized to fill the whole client area / content pane of the dialog window. That's fine and as I want it.
Question: How can I obtain the final size of the components before calling setVisible(true)?
getPreferredSize() will not be the size I want because I want the component to adapt to the given dialog's size
dialog.pack() is also not the right thing because it resizes the dialog to the preferred size of the components which I don't want
dialog.validate() does nothing useful here, the size of the component is still 0
dialog.getLayout().layoutContainer(dialog) also does not set the size of the components
So I am at a loss here. I want to make the layoutmanager calculating the right sizes of all components and sub components before showing the dialog, adapted to the overall size of the dialog. But I don't know how.
I now found that it can be done as follows:
JDialog dialog = new JDialog();
JLabel label = new JLabel("Test");
dialog.add(label);
// pack(), setSize(), validate() in this order will
// set sizes on all components as wished
dialog.pack();
dialog.setSize(800, 600);
dialog.validate();
System.out.println(label.getSize());
Also here the output is "[width=784,height=562]" but the dialog is not yet visible. The important part is the combination of pack(), setSize(desiredSize) and validate() in this order. The pack() probably determines a new size of the dialog (preferred sizes of all components), that's why here the size has to be set afterwards and the validate() is responsible for the resizing of the components. Probably setVisible(true) which arrives at the same sizes is doing internally something similar.
It seems a bit of a waste to resize the components several times but without pack() also setSize() and validate() do not have any effect.
I guess the other answers were based on some misunderstanding because they implicitly always assumed that you want to have the preferred size, but there are cases, e.g. if the user resizes the dialog or if the dialogs size is fixed from the beginning where you cannot attain the preferred size and some components just have to fill the available space.
That was the layout problem here, having a given global size of the dialog and determining the size of the components as they fill the available space. LayoutManagers solve this problem quite nicely, however usually only after setVisible(true).
I tested a bit more:
// new dialog
JDialog dialog = new JDialog();
// new label, prints messages if resized or painted
JLabel label = new JLabel("Test") {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Component painted.");
}
};
label.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
System.out.println("Resized: " + e.getComponent().getSize());
}
});
dialog.add(label);
System.out.println("Size after new JLabel: " + label.getSize());
// pack dialog - necessary for setSize/validate to work
dialog.pack();
System.out.println("Size after pack: " + label.getSize());
// set a size and validate changes sizes
dialog.setSize(800, 600);
dialog.validate();
System.out.println("Size after setSize and validate: " + label.getSize());
// set visible would have also done the trick
dialog.setVisible(true);
System.out.println("Size after setVisible(true): " + label.getSize());
// and another resizing (no validation neccessary)
dialog.setSize(300, 200);
// dispose
dialog.dispose();
And the output is
Size after new JLabel:java.awt.Dimension[width=0,height=0]
Size after pack: java.awt.Dimension[width=116,height=16]
Size after setSize and validate: java.awt.Dimension[width=784,height=562]
Size after setVisible(true): java.awt.Dimension[width=784,height=562]
Resized: java.awt.Dimension[width=284,height=162]
Resized: java.awt.Dimension[width=284,height=162]
Component painted.
I learned more about the inner workings of Swing:
ComponentResized events are not fired before setVisible(true) even if components are resized (their size changes)
ComponentResized events even with the same size can be fired several times in a row
Components might not be painted in between resizing if they follow each other fast enough
The first painting is in any case after setVisible(true) and the component will have the desired size (preferred size or defined by other constraints as here) by then.
If for some reason you must know the size of the components before the first drawing, do it with pack(), setSize(), validate()
I tested some more, also with maximized frames and now can combine all the results into: The first painComponent() is always with the right size and the related componentResized() event always follows afterwards, sometimes two times.However the LayoutManager must know before, otherwise the examples would not be drawn correctly. So in case one draws the background by itself, either read out the right size in every paintComponent or implement a custom layout manager or wait for the resized event and invoke repaint, so the component is drawn two times but it should work. Apllications include cases where the number of components to show depend on the size (as in my geographical map application).
Just to complete the picture I think the flow goes like this in case a user maximized or resized a frame/dialog:
frame/dialog.setSize()
LayoutManager.layoutContainer(frame/dialog) using the actual size
frame/dialog paint() using the layouted sizes
Resized() events fired for all components etc.
And pack() probably just calls setSize(layout.preferredLayoutSize()) as the first step.
So in case depending on the size you have to add or remove components for example, it could be a good idea to override setSize() and listen there for changes. I initially was listening for Resized() events but they arrive too late for the first drawing.
Top-Level Containers return own Size, PreferredSize, Bounds in two cases (if they are)
already visible
after call pack(), in your case dialog.pack();
have to calculating with Borders and ToolBar came from Native OS
have to get this size from Top-Level Containers#getContentPane().getWhatever
most of JComponents returns own PreferredSize, then there no reason to sizing for Standard Layout Manager
I have a JPanel that is located at the BorderLayout.SOUTH position of a parent JPanel. I would like to programatically change the height of this child JPanel.
I am calling childPanel.setPreferredSize(new Dimension(getWidth(), newHeight));, but it has no effect. (I have tried setting the Maximum and Minimum sizes as well.)
What (probably very simple thing) am I missing?
After changing the preferred size, you need to call revalidate() on that childPanel.
You may want to read this Oracle Tutorial on LayoutManagers
I need to find a component's exact screen size in pixels when the main JFrame is resized.
I've tried several things and couldn't find an easy way to do it: it probably have missed something obvious.
BBBBB JFRAME BORDER BBB
BZZZZZZZZZZZZZZZZZZZZZB
BAAAAAAAAAAAAAAAAAAAAAB
BCC1................DDB
BCCC................DDB
BCCC................DDB
BCCC................DDB
BCCC................2DB
BEEEEEEEEEEEEEEEEEEEEEB
BBBBBBBBBBBBBBBBBBBBBBB
Resized, it could become this if, say, the user made the main JFrame shorter (vertically) and wider (horizontally):
BBBBBB JFRAME BORDER BBBBBB
BZZZZZZZZZZZZZZZZZZZZZZZZZB
BAAAAAAAAAAAAAAAAAAAAAAAAAB
BCC1....................DDB
BCCC....................2DB
BEEEEEEEEEEEEEEEEEEEEEEEEEB
BBBBBBBBBBBBBBBBBBBBBBBBBBB
What I want is to find the exact size in pixels, on screen, of the rectangle area indicated by dots.
I'm adding a ComponentListener to get the resizing events, which works fine.
The issue I'm having is that calling getWidth()/getHeight() on a Component does apparently not return the component's screen size but the component's actual size (and you can have, for example, a JPanel wider than the component it is into).
Any hint most welcome.
You're looking for JComponent.getVisibleRect().
getWidth() and getHeight() returns the width and height of the component. I've never heard of any difference between "actual" width/height and "screen" width/height.
Some layout managers however, does not fill the panel with a component it contains, and some space between the component and the panels edge may not be occupied by the component. In that case I usually setBackground(Color.BLUE) on the underlying panel to see what's going on.
This is a very rubbish way but you can use a wrapper Panel, where you put your main panel into. Than you can call getWidth() and getHeight() from the wrapper panel.