JDialog doesn't respect dimensions of child components - java

I have a setup that usually works, but I'm finding it a bit of a pain at the moment.
I have a JDialog (formerly a JFrame, but I've changed the UI flow recently to remove redundant JFrames) set to BorderLayout that contains three main JPanels, header, content and footer.
The header JPanel has a background image that is loading fine. However, I'm calling all manner of setMinimumSize / setPreferredSize / etc (I even tried setSize and setBounds out of desperation) and the JPanel isn't being sized correctly. The layout for that component is BoxLayout, and the children sit comfortably within it, but that shouldn't affect what I'm trying to do with the header panel itself.
The only thing that works is setting a size on the parent JDialog. But I don't want to do that / from what I've read it seems like bad design. I'd also have to maths out the potential width / height of all the children, add the margins, etc.
Advice I am not looking for: "use a different LayoutManager."
I want to understand if there's a reason for the child components not being respected.
I can provide code, but isolating a small, runnable segment is difficult given the amount of interlocked systems I'm juggling. Here is the relevant snippet to set the size of the JPanel. The image dimensions are 480 * 96.
public JPanelThemed(BufferedImage image) {
super();
this.backgroundImage = image;
setAllSimilarConstraints(new Dimension(image.getWidth(), image.getHeight()));
setOpaque(false);
}
// for use with BoxLayout as it requires explicit bounds to be set
public void setAllSimilarConstraints(Dimension dim) {
setPreferredSize(dim);
setMinimumSize(dim);
setMaximumSize(dim);
}

I would implement it a bit another way - to ensure that preferred/min/max/Size can not be changed from elsewhere:
public JPanelThemed(BufferedImage image) {
this.backgroundImage = image;
setOpaque(false);
}
public Dimension getPreferredSize() {
return new Dimension(this.backgroundImage.getWidth(), this.backgroundImage.getHeight());
}

If I don't define dimensions, how do I ensure the whole background I'm painting is displayed?
You could use a JLabel to display the ImageIcon. The size of the label will be the size of the image. You then set the layout manager of the label so you can add components to it.
Edit:
do you know why it's commonly-recommended to subclass JPanel when painting backgrounds in Java
When you use a JPanel the size of the panel is based on the components added to the panel and the layout manager you are using. Your custom painting code then needs to paint the image the way your want. That is you can paint the image from (0, 0), or you can center the image on the panel, you can scale the image to make it fit the pane, but the image in no way controls the size of the panel, unless you override the getPreferredSize() method of the panel to use custom code to base the size of the larger of the components or the image. So you are in full control.
When you use the JLabel approach suggested here, the size of the label is always the size of the image. Then components will be positioned based on the layout manager but can be truncated if the image is smaller than the space required by the components.
So based on your requirement that you want to make sure the entire image is displayed, I suggested the JLabel approach, since you don't need to write any custom code. I find this a simple approach when using popup non-resizable dialogs to display a background image and a few components and buttons to close the dialog.
You can also check out Background Panel which provides these common painting features in a reusable class.

Related

Java Swings Auto-Resize Pictures

I'm doing this project using Swing.
To add a more welcoming feel to the UI, I'm also adding a few Photoshopped images as the background.Here's where the problem begins...
I want the images to automatically resize themselves once the size of the window is increased or decreased, how can I make this happen ?
One way is to override the paintComponent(...) method of a JPanel to paint the image using the drawImage(....) method.
Another option is use a JLabel with an Icon as the background for the frame. Then you can use the Stretch Icon which will automatically scale based on the space available to the label. This is the most flexible solution since the StretchIcon can be used on any component that supports icons.
You can also check out the Background Panel which allows you to display an image. You can display the image at its actual size, scaled or tiled.

make JLabel fit its parent JPanel or give a minimum size to it

I have a gridlayout that consist from jPanels. Every JPanel containst a JLabel, they are 9x9. For this is a chess game, I need to be able to highlight every jPanel or its jLabel upon some events (such as being possible move option or an endangered field). However, I can only highlight fields that have their icons (figures) set, which means an empty jLabel has probably zero dimensions.
This is how I put a figure in jLabel (it sucks, but it somewhat works):
public void drawFigureAt(Figure fg, int x, int y) {
//retrieve the Jlabel from the field array
JLabel pole = policka_l[y*8+x];
//Set icon from file
pole.setIcon(new ImageIcon(fg.imageName()+(fg.color==1?"_white":"_black")+".png"));
//force redraw or whatever it is
pole.revalidate();
}
Some of the fields are now highlighted in very transparent black, so I can see that the JLabels ar as large as their icon is.
This is how I highlight:
public void highlightField(int x, int y, Color color) {
//Get the JLabel obejct
JLabel pole = policka_l[y*8+x];
//Set the color as the background
pole.setBackground(color);
//What is this?
pole.setOpaque(true);
//Another magic
pole.revalidate();
repaint();
}
But, as I've said, if JLabel has no icon, it can't be highlighted and that's very sad form me. I's much worse that this is a school project that must work this morning, so using different rendering system is not in question.
I've found some clues how to deal with it, however, they don't work. I hoped a lot in this:
JLabelinstance.setMinimumSize(new Dimension(70, 70));
No effect unfortunatelly.
So what I want now is to either make the function above work, or even better - configure JLabels to fill their parent JPanels so that highlighting will look nice, not stupid.
Also my figures do not resize with the window at all, so they tend to overflow.
I have a gridlayout that consist from jPanels.
You could:
1) Just add the labels directly to the GridLayout. Then the label will be the size of the grid
2) Set the layout manager of the panel to be a BorderLayout, then the label will be resized to fill the entire space of the panel.
pole.setBackground(color);
pole.setOpaque(true);
//pole.revalidate();
//repaint();
The revalidate() and repaint() methods are not needed. Swing components will automatically repaint themselves when a property changes.
Also my figures do not resize with the window at all, so they tend to overflow.
Don't use setSize(). You should add the icons to the label and then labels to the grid. Then pack() the frame and the grid will be sized to hold the largest icon.

Make a JPanel scale to fit width but keep a fixed height

I have a JPanel which I'm placing inside of a JScrollPane. I am manually painting it using paintComponent(), since it's being used as a canvas, and I want the panel to automatically fit to the width of the scroll pane. I can then use getWidth() in the painting code to automatically scale to fit the container. However, I want to be able to manually set the preferred height so the I make use of the scroll pane's vertical scrolling capabilities.
What's the best way to do this? This would obviously still work if I was just able to get the width of the scroll pane in the painting code, but I don't want to break encapsulation too much with hacky code like getParent().
Let your panel implement Scrollable: the getScrollableTracksViewportWidth/Height control how the viewport handles the sizing in the horizontal/vertical dimension, respectively. A value of true indicates that the component is forced to the same size as viewport (that is, no scrolling in that direction), so getWidth() can be consistently used for scaling.
I would get your paint code to use the horiz width of itself as the basis for painting.
Then set the scroll pane so it never scrolls horizontally, only vertically.
Is this good enough ? Seems too simple now I've typed it out !

permanent background image on a JPanel

I am building a swing interface for drawing overlays over a particular image. I have found several examples of how to draw an image on a JPanel, and I am able to do this without difficulty. However, I would like to keep this image completely static while dynamically drawing overlays on top of it. With that in mind, it would be better if I could set the image as a permanent background of the panel, rather than having paintComponent() redraw the image every time. This would be particularly useful, as there will frequently be situations where I want to removeAll() graphics currently on the panel and redraw new ones.
So my question is this: is there a way to set a permanent background image in JPanel, or do I have to redraw the image every time paintComponent() is called?
put Image as Icon / ImageIcon to the JLabel
use this JLabel instread of JPanel
JLabel hasn't implemented any LayoutManager in compare with JFrame(BorderLayout) or JPanel(FlowLayout)
you have to define the proper LayoutManager, that accepting PreferredSize came its child(s)
then there are accesible (almost nearly) all methods as for JPanel, as container works
maybe you can try putting the Jpanel inside another Jpanel. You draw the image on the container JPanel. then make your inner JPanel a transparent one. this way, when there are no objects on it, you see the background of the parent JPanel which did not change.

Java Swing: component that resizes itself but doesn't influence the layout

I'll try to explain my problem as simply as possible but it's a tricky topic and people who haven't encountered the issue probably won't know what I'm talking about.
I want to use a BorderLayout using west, east, north, south, etc. components that are my "normal" components (JLabels, JButtons, etc.) then I want the center component to be an "image" (that is: pixels). To this end I'm using a BufferedImage and using setIcon on a JLabel that is inside a panel that is part of the "center".
However I want my image/pixels to be "fluid": whenever the user resizes the app, I want to compute the exact size of the JLabel (icon/text gap is set to 0) and then create a new image (pixels that I manipulate directly in a BufferedImage but whatever) that has exactly that size.
Now it does work fine. But only when I resize the main window ("window" as in "one of the window of the operating system) by making it bigger.
It doesn't work when I downsize my main window.
The reason, after a lot of testing, is obviously because the size of my JLabel (in which I did a setIcon( img ) is influencing the computation of the layout manager.
So here comes the billion dollar question: how should I use a BorderLayout (or any other layout) so that I can create a "fluid" rectangle of pixels in the center of my app?
Answering my own question with an answer that I will not accept even tough it does work...
The problem can be "worked around" by creating a picture a few pixels smaller than the getVisibleRect of the center area.
So in my case I create an ImageIcon from a BufferedImage that is 20 pixels smaller (both in width and height) than the area that will hold it.
What happens then is that because the picture is smaller it doesn't "block"/prevent the layout manager from putting everything at their correct place when downsizing the main window.
So by using such an hack I get the "fluid" behavior I want.
This is however an hack whose level of hackyness cannot be understated and I'm sure there's a very clean way to solve this.
The reason, after a lot of testing, is
obviously because the size of my
JLabel (in which I did a setIcon( img
) is influencing the computation of
the layout manager.
The preferred size of the JLabel is used in the preferred size of the panel, but this size is ignored when you resize the frame, since the CENTER only gets whatever space is left over after the preferred size of the other 4 components is considered.
To this end I'm using a BufferedImage
and using setIcon on a JLabel that is
inside a panel that is part of the
"center".
Sounds to me like it should work.
Create the panel with a BorderLayout. Add the JLabel to the Center of your main panel. Then add a ComponentListener to the panel. Now when the frame is resized the center panel size will be adjusted to take the space available to it. Now that you know the size of the center panel you can recreate the Icon and add it to your JLabel,
This is how you write a SSCCE:
import java.awt.*;
import javax.swing.*;
public class LabelTest2 extends JFrame
{
public LabelTest2()
{
JLabel picture = new JLabel(new ImageIcon("???.jpg"));
add(picture);
}
public static void main(String[] args)
{
LabelTest2 frame = new LabelTest2();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}

Categories