i' m programming an application which works with swing components, i notice one thing on which i would an explanation
i have these classes:
this enum on which i instantiate the gui dimension
public enum GuiDimension {
WIDTH(700), HEIGHT(400);
private final int value;
private GuiDimension(int value) {
this.value = value;
}
public int getValue(){
return value;
}
}
this class that starts the application
private GamePanel gamePanel = new GamePanel();
public static void main(String[] args) {
new MainFrame();
}
public MainFrame() {
initGameFrame();
}
private void initGameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(gamePanel);
setResizable(false);
setUndecorated(true);
pack();
setVisible(true);
setLocationRelativeTo(null);
}
}
and this class that set the size of the panel
public class GamePanel extends JPanel {
public GamePanel() {
setPreferredSize(new Dimension(GuiDimension.WIDTH.getValue(),GuiDimension.HEIGHT.getValue()));
//it makes other stuff that are not of interest for this contest
}
}
What I noticed is that, it is true that enums are not really integers but objects ,but when I return
GuiDimension.WIDTH.getValue()
GuiDimension.HEIGHT.getValue()
they return integers that can well be used for other purposes once it has been taken.
now if I insert this on:
SetSize (new Dimension (GuiDimension.WIDTH.getValue (), GuiDimension.HEIGHT.getValue ()));
or
SetSize (GuiDimension.WIDTH.getValue (), GuiDimension.HEIGHT.getValue ());
instead of this,which i inserted in the example
setPreferredSize(new Dimension(GuiDimension.WIDTH.getValue(),GuiDimension.HEIGHT.getValue()));
the frame is displayed with wrong dimension, and I do not understand why.
If GuiDimension.WIDTH.getValue () and GuiDimension.WIDTH.getValue ()) are correct for setPreferredSize (...),
why is not the same for setSize (int,int) and for setSize(Dimension) ?
when tested this simple code you can see that.
Most of the layout managers will ignore calls a component's size but will respect its preferredSize, and sometimes the minimum and maximum, and so when you call pack(), your size will change to what the layout managers and the constituent component preferred sizes think should be the best size.
Incidentally, per kleopatra (Jeanette), if you absolutely need to set a component's preferred size, you're better off overriding getPreferredSize() than by calling setPreferredSize(...). The latter can be overridden by calling setPreferredSize(...) on the same component elsewhere while the former can't.
As an aside, in your example code, you are using WIDTH twice and appear to be not using HEIGHT.
Edit
You had a comment that was deleted regarding pack and component sizes. My reply to it was:
The pack() method requests that the layout managers do their laying out of components, and its the layout managers that matter here -- what do they look at, the size vs. the preferredSizes. If you read the javadoc and tutorials for most of the layout managers, you'll see that they respect the preferred sizes most. Some, like BoxLayout, also look at the maximum size and minimum size as well.
Related
As the title says - say I have a jPanel enclosed in a jScrollPane. How do I find the size, from inside the jPanel, of the jScrollPane it is enclosed in?
Fairly simple question that I get the feeling I should be able to google easily but I haven't found an answer.
You can call Component's getParent() which will give you the direct parent of this component and then you can query its dimensions.
Here is an example, although to be fair, it's the preferred size what I'm checking here. If you run it you will see that "200.0" is printed to the console, as you would expect.
Presumably using getWidth() will get you the actual width too, although that requires a little more knowledge of Swing's internal workings to ensure, since the user is not in direct control of the actual size of the rendered panel.
public class SomeJFrame extends JFrame {
public SomeJFrame() {
setLayout(new BorderLayout());
JPanel parent = new JPanel(new BorderLayout());
parent.setPreferredSize(new Dimension(200, 201));
JPanel child = new JPanel(new BorderLayout());
parent.add(child);
System.out.println(child.getParent().getPreferredSize().getWidth());
add(parent, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SomeJFrame();
}
});
}
}
Finding the JScrollPane around a given Component is a little bit special,
because its immediate parent is actually a JViewport,
and the parent of that JViewport is the JScrollPane.
You can avoid fiddling with these details by using
SwingUtilities.getAncestorOfClass
JScrollPane scrollPane = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, comp);
Dimension size = scrollPane.getSize();
I have a lengthy Java class
public class EditDialog extends JPanel implements ActionListener, ... {
private JButton editDialog;
// lots of code
public EditDialog (String setting){
this.setting = setting;
JPanel main = new JPanel();
// lots of code
}
}
This dialog can be resized but the default dimensions are too small. I want to set larger size by default while keeping the scaling ability. How can I do this? It seems that getPreferredSize and setPreferredSize might be useful, but I can't find an example how to use these functions.
In general you call setSize() on the top level frame/dialog before making it visible.
C.f. Top Level Containers
When I run my JFrame it shows me in hidden size. I have to resize to see JFrame.
Why I cannot get fully sized JFrame.
I used: Pack(); setVisible(true); but it doesn't work.
Here is my code:
public class Mytest extends JFrame{
JLabel label=new JLabel();
JLabel label2=new JLabel();
Timer myTimer;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new Mytest().show();
}
public Mytest(){
getContentPane().setLayout(new GridBagLayout());
setTitle("test");
GridBagConstraints gridCon=new GridBagConstraints();
gridCon.gridx=0;
gridCon.gridy=0;
getContentPane().add(label,gridCon);
gridCon.gridx=0;
gridCon.gridy=1;
getContentPane().add(label2,gridCon);
myTimer = new Timer(1000,new ActionListener(){
public void actionPerformed(ActionEvent e){
myTimerActionPerformed(e);
}
});
pack();
myTimer.start();
setLocationRelativeTo(null);
}
private void myTimerActionPerformed(ActionEvent e){
Date today=new Date();
label.setText(DateFormat.getDateInstance(DateFormat.FULL).format(today));
label2.setText(DateFormat.getTimeInstance().format(today));
}
}
There are two basic problems...
The first is, when you create and add your JLabels, they have no content, therefore their preferred size is 0x0...
The second is the use of pack, from the JavaDocs
Causes this Window to be sized to fit the preferred size and layouts
of its subcomponents. The resulting width and height of the window are
automatically enlarged if either of dimensions is less than the
minimum size as specified by the previous call to the setMinimumSize
method. If the window and/or its owner are not displayable
yet, both of them are made displayable before calculating the
preferred size. The Window is validated after its size is being
calculated.
So, basically, you are adding two labels, whose total combined size is 0x0 and pack is doing exactly what it was designed to, packing the frame to meet the requirements of the layout manager.
You need to seed the values of the labels before you call pack, for example, add a method that updates the labels, for example...
public void updateLabels() {
Date today = new Date();
label.setText(DateFormat.getDateInstance(DateFormat.FULL).format(today));
label2.setText(DateFormat.getTimeInstance().format(today));
}
Then in your constructor, before you call pack, call this method...
updateLabels();
pack();
myTimer.start();
setLocationRelativeTo(null);
(You should also call this updateLabels method from the Timers actionPerformed method to keep it consistent).
This will seed the labels with some content, which pack and the layout manager can use to determine size of the window...
You should also use setVisible over show as show is deprecated and may be removed at some time in the future
You can maximize frame like this :
setExtendedState(JFrame.MAXIMIZED_BOTH);
or you can size frame as per your requirement like
setSize(500,500);
and you have to add below statement as i cant see this in your code
setVisible(true);
After manually swapping components via add and remove, I invoke validate() on the container. According to the documentation,
The validate method is used to cause a container to lay out its
subcomponents again. It should be invoked when this container's
subcomponents are modified (added to or removed from the container, or
layout-related information changed) after the container has been
displayed.
The phrase "lay out its subcomponents again" makes me think that the container will resize itself accordingly, but it doesn't. Instead, after invoking validate(), I need to invoke pack() as well in order to view all its subcomponents.
Why is this? Am I doing something wrong?
I think that you answered your question by yourself, hope help you this demo
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class AddComponentsAtRuntime {
private JFrame f;
private JPanel panel;
private JCheckBox checkValidate, checkReValidate, checkRepaint, checkPack;
public AddComponentsAtRuntime() {
JButton b = new JButton();
b.setBackground(Color.red);
b.setBorder(new LineBorder(Color.black, 2));
b.setPreferredSize(new Dimension(600, 10));
panel = new JPanel(new GridLayout(0, 1));
panel.add(b);
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel, "Center");
f.add(getCheckBoxPanel(), "South");
f.setLocation(200, 200);
f.pack();
f.setVisible(true);
}
private JPanel getCheckBoxPanel() {
checkValidate = new JCheckBox("validate");
checkValidate.setSelected(false);
checkReValidate = new JCheckBox("revalidate");
checkReValidate.setSelected(false);
checkRepaint = new JCheckBox("repaint");
checkRepaint.setSelected(false);
checkPack = new JCheckBox("pack");
checkPack.setSelected(false);
JButton addComp = new JButton("Add New One");
addComp.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton b = new JButton();
b.setBackground(Color.red);
b.setBorder(new LineBorder(Color.black, 2));
b.setPreferredSize(new Dimension(600, 10));
panel.add(b);
makeChange();
System.out.println(" Components Count after Adds :" + panel.getComponentCount());
}
});
JButton removeComp = new JButton("Remove One");
removeComp.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int count = panel.getComponentCount();
if (count > 0) {
panel.remove(0);
}
makeChange();
System.out.println(" Components Count after Removes :" + panel.getComponentCount());
}
});
JPanel panel2 = new JPanel();
panel2.add(checkValidate);
panel2.add(checkReValidate);
panel2.add(checkRepaint);
panel2.add(checkPack);
panel2.add(addComp);
panel2.add(removeComp);
return panel2;
}
private void makeChange() {
if (checkValidate.isSelected()) {
panel.validate();
}
if (checkReValidate.isSelected()) {
panel.revalidate();
}
if (checkRepaint.isSelected()) {
panel.repaint();
}
if (checkPack.isSelected()) {
f.pack();
}
}
public static void main(String[] args) {
AddComponentsAtRuntime makingChanges = new AddComponentsAtRuntime();
}
}
(may be due this ambiguity the description is changed in latest javaDoc )
JavaDoc 7 is NOT saying,
The validate method is used to cause a container to lay out its subcomponents again..
so its only laying the components, whereas you need a pack() again.
Note that pack() clearly says,
Causes this Window to be sized to fit the preferred size and layouts of its subcomponents.
The fundamental, yet subtle assumption at play here: layout and size are directly related, 1-to-1. This is not the case, and is a common assumption in Swing programming. Size is the result of layout and size constraints.
Layout is:
Within the space constraints you've specified
And given the components I have to fit within that space
Position those components in relation to one another given the specified strategy (BoxLayout, BorderLayout, etc.)
If the LayoutManager can fit the components you've given to it, without changing the overall size of the container, it will not alter the size of the container. A call to pack, on the other hand, is an explicit request to minimize the space being used. That's the basic reason you're seeing the results that you are.
Some things you might try:
Make sure you're setting a maximum size on your components/containers, which will force size constraints on components when re-doing the layout
Always call pack() as a habit
Try some of the suggestions regarding common layout issues
It's tricky with Swing, because you've got to understand the painting pipeline, the layout managers, and some details of the windowing system. When it comes to the Swing documentation (and all the methods and the several different ways there are to doing any one thing) I try to read the documentation with an "assume nothing" approach, meaning, "What's the minimum possible thing that this method's documentation implies that it might do," and unless you observe additional behavior, don't get tricked into thinking that it does more than that.
Finally, I would add that the job of LayoutManagers in general is not sizing of containers so much as it is to place components in some relation to one another, according to the layout strategy (this is discussed in additional detail here). The idea is that, with the proper LayoutManager, you specify a basic layout strategy, and as a result when you resize the window they LayoutManager will intelligently move the components around so that your UI continues to follow that overall strategy. In this way layouts are basically meant to be independent of the overall size of the space in which they work, so they try not to make assumptions about what space is available - instead they take the size they are given and try to do what makes sense. Unless you explicitly put size constraints on your components, you can't guarantee what size they will be.
That means, if the LayoutManager doesn't believe that it needs to resize something in order to make it fit its overall strategy, basically it won't resize it. A call to pack, on the other hand, is an explicit request to pack things together and remove extra space.
I just can't get this right. I have a slider to increase my JPanel's size (used as a canvas to draw on).
Whenever the JPanel receives the event, I resize it with setBounds() and I can see it resizing for a split second, but a next Paint or something switches it back to the original size given by the slider's preferred size property.
public class ShapesMainFrame extends JFrame {
private PaintCanvas paintCanvas;
public ShapesMainFrame() {
[...]
JScrollPane scrollPane = new JScrollPane(paintCanvas);
scrollPane.setPreferredSize(new Dimension(1,600));
add(scrollPane, BorderLayout.CENTER);
pack();
}
}
public class PaintCanvas extends JPanel {
[...]
public void setScale(int value) {
setSize(1000,1000);
}
}
So when I try to change the size of the JPanel to a big value it should resize and the scrollbars should appear right? Well it stays the same 600px tall how I set it at the start.
Never use setSize() or setBounds when using a layout manager. Its the "preferred size" that is important. Normally the preferred size of a component is determined automatically by the layout manager. But if you are doing custom painting on the panel you may need to determine the preferred size manually.
The scrollbars will appear when the preferred size of the panel is greater than the size of the scroll pane. Override the getPreferredSize() method (preferred solution) or use the setPreferredSize() method of the custom panel.
All you need to do is call revalidate() on the content within the JScollPane after updating it's size. Also, use the setPreferredSize() when using a layout manager.
public void setScale(int value) {
setPreferredSize(new Dimension(1000, 1000);
revalidate();
}
That will force the JScrollPane to update it's scrollbars.
Also, you could call
paintCanvas.revalidate()
If you wanted to update the JScrollPane from outside of your paintCanvas class