At the moment I've got a 9x9 grid of text fields using a GridBayLayout. Because the grid makes each text field slighter longer than it is tall (even when I set a specific height for the text field), the grid is a rectangle, rather than a square.
How can I either make the entire grid a specific size, or each text field inside the grid a specific size?
EDIT: I need the 9 x 9 grid of text fields to be square, rather than rectangular.
The problem is with sizing hints, methods [get/set][Minimum/Maximum/Preferred]Size are telling the layout manager what size the component wants to be and layout managers sometimes ignore that and sometimes they don't. GridBagLayout respects that and your JTextFields are tellimg him that they want to be rectangular. How JTextFields figure out what size they want to be is all kinds of difficult so let's leave that out.
If you want to make sure that all your JTextFields are of the same size, you should override these methods. Subclass JTextField and make something like this:
final class SquareJTextField extends JTextField {
private static Dimension maxDimension = new Dimension(0, 0);
#Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
// take the larger value
int max = d.width > d.height ? d.width : d.height;
// compare it against our static dimension
// height je rovnaky ako width
if (max > maxDimension.width)
maxDimension = new Dimension(max, max);
// return copy so no one can change the private one
return new Dimension(maxDimension);
}
#Override
public Dimension getMinimumSize() {
Dimension d = super.getPreferredSize();
int max = d.width > d.height ? d.width : d.height;
if (max > maxDimension.width)
maxDimension = new Dimension(max, max);
return new Dimension(maxDimension);
}
#Override
public Dimension getMaximumSize() {
Dimension d = super.getPreferredSize();
int max = d.width > d.height ? d.width : d.height;
if (max > maxDimension.width)
maxDimension = new Dimension(max, max);
return new Dimension(maxDimension);
}
}
All objects of this class should be the same dimension and should be square.
Replace JtextFields in your grid with SquareJTextField
Related
Using this class and this main method, I am trying to make it so that the window that is created when running the main class is the right size to hold all of the icons that are passed into it without having to resize the window, and without hardcoding a value into what I want the window size to be when I initialize it.
Right now when it runs, the window starts extremely tiny, and as I resize it the layout of all of the icons that are painted onto it are messed up.
I know how to determine the proper size it should be, but I am not sure how I I know using the coordinates ArrayList is how I would determine the size, but I am not sure how I would change the size of the window after it has already been initialized.
public class CompositeIcon implements Icon
{
ArrayList<Icon> iList;
static int width;
static int height;
ArrayList<Point> coordinates;
public CompositeIcon()
{
iList = new ArrayList<Icon>();
coordinates = new ArrayList<Point>();
}
public int getIconHeight()
{
return height;
}
public void addIcon(Icon icon, int x, int y)
{
iList.add(icon);
coordinates.add(new Point(x, y));
}
public int getIconWidth()
{
return width;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
int i = 0;
for (Icon s : iList)
{
Point offset = coordinates.get(i++);
s.paintIcon(c, g, x + offset.x, y + offset.y);
}
}
This is the main to test it
public static void main (String args[]) {
JFrame frame = new JFrame();
Container panel = frame.getContentPane();
panel.setLayout(new BorderLayout());
CompositeIcon icon = new CompositeIcon();
try {
icon.addIcon(new ImageIcon(new URL("http://th02.deviantart.net/fs71/150/f/2013/103/2/7/java_dock_icon_by_excurse-d61mi0t.png")), 10, 10);
icon.addIcon(new ImageIcon(new URL("http://www.bravegnu.org/blog/icons/java.png")), 5, 370);
icon.addIcon(new ImageIcon(new URL("http://fc03.deviantart.net/fs20/f/2007/274/9/8/3D_Java_icon_by_BrightKnight.png")), 200, 200);
}
catch (MalformedURLException e) {
System.err.println("Apparently, somebody cannot type a URL");
}
panel.add(new JLabel(icon));
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Basically, the width and height of the CompositeIcon should represent the combined width and height's of the icons you add (allowing for the x/y offsets)
Something like...
public void addIcon(Icon icon, int x, int y) {
iList.add(icon);
width = Math.max(width, x + icon.getIconWidth());
height = Math.max(height, y + icon.getIconHeight());
coordinates.add(new Point(x, y));
}
You will need to remove the static declearations for width and height as each instance of CompositeIcon should have it's own width and height
Your approach allows for random positioning of Icons, but it means you are responsible for knowing the size of each Icon and positioning them so the don't overlap one another.
For a more structured approach, you can check out Compound Icon. This class supports vertical, horizontal and stacked Icon alignment and does all the location/size calculations for you.
Normally, FlowLayout uses more than one line if needed. Apparently this doesn't happen if the component with the FlowLayout is itself part of a GridBagLayout.
Consider this code:
import java.awt.*;
import javax.swing.*;
public class Xyzzy extends JFrame{
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Xyzzy frame = new Xyzzy();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
JPanel top = new JPanel();
top.setLayout(new FlowLayout());
for (int i=1; i<=30; ++i)
top.add(new JLabel(String.format("Label #%d",i)));
GridBagConstraints c = new GridBagConstraints();
c.weightx = 1.0;
c.gridwidth = GridBagConstraints.REMAINDER;
c.anchor = GridBagConstraints.CENTER;
frame.add(top,c);
frame.add(new JLabel("Bottom"),c);
//top.setPreferredSize(new Dimension(500,300));
frame.setSize(600, 600);
frame.setVisible(true);
}
});
}
}
This code is intended to display the JLabels "Label #1", "Label #2" etc. on multiple lines, but in fact it only uses one line.
I can force it to use multiple lines by removing the '//' before the call to setPreferredSize in the above code, but this requires me to set both a width and a height, and I don't know what height to use. (I cannot use FontMetrics to calculate the height, because in my actual case the JLabels are in reality small JPanels of varying size.)
So is there a way to force FlowLayout to use multiple lines? (Or, alternatively, is there a way to calculate the required height of a component when its width is known?)
Try to add constraint parameters:
c.weighty = 1.0;
c.fill = GridBagConstraints.VERTICAL;
It should allow growing of panel with FlowLayout in vertical direction.
To the best of my knowledge, FlowLayout does require a preferred size in order to actually wrap anything. I've created a subclass which works out the preferred size as the parent container's width, and then whatever height is required:
public class WrappingFlowLayout extends FlowLayout {
#Override
public Dimension preferredLayoutSize(Container target) {
synchronized (target.getTreeLock()) {
Dimension result;
int w = target.getWidth();
if (w == 0) {
// The container hasn't been assigned any size yet; just behave like a regular flow layout.
result = super.preferredLayoutSize(target);
} else {
Insets insets = target.getInsets();
int wrapW = w - insets.left - insets.right;
int maxW = 0; // Width of the widest row.
int rowH = 0; // Current row height.
int x = 0;
int y = 0;
boolean firstVisibleComponent = true;
for (Component c : target.getComponents()) {
if (c.isVisible()) {
Dimension d = c.getPreferredSize();
if (firstVisibleComponent) {
x = d.width + (getHgap() * 2);
y = getVgap();
rowH = d.height;
firstVisibleComponent = false;
} else if (x + d.width + getHgap() <= wrapW) {
// Add to current row.
x += d.width + getHgap();
rowH = Math.max(rowH, d.height);
} else {
// New row.
x = d.width + (getHgap() * 2);
y += rowH + getVgap();
rowH = d.height;
}
maxW = Math.max(maxW, x);
}
}
y += rowH + getVgap();
result = new Dimension(maxW + insets.left + insets.right, y + insets.top + insets.bottom);
}
return result;
}
}
}
Note that you'll need to listen for resize events on the parent component (top in your code) and trigger an update of the preferred size. Your best bet for doing this is probably a ComponentListener.
I have a custom layout where the principal behavior is to grow and shrink a child JTextArea when the JScrollPane it's in changes in width. The scroll pane has the horizontal scroll bar disabled and the text area is supposed to expand and contract so as to avoid needing a horizontal scroll bar. For a number of months, I worked around this using one of the standard layout managers, but now I need some different functionality.
What's happening is that when the user expands horizontally the scroll pane, the layout manager layoutContainer method is called. It resizes the text area and the text reflows properly. However, when you shrink the scroll pane, layoutContainer is not called and the text area stays fixed. I've put some printlns in the layoutContainer method to make it obvious when it's working and not.
The essential thing to note is that the problem happens when JTextArea.setColumns() is called. I can comment it out and the layoutContainer gets called during resizing (of course, then the text area doesn't get resized.) I've tried also using JTextArea.setSize(), with the same results.
Here's the code:
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#SuppressWarnings("serial")
class XTextArea extends JTextArea
{
XTextArea (String text)
{
super (text);
}
public int getColumnWidth()
{
return super.getColumnWidth();
}
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
class PackLeftLayout implements LayoutManager
{
Component viewPort;
Component flexWidthComponent;
int preferredWidth = 0;
int preferredHeight = 0;
//----------------------------------------------------------------------------
// viewPort - if null, compute width as sum of component's preferred width;
// otherwise width will be the viewPort's width.
// flexWidthComponent - if not null, this component width will be sized to right
// justify rightmost component.
public PackLeftLayout (Component viewPort, Component flexWidthComponent)
{
super ();
this.viewPort = viewPort;
this.flexWidthComponent = flexWidthComponent;
}
//----------------------------------------------------------------------------
public void addLayoutComponent(String name, Component comp)
{
}
//----------------------------------------------------------------------------
public void removeLayoutComponent(Component comp)
{
}
//----------------------------------------------------------------------------
// Calculates the preferred size dimensions for the specified container, given the
// components it contains.
// parent - the container to be laid out
// Components layed out left-to-right with no additional spacing.
public Dimension preferredLayoutSize (Container parent)
{
Insets insets = parent.getInsets();
int width = 0;
int height = 0; // will become max of all component's preferred height
// calculate sum of fixed width components - skip the flexwidth component
width = insets.left + insets.right;
for (int i = 0, limit = parent.getComponentCount(); i < limit; i++)
{
Component c = parent.getComponent(i);
if (c.isVisible())
{
if (c != flexWidthComponent)
{
Dimension size = c.getPreferredSize();
if (size.height > height)
height = size.height;
width += size.width;
}
}
}
// determine width of flex width component
if (flexWidthComponent != null)
{
int flexWidth = viewPort.getWidth() - width;
if (flexWidth < 1)
flexWidth = 1;
if (flexWidthComponent instanceof XTextArea)
{
// some trickery here to get the xtextarea to tell us its preferred height
// given a specific width.
int colWidth = ((XTextArea)flexWidthComponent).getColumnWidth();
// the following line causes the failure:
((XTextArea)flexWidthComponent).setColumns (flexWidth / colWidth);
Dimension taSize = flexWidthComponent.getPreferredSize();
width += taSize.width;
if (taSize.height > height)
height = taSize.height;
}
else
{
Dimension size = flexWidthComponent.getPreferredSize();
width += flexWidth;
if (size.height > height)
height = size.height;
}
}
preferredWidth = width; // already include insets
preferredHeight = height + insets.top + insets.bottom;
return new Dimension (preferredWidth, preferredHeight);
}
//----------------------------------------------------------------------------
// Calculates the minimum size dimensions for the specified container, given the
// components it contains.
// parent - the component to be laid out
public Dimension minimumLayoutSize(Container parent)
{
return new Dimension (10, 10); //???
}
static int k = 0;
//----------------------------------------------------------------------------
public void layoutContainer(Container parent)
{
System.out.println ("layout" + (k++));
Insets insets = parent.getInsets();
int left = insets.left;
if (preferredWidth == 0 || preferredHeight == 0)
preferredLayoutSize (parent);
for (int i = 0, limit = parent.getComponentCount(); i < limit; i++)
{
Component c = parent.getComponent(i);
Dimension size = c.getPreferredSize();
c.setBounds (left, insets.top, size.width, preferredHeight);
left += size.width;
}
// force another layout calc
preferredWidth = 0;
}
}
public class ResizablePane extends JFrame
{
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(
new Runnable() {public void run()
{
new ResizablePane();
}});
}
ResizablePane ()
{
super ("ResizableDemo");
// put a button and text area into a panel, then into a scroll pane
JButton button = new JButton ("button");
XTextArea text = new XTextArea (
"For three years I ran as fast as I could, trying to live and love and learn at " +
"double speed to make up for what Anne-Marie lost. Trying to anesthetize myself " +
"from what Id lost. When I decided to read a book a day and write about it, Id " +
"finally stopped running away.");
text.setLineWrap (true);
text.setWrapStyleWord (true);
JScrollPane scroll = new JScrollPane();
JPanel panel = new JPanel();
panel.setLayout (new PackLeftLayout(scroll.getViewport(), text));
panel.add (button);
panel.add (text);
scroll.setHorizontalScrollBarPolicy (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setViewportView (panel);
getContentPane().add(scroll);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
JTextArea wont shrink inside JPanel
You need to set the minimum size of the text area:
textArea.SetMinimumSize(new Dimension(100,100));
ps. I'm Using GridLayout in my panel with just the one component.
This is old question, but i hope that it will be usefull to someone.
For me worked:
jScrollPane = new JScrollPane(textArea);
jScrollPane.setPreferredSize(jScrollPane.getPreferredSize());
In Java, is it possible to get the Width and Height of the JFrame without the title and other borders?
frame.getWidth() and frame.getHeight()1 seems to return the width including the border.
Thanks.
frame.getContentPane().getSize();
frame.pack();
System.out.println("frame width : "+getWidth());
System.out.println("frame height: "+getHeight());
System.out.println("content pane width : "+getContentPane().getWidth());
System.out.println("content pane height: "+getContentPane().getHeight());
System.out.println("width of left + right borders: "+(getWidth()-getContentPane ().getWidth()));
System.out.println("height of top + bottom borders: "+(getHeight()-getContentPane().getHeight()));
This works fine
frame.getContentPane().getSize();
But not if you haven't added content yet. In my case, I wanted to calculate the inner dimensions of the JFrame before adding content, so I could divide up the content accordingly. Here's what I came up with.
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
pack(); // Need this, otherwise insets() show as 0.
int scrW = (int)screenSize.getWidth();
int scrH = (int)screenSize.getHeight();
int innerW = scrW - getInsets().left - getInsets().right;
int innerH = scrH - getInsets().top - getInsets().bottom;
// Need to setSize(), otherwise pack() will collapse the empty JFrame
setSize(scrW, scrH);
Here is a code snippet which works on JFrame as well as AWT's Frame (which happens to be the super-type of JFrame):
public static Dimension getInnerSize(Frame frame) {
Dimension size = frame.getSize();
Insets insets = frame.getInsets();
if (insets != null) {
size.height -= insets.top + insets.bottom;
size.width -= insets.left + insets.right;
}
return size;
}
Beware: The insets are only valid once the frame has been shown.
Here is another code snippet to work around this problem:
private static Insets defaultInsets;
public static Insets getInsetsWithDefault(Frame frame) {
// insets only correct after pack() and setVisible(true) has been
// called, so we use some fallback strategies
Insets insets = frame.getInsets();
if (insets.top == 0) {
insets = defaultInsets;
if (insets == null) {
insets = new Insets(26, 3, 3, 3);
// usual values for windows as our last resort
// but only as long as we never saw any real insets
}
} else if (defaultInsets == null) {
defaultInsets = (Insets) insets.clone();
}
return insets;
}
This code needs to be called once with a visible Frame. After that it can correctly predict the insets even for invisible frames (due to caching of the defaultInsets), assuming they are always the same.
Of course this only works if all windows get the same window-decorations. But i am not aware of any case where they might differ from window to window.
This might be useful, too:
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowOpened(WindowEvent e) {
MyUtilClass.getInsetsWithDefault(frame); // init the defaultInsets
}
});
It will call the getInsetsWithDefault() method once the window is visible and initialize the correct defaultInsets.
I have a JPanel and I create, dynamically, JCheckBoxes inside.
These have to be added JCheckBoxes always a side by side. In case there is more space to be inserted in the side, a new line of JCheckBoxes is created, as in a simple text editor.
This is happening perfectly. But ...
I set the layout on this JPanel to FlowLayout, exactly what I want.
The obvious problem is that a window has limited space. So a good solution to this is: Insertion of this JPanel in a JScrollPane,l and making that happen only in the vertical scrolling.
But I have problems. Although you can make only a vertical scroll bar to appear, the items are always added "forever" side by side. And the vertical scroll simply does not work, only horizontally.
I've tried many ways to make the scroll only vertically, but nothing worked (if it had worked I would not be here:]).
So, has anyone had any similar problem, and can help me?
I shall be very grateful to those who help me.
No more.
I dealt with the same issue with ScrollPanes and FlowLayouts. I found the best solution is to use a modified version of FlowLayout that takes into account vertical changes. Here is the code for such a layout. You can include it in your project and call it just like a FlowLayout, however it will actually work nice with a scrollpane.
import java.awt.*;
/**
* A modified version of FlowLayout that allows containers using this
* Layout to behave in a reasonable manner when placed inside a
* JScrollPane
* #author Babu Kalakrishnan
* Modifications by greearb and jzd
*/
public class ModifiedFlowLayout extends FlowLayout {
public ModifiedFlowLayout() {
super();
}
public ModifiedFlowLayout(int align) {
super(align);
}
public ModifiedFlowLayout(int align, int hgap, int vgap) {
super(align, hgap, vgap);
}
public Dimension minimumLayoutSize(Container target) {
// Size of largest component, so we can resize it in
// either direction with something like a split-pane.
return computeMinSize(target);
}
public Dimension preferredLayoutSize(Container target) {
return computeSize(target);
}
private Dimension computeSize(Container target) {
synchronized (target.getTreeLock()) {
int hgap = getHgap();
int vgap = getVgap();
int w = target.getWidth();
// Let this behave like a regular FlowLayout (single row)
// if the container hasn't been assigned any size yet
if (w == 0) {
w = Integer.MAX_VALUE;
}
Insets insets = target.getInsets();
if (insets == null){
insets = new Insets(0, 0, 0, 0);
}
int reqdWidth = 0;
int maxwidth = w - (insets.left + insets.right + hgap * 2);
int n = target.getComponentCount();
int x = 0;
int y = insets.top + vgap; // FlowLayout starts by adding vgap, so do that here too.
int rowHeight = 0;
for (int i = 0; i < n; i++) {
Component c = target.getComponent(i);
if (c.isVisible()) {
Dimension d = c.getPreferredSize();
if ((x == 0) || ((x + d.width) <= maxwidth)) {
// fits in current row.
if (x > 0) {
x += hgap;
}
x += d.width;
rowHeight = Math.max(rowHeight, d.height);
}
else {
// Start of new row
x = d.width;
y += vgap + rowHeight;
rowHeight = d.height;
}
reqdWidth = Math.max(reqdWidth, x);
}
}
y += rowHeight;
y += insets.bottom;
return new Dimension(reqdWidth+insets.left+insets.right, y);
}
}
private Dimension computeMinSize(Container target) {
synchronized (target.getTreeLock()) {
int minx = Integer.MAX_VALUE;
int miny = Integer.MIN_VALUE;
boolean found_one = false;
int n = target.getComponentCount();
for (int i = 0; i < n; i++) {
Component c = target.getComponent(i);
if (c.isVisible()) {
found_one = true;
Dimension d = c.getPreferredSize();
minx = Math.min(minx, d.width);
miny = Math.min(miny, d.height);
}
}
if (found_one) {
return new Dimension(minx, miny);
}
return new Dimension(0, 0);
}
}
}