Component setSize method in FlowLayout object - java

I'm currently making a GUI that makes use of the FlowLayout class. Now, this class is meant to allow components be set by their prefer sized methods and I believe, isn't supposed to have priority in setting the component size. However, when I used a setSize method for a JTextField, the FlowLayout object didn't seem to recognize the change size command. But when I used the setColumn method, the FlowLayout object did respond to the change in size command.
Why is this?

FlowLayout object didn't seem to recognize the change size command.
But when I used the setColumn method, the FlowLayout object did
respond to the change in size command. Why is this?
Form your own question i understand that you know FlowLayout works obeying component's preferred size. However to answer your question why really JTextFeild.setColumn(int) responds: Because,
As soon as setColumn(int) is called, it invalidate() the JTextFeild component and and all parents above it to be marked as needing to be laid out.
public void setColumns(int columns) {
int oldVal = this.columns;
if (columns < 0) {
throw new IllegalArgumentException("columns less than zero.");
}
if (columns != oldVal) {
this.columns = columns;
invalidate(); // invalidate if column changes
}
}
Then while laying out, FlowLayout calls the getPreferredSize() function of JTextFeild, which is overridden and implemented such that it returns the preferred width by adding the column width:
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (columns != 0) {
Insets insets = getInsets();
size.width = columns * getColumnWidth() +
insets.left + insets.right; // changing the width
}
return size;
}
Guess what! I am becoming fan of source code.

Related

JDialog.pack() hides JLabel

I am trying to write a form in java, but after dynamically inserting JLabels to the current JDialog and doing a pack() the windows is resized to minimum. The JLabels are displayed, but I have to resize the window manually.
Here is the part where the JLabels are inserted:
public void displayQuizz(Test quiz){
int xLable = 44;
int yLable = 41;
int widthLable = 403;
int heightLable = 70;
int noOfQuestion = 1;
for(Question question : quiz.getQuestions()){
JLabel lblNewLabel = new JLabel(Integer.toString(noOfQuestion) + ". " + question.getStatement());
lblNewLabel.setBounds(xLable, yLable, widthLable, heightLable);
contentPanel.add(lblNewLabel);
contentPanel.revalidate();
contentPanel.repaint();
this.pack();
noOfQuestion++;
yLable += heightLable;
}
}
The pack() method sets the size of a Window (where JFrame and JDialog are subclasses from) to the preferred size.
The preferred size is determined by
The LayoutManager, which takes the arrangement of the components and
their preferred size into account
The component itself, if it does not have a layout manager
As you don't use a layout manager in your example (and set the bounds of the label manually), you also have to specify the preferred size yourself (see getPreferredSize(), the default is 0x0, that's the problem you encountered).
I'd encourage you to get used to always use layout managers (there's quite a lot of them, and you can easily write your own layout manager strategy if none suffices your needs).

MigLayout: how to resize the height proportionally to the width (keeping the aspect ratio)?

I'm arranging images in a grid with MigLayout. I could get the width to take the whole space available in the column, but then I want the height to grow accordingly so the proportion of the image still fixed.
What is the best approach for this? Could I do that with an expression in the constraint or I have to override the getPreferredSize()?
Thanks!
After a lot of researching, I realize that there is no nice way of doing this with Swing. The problem is not only coming from MigLayout but also from layouts like ScrollPaneLayout, which assumes that the preferred height will remain the same after a different effective width (which is not the preferred width).
There are two options:
1) Currently, I'm doing my own implementation of MigLayout with an aspect ratio component constraint. You can download it from here:
https://github.com/lqbweb/miglayout-aspect
So far, it works with shrinking and growing X in a simple grid case with 1 component / cell. I still have to test with spanning, flowing in the cell and docking..... I'll keep that repository updated and any help is well welcome.
As you will probably use it as a view on a ViewPort, you will have to hack a bit the getPreferredSize of the view if you use it with the Scrollable.getScrollableTracksViewportWidth() returning true, so it doesn't return the real preferred height but the one that matches the width. In my code there is a getter for the grid, and the grid has a function to return a preferred height for a given width.
2) Keeping the current implementation of MigLayout untouched (4.2 at the time of this answer), I only found one way to achieve this: by adding a callback to the layout and implementing getSize() method with something like this:
migLayout.addLayoutCallback(new LayoutCallback() {
/**
* This is run before the layout starts laying out
*/
#Override
public BoundSize[] getSize(ComponentWrapper comp) {
if(comp.getComponent() instanceof DCMImageWrapper) {
DCMImageWrapper img=(DCMImageWrapper) comp.getComponent(); //this is the BufferedImage embedded in a JLabel
int calculatedHeight=img.getHeightFor(comp.getWidth());
UnitValue maxHeight=new UnitValue(calculatedHeight);
BoundSize height=new BoundSize(maxHeight, maxHeight, maxHeight, null);
return new BoundSize[]{null, height};
} else {
return null;
}
}
private double getCurrentAspect(ComponentWrapper comp) {
if(comp.getWidth()==0 || comp.getHeight()==0) return 0;
double currentAspect=comp.getWidth()/(double)comp.getHeight();
return currentAspect;
}
/**
* Check if the aspect still valid
*/
#Override
public void correctBounds(ComponentWrapper comp) {
if(comp.getComponent() instanceof DCMImageWrapper) {
DCMImageWrapper img=(DCMImageWrapper) comp.getComponent();
double currentAspect=getCurrentAspect(comp);
double origAspect=img.getDCMImage().getAspect();
double currentError=Math.abs(origAspect-currentAspect);
if(currentError > img.getDCMImage().getAspectError()) {
//recalculate layout
revalidate();
}
}
}
});
and then, adding the component like:
CC constraints=new CC();
constraints.shrinkX(100);
constraints.minWidth("1");
constraints.minHeight("1");
add(tmpImg, constraints);
But, you will have to add and keep updated a layout constraint (LC) to set manually the preferred size of the layout, as after the callback it gets biased.

How to set dialog's width relative to the width of its title?

I have a JDialog with just a few components inside it. I want to make the dialog as small as possible. Currently I am using pack(). This has the unintended effect of reducing the dialog's width so much that the title is no longer completely in view. I want the dialog's width to always be great enough such that the title is always completely in view.
I am using swing. I realize that the title bar appearance/font is determined by the OS. I would prefer to stick with swing so at the moment i am planning on calculating the title string width based on the font of a JLabel. Then I will set the minimum width of one of my components equal to that.
Is there any better way to pack a JDialog while keeping its title visible?
public static void adjustWidthForTitle(JDialog dialog)
{
// make sure that the dialog is not smaller than its title
// this is not an ideal method, but I can't figure out a better one
Font defaultFont = UIManager.getDefaults().getFont("Label.font");
int titleStringWidth = SwingUtilities.computeStringWidth(new JLabel().getFontMetrics(defaultFont),
dialog.getTitle());
// account for titlebar button widths. (estimated)
titleStringWidth += 110;
// set minimum width
Dimension currentPreferred = dialog.getPreferredSize();
// +10 accounts for the three dots that are appended when the title is too long
if(currentPreferred.getWidth() + 10 <= titleStringWidth)
{
dialog.setPreferredSize(new Dimension(titleStringWidth, (int) currentPreferred.getHeight()));
}
}
EDIT:
after reading trashgod's post in the link, I adjusted my solution to override the getPreferredSize method. I think this way is better than my previous static method. Using the static method, I had to adjust it in a pack() sandwich. pack(),adjust(),pack(). This wasy doesn't require special consideration with pack().
JDialog dialog = new JDialog()
{
#Override
public Dimension getPreferredSize()
{
Dimension retVal = super.getPreferredSize();
String title = this.getTitle();
if(title != null)
{
Font defaultFont = UIManager.getDefaults().getFont("Label.font");
int titleStringWidth = SwingUtilities.computeStringWidth(new JLabel().getFontMetrics(defaultFont),
title);
// account for titlebar button widths. (estimated)
titleStringWidth += 110;
// +10 accounts for the three dots that are appended when
// the title is too long
if(retVal.getWidth() + 10 <= titleStringWidth)
{
retVal = new Dimension(titleStringWidth, (int) retVal.getHeight());
}
}
return retVal;
}
};
1) Use FontMetrics to find out the width of your title
2) Add to this value a number representing the window icon and the X (close) button (you should guess that).
3) Set the dialog's width with the above value.
You can't find the exact width size you need but this is a way to make a good guess.

Layouts in java Swing specifying size

My question is how can I specify size of the parts on my layout?
I need somehow set size of "parts" not using preferedSize, maybe in layout managers, doesn't matter where - only I need is stable size.
I want to create layout for game. I've already created one but I'm dealing with problem with size of components. So I considered that it would be better to make better concept of my layout.
Let's look at my draft.
+-----------+
| UPPER |
+-----+-----+
| A | |
+-----+ C |
| B | |
+-----+-----+
| Footer |
+-----------+
A+B+C make together Center.
Main part consist of this tree parts:
Upper- there will be menu.
Center - this consists of 3 parts A,B,C
Footer - there will be status bar
My idea is to be able to set the size of each component.
All layout is dependent on part C it could have size 450x450 px or 600x600 px.
For part A and B i need specify only the width, because there will be only some text info - it should be about 300 px.
I tryed to use GridBagLayout for Center part but setSize for C didn't worked well.
I make the parts in Containers (java.awt.Container) - in them I add the content of each part and then add the Container to the upper level.
The simplest way: use BorderLayout for the contentPane (which already is)
- Upper panel goes to North
- Footer panel goes to South
- Panels A and B goes into a Panel ab with GridLayout(2,1)
- Panel ab and C goes into a Panel abc with GridLayout(1,2)
- Panel abc goes into the Center
And setPrefferedSize() of your A, B, C
In general, GridBagLayout ignores the values you set for controls with setSize, instead it asks the controls for their preferred size (by calling getPreferredSize) and uses that for calculating the overall layout. Simply setting that preferred size yourself is not recommended, since most controls tend to recalculate those values whenever a layout is triggered, so you will have a hard time getting them to "stick".
If you really want to make sure the UI element C has a certain size, implement it as a custom class deriving from a suitable base (JPanel, for example) and override the getPreferredSize method to make it return the size you want/need for that part of your UI.
Edit: Here's a little example for a wrapper that can contain another UI element and can be set to a fixed size (using the setSize method which has been overridden), which should be respected by layout managers:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.JPanel;
public class FixedSizeComponent extends JPanel {
private Dimension size;
private final JComponent content;
public FixedSizeComponent(JComponent content) {
super(new BorderLayout());
this.content = content;
super.add(content, BorderLayout.CENTER);
}
#Override
public void setSize(Dimension d) {
size = d;
}
#Override
public void setSize(int width, int height) {
size = new Dimension(width, height);
}
#Override
public Dimension getSize() {
if (size != null) return size;
return content.getSize();
}
#Override
public Dimension getSize(Dimension rv) {
if (size != null) {
if (rv == null) rv = new Dimension();
rv.height = size.height;
rv.width = size.width;
return rv;
}
return content.getSize(rv);
}
#Override
public Dimension getPreferredSize() {
if (size != null) return size;
return content.getPreferredSize();
}
#Override
public Dimension getMaximumSize() {
if (size != null) return size;
return content.getMaximumSize();
}
#Override
public Dimension getMinimumSize() {
if (size != null) return size;
return content.getMinimumSize();
}
}
I had a similar problem, with a status tool bar at the bottom containing a number of other components. My problem was that it would get taller. So what I did was to override the maximum size setting the maximum height to be the minimum height.
JPanel status = new JPanel( new SpringLayout() ) {
#Override
public Dimension getMaximumSize() {
Dimension max = super.getMaximumSize();
Dimension min = getMinimumSize();
return new Dimension( max.width, min.height );
}
};
This answer is too little and WAY too late,
(maybe this method did not exist at the time of asking of this question)
just like getPreferredSize, there is also a setPreferredSize method which takes a Dimension object.
By default, your layout will ignore your components sizes (which you may have set using setSize), instead it will use the preferred sizes.
By using setPreferredSize, you will be able to override the default preferred sizes of the component
I hope my answer can help you in some way. From experience with setting JPanel or JFrame size, I have always used setPreferredSize(new Dimension(WIDTH,HEIGHT));

Making sure SpringLayout doesn't shrink below a certain size

I'm trying to implement a quite simple UI using SpringLayout (partly because I, as opposed to most tutorial writers I find on the net, quite like the coding interface compared to other layout managers and partly because I want to learn how to use it). The UI basically looks like this:
This is all well. The UI resizes the way I want (keeping the welcome text centered and expanding the text area to fill all the new available space) if I increase the window size. However, below a certain point (more specifically when the window becomes too narrow for the welcome text):
I would like the window to not allow further shrinking, so that if the user tries to shrink the window to a size smaller than enough to house the components, it simply stops. How do I accomplish this, using the SpringLayout layout manager?
I know I could probably do this by handling some resize-event and checking if the minimum size is reach, and then just set the size to the minimum size. But this requires me to a) know, or know how to calculate, the minimum size of the window, even before it renders, b) write a bunch of event-handling code just to get some UI rendering right, and c) write a bunch of code for things that I expect a good layout manager to take care of ;)
you can override MinimumSize for TopLevelContainer
you have put JTextArea to the JScrollPane
easiest way is mixing LayoutManagers (called as NestedLayout) by spliting GUI to the parts (separated JPanels with same or different LayoutManager), rather than implements some most sofisticated LayoutManager (GridBagLayout or SpringLayout) for whole Container
some LayoutManagers pretty ignore setXxxSize
SpringLayout isn't my cup of Java
import java.awt.*;
import javax.swing.*;
public class MinSizeForContainer {
private JFrame frame = new JFrame("some frame title");
public MinSizeForContainer() {
JTextArea textArea = new JTextArea(15, 30);
JScrollPane scrollPane = new JScrollPane(textArea);
CustomJPanel fatherPanel = new CustomJPanel();
fatherPanel.setLayout(new SpringLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(fatherPanel, BorderLayout.CENTER);
frame.setLocation(20, 20);
frame.setMinimumSize(fatherPanel.getMinimumSize());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MinSizeForContainer Mpgp = new MinSizeForContainer();
}
});
}
}
class CustomJPanel extends JPanel {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(400, 400);
}
}
There are several issues to achieve a "real" (that is not shrinkable beyond) min size:
the child components must return some reasonable (based on their content) min size, many core components don't
the layoutManager must respect the compounded min of all children, no matter how little space is available
the top-level container (here the JFrame) must not allow shrinking beyond the min
The first is true for a JLabel, the second is met for SpringLayout (that's why the label is truncated) - which leaves the third as the underlying problem, the solution to which isn't obvious, actually I wasn't aware it's even possible before running #mKorbel's example. The relevant line indeed is
frame.setMinimumSize(someSize);
With that line in place, it's not possible to shrink the frame below. Without, it is. Starting from that observation, some digging turns out the doc for its override in Window
Sets the minimum size of this window to a constant value. [..] If
current window's size is less than minimumSize the size of the window
is automatically enlarged to honor the minimum size. If the setSize or
setBounds methods are called afterwards with a width or height less
[...] is automatically enlarged to honor the minimumSize value.
Resizing operation may be restricted if the user tries to resize
window below the minimumSize value. This behaviour is platform-dependent.
Looking at the code, there are two (implementation, don't rely on them :-) details related to the min size
Dimension minSize;
boolean minSizeSet;
and public api to access
public Dimension getMinimumSize()
public boolean isMininumSizeSet()
the first rather oldish (jdk1.1), the latter rather newish (jdk1.5) - implying that the first can't rely on the latter but internally has to check for a null minSize. The overridden sizing methods (with their guarantee to doing their best to respect a manually set minSize) on Window are the latest (jdk6) and do rely on the latter. Or in other words: overriding isMinimumSizeSet does the trick.
Some code snippet (beware: it's a hack, untested, might well be OS dependent with undesirable side-effects!):
// JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("some frame title") {
/**
* Overridden to tricks sizing to respect the min.
*/
#Override
public boolean isMinimumSizeSet() {
return true; //super.isMinimumSizeSet();
}
/**
* Overridden to adjust for insets if tricksing and not using
* LAF decorations.
*/
#Override
public Dimension getMinimumSize() {
Dimension dim = super.getMinimumSize();
// adjust for insets if we are faking the isMinSet
if (!super.isMinimumSizeSet() && !isDefaultLookAndFeelDecorated()) {
Insets insets = getInsets();
dim.width += insets.left + insets.right;
dim.height += insets.bottom + insets.top;
}
return dim;
}
};
// add a component which reports a content-related min
JLabel label = new JLabel("Welcome to my application!");
// make it a big min
label.setFont(label.getFont().deriveFont(40f));
frame.add(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);

Categories