I have a swing component where the ideal size of the component will vary based on the content that is is currently being displayed (which happens in this case to be an image).
I'd like this preferred size to change when the content is changed, and also allow the layout to be changed (for example, if the component is being used inside a JScrollPane then the scroll extents would change to fit the size of the component).
What is the canonical way to do this in Swing ?
Suggestions:
Use a class extends JPanel (or JComponent),
give it a getPreferredSize() method override where you return a Dimension with the parameters that you desire.
For instance if the diameter will be based on a BufferedImage, you could have something like:
getPreferredSize example:
public Dimension getPreferredSize() {
if (myBuffImg != null) {
return new Dimension(myBuffImg.getWidth(), myBuffImg.getHeight());
}
// default return value
return super.getPreferredSize();
}
Edit
Regarding your comment:
how would you handle the case of the component's content image changing though? is it just a a case of triggering a re-layout in the surrounding container?
You'd give this class a setImage(Image image) method of course, and you could repaint() this panel from within this method. The method I suppose could call revalidate() on this JPanel's ancestor causing this component to be re-layed out, but I'm not too crazy about methods in this class having side effects on its ancestor and think that likely it will be better for the code calling the setImage(...) method to suggest that the container revalidate itself.
A lot will come down to your individual needs. When dealing with "image panes", I typically will call setPreferredSize and invalidate, repaint when the image changes.
Changing the layout should automatically trigger a invalidate, reprint request anyway.
But I agree with Hovercraft, you'll want to do this from your own customs panel.
Another approach would be to use something like CardLayout to handle changing between different content layout outs, rather the cleaning up a single panel and re-adding comports to it
I'd also have a look at the Scrollable
Related
I read some posts here and I started why some people do
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
instead of
setPreferredSize(new Dimension(500, 500));
Isn't the second one better because it creates only one Dimension object whereas the first one possibly creates several (even if it's not that much wasted memory)? Or am I wrong? Is there a difference at all?
A big difference is how the value can change over time, and so the one you choose should be dependent on what you're wanting to do with the code.
If you simply call setPreferredSize(new Dimension(500, 500)); in your code, it will do as you expect - it sets the preferred size to 500x500. However, other code in your application can potentially overwrite this value with a new one - anything can call setPreferredSize() and the last call to this method will be the end result.
However, if you override the getPreferredSize() method in your code, it will always return 500x500. It doesn't matter if any of your code calls the setPreferredSize() method, because they are effectively ignored. If you also override getMinimumSize() and getMaximumSize(), you can force a fixed size on a component that shouldn't change regardless of the size of the window and the other components.
However, as mentioned by #Andrew Thompson in the comments, this isn't guaranteed as some layout managers can choose to ignore these, especially if you're writing your own layout manager, and adding a custom component to some parent containers will also ignore these methods, depending on where/how the component is used. Regardless, it's still more rigid than calling setPreferredSize() which can easily be called by other code and be totally overwritten.
I also override the getPreferredSize() method (plus getMinimumSize() and getMaximumSize()) for any of my custom components, such as a color picker that needs to have specific dimensions for the component to be painted properly. Without overriding these methods, the Swing layout managers don't understand how your custom component can be positioned and sized appropriately for the size of the JFrame or JPanel.
I've created JPanel and have already added components into it and I'm going to pass that JPanel to PopUpFactory... So can I get size of JPanel before passing it?
I put Jlabel into it and text after that and I don't know the size of that text...
You can set the preferred size using setPreferredSize(Dimension); e.g.
JPanel pnl = new JPanel(new BorderLayout());
pnl.setPreferredSize(new Dimension(640, 480));
This value will subsequently be obtainable by calling getPreferredSize() and will be used when laying out the component, although note that it is not guaranteed that it will actually be rendered at this size.
Why do you actually require the size prior to rendering it? Typically with Swing programming you don't need to deal with explicit dimensions / sizes as the chosen layout will take care of these specifics for you.
EDIT
To address the OP's query regarding JTextField, one option here it to call the int based constructor that accepts the anticipate number of columns. This causes the text field to be rendered wide enough to support that number of characters.
My second point addresses the comment that the setXXXSize methods should never be called directly and that the developer should rely solely on the LayoutManager. This is not always appropriate - Typically it is necessary to set the preferred size of your main application frame. For example, suppose I were writing a a simple browser application in Swing. The majority of the main frame is a JEditorPane for rendering HTML. If I do not set a preferred size for this component (or the containing frame) and I call pack() the frame is likely to be rendered as small as possible, rather than with sensible dimensions.
JComponents doesn't returns getSize, getLocation, getBounds or getXxxSize if a JComponents hasn't been previously visible on the screen or after call pack()
but why care about that, because usage of (proper and correct) LayoutManager can do that automatically, that reason why LayoutManager exist there, really why care about that
Just call getPreferredSize method for JLabel.No matter if container of it is not realized, preferred size changes if you are setting text of jlabel even before you set it visible.
I've implemented a custom JPanel, whose paint method I've extended to do a lot of manual rendering in full screen mode. Now I would like to integrate another JComponent to this (in my case a JPanel that contains a JScrollpane with a JTextPane as its viewport) that should appear on top of my first panel, but because my custom rendering pipeline is complex, adding the JComponent to my panel and having it painted the traditional way through the AWT system is not an option (I tried and it's quirky at best, not functional at worst), so my question is: is it possible to manually order the JComponent to be painted at one point in my program by calling its regular paint method without tying it to a JContainer and if yes, how do I do this?
Thanks in advance for your answers.
See the LabelRenderTest.java source on this thread. The label is eventually drawn to screen, but it is painted to BufferedImage before ever being displayed.
The important line of the source is..
textLabel.setSize(textLabel.getPreferredSize());
You can take a look at CellRendererPane and see how for example BasicTableUI paints component images with it.
Yes, just call the normal paint method on the object and pass the Graphics you want it to paint on. However, this is just going to paint it and it sounds like you want it to possibly scroll which means you will need to add it to your custom JPanel. In that case just add the panel and you a layout manager that will place the component where you need it.
You should set size for the component. Then to position it use your Graphics' translate(x,y) to position the component in desired Point.
if there is any container higher level in the hierarchy you can use
validate(); repaint();
pair to do that.
if not you can change it's size or bounds ( like +1 , -1 ) at the end to make it repaint itself.
In our product, we have frames that are basically three inheritance levels down from what is essentially a JDialog. This frame overrides the default pack() method as shown:
#Override
public void pack() {
this.setSize(getMaximumSize());
validate();
super.pack();
}
#Override
public Dimension getMaximumSize(){
return super.getPreferredSize();
}
pack() here gets called after pretty much everything is on the screen. Lots of these windows have a title bar, a couple of tool bars, a pane that holds the main content (can often be mostly empty), and a status bar at the bottom.
My problem appears to be that when it calls the getPreferredSize() for the container, the result is just too small, i.e. the width seems okay, but the height isn't. Reading through the docs, it seems like the preferred size is computed based on the layout manager if it isn't set explicitly (which I'm pretty sure it isn't). I'm not quite sure how it calculates or if I should be doing something else first.
Anybody have any idea or thoughts as to what my problem could be here? It's not always too small, just some of the time. Please let me know what other information/code may be helpful to figuring this out. Thanks.
I believe most of the Layouts will use the basic premise of getting the preferred size of the immediate children of the container and then returning the minimum Dimension that would contain those components laid out using their preferredSizes (a quick check of the source shows this is the case for FlowLayout, BoxLayout, and GridLayout). So two suggestions I can offer are:
Read the source for getPreferredLayoutSize(Container) for the Layouts you use (or the ones you use most frequently) and confirm they are using preferred sizes for their calculations.
Set a preferredSize explicitly for the lowest level components (items that aren't a composite of other components), then the composites of those components should have an appropriate preferredSize in relation.
It looks like there is stuff being added to the dialog after the pack() and the getPreferredSize() happen, however those additions aren't resizing things properly.
As is often the case, the answer turned out to be rather pedestrian and obvious.
I am using a JPanel (with several labels inside) to add a dynamic information on a graph. This panel is dynamically created, it is not visible before I use it to draw.
For this, I am using a BufferedImage, and I follow approximately the same steps as described on this other question. It works good, as long as I specify all sizes (the panel, and its components).
Like asked as well in comments of the referred question, how can I determine the optimal size of this panel? The same operation would be done if this panel was displayed in a regular frame/layout setting.
In my case, how can I "pack", in a way, this panel, so that its size, and size of its content are set to the optimal (determined by the size of labels, then)?
Suraj and willcodejavaforfood put me on the good track.
Checking what is actually done in a pack() method, I see that this is mostly setting the current size to the one returned by getPreferredSize().
From this, I managed to make such solution:
// Creating the panel
JPanel lPanel = new JPanel();
//lPanel.setSize(1000, 1000); //default size, not needed anymore
lPanel.setLayout(new BoxLayout(lPanel, BoxLayout.PAGE_AXIS));
//Adding the content
lPanel.add(new JLabel("Blah"));
// etc...
//Adjust the panel to its preferred size
lPanel.setSize(lPanel.getPreferredSize());
//Call the layout method
//(this will adjust the content components to their correct size and position)
lPanel.doLayout();
This method works correctly, and adjusts the panel and its content to the correct size (and answers my question in a simplistic way: "how to find the preferred size? getPreferredSize()").
However, it requires to set the initial size to a large enough size, so that the content fits in, or they won't be put on the layout. This is a bit pity, and not really "clean", but I can't find a way to avoid that, for now.
Edit: Actually, the default size was not necessary, because getPreferredSize() returns the correct value, even before calling doLayout(). As such, the panel can be set to its proper size before calling the layout method.
The direct answer is to call Window#pack(). This method will automatically set the size of all underlying children to thier preferred sizes(ofcourse this depends on layouts of child containers, for e.g. BorderLayout doesent give a damn about preffered sizes).
So as long as you have set preferred sizes(or min/max sizes in case layouts are like BorderLayout) of your child components, pack() method will be all you need.
[UPDATE]One way is to do is add a HierarchyListener to your jpanel and check for HierarchyEvent#DISPLAYABILITY_CHANGED events. This event is called when your panel is realized that is ready to be shown(and a parent is available), at this moment you can do:
SwingUtilities#getWindowAncestor(myPanel).pack();