How to set maximum size of JFrame? - java

There is the following code:
double gridWidth=columnNumber*(IMAGE_WIDTH_NORMAL+10)+(columnNumber+2)*10;
double screenWidth=Toolkit.getDefaultToolkit().getScreenSize().getWidth()*0.8;
screenWidth=(gridWidth>=screenWidth) ? screenWidth : gridWidth;
double gridHeight=(rules.size()+2)*(IMAGE_HEIGHT_NORMAL+10)+rules.size()*20+(rules.size()+2)*10;
double screenHeight=Toolkit.getDefaultToolkit().getScreenSize().getHeight()*0.8;
screenHeight=(gridHeight>=screenHeight) ? screenHeight : gridHeight;
setSize((int)screenWidth, (int)screenHeight);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setLocation(screenSize.width/2-getWidth()/2, screenSize.height/2-getHeight()/2);
setMaximumSize(new Dimension((int)gridWeight, (int)gridHeight));
setVisible(true);
This code does the following thing: calculates preferred width and height of frame, and if the size bigger then I want (80% of width and height), then application sets another size; but I want to allow user to click by "full screen" icon on the jframe in order to maximize jframe for preferred size (setMaximumSize() method), but it code doesn't work! Always a windows maximizes to full screen of my laptop! How can I fix it?

I'm not sure if I understood your problem correctly, but I suggest using
java.awt.Window#setBounds(int, int, int, int)
to set the location and size of a JFrame.
int x = screenSize.width/2-getWidth()/2;
int y = screenSize.height/2-getHeight()/2;
int width = (int)gridWeight;
int height = (int)gridHeight);
setBounds(x, y, width, height);
To maximize a JFrame I suggest using
java.awt.Frame#setExtendedState(int)
like this
setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);

You need to do like this :
_mainDialog.setBounds(rectangle);
_mainDialog.setExtendedState(windowsState);
Example :
addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent e) {
int oldState = e.getOldState();
int newState = e.getNewState();
if ((oldState & JFrame.ICONIFIED) == 0 && (newState & JFrame.ICONIFIED) != 0) {
setMainDialogRectangle(minRectangle, JFrame.NORMAL);
}
else if ((oldState & JFrame.ICONIFIED) != 0 && (newState & JFrame.ICONIFIED) == 0) {
setMainDialogRectangle(minRectangle, JFrame.NORMAL);
}
else if ((e.getNewState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) {
setMainDialogRectangle(maxRectangle, JFrame.MAXIMIZED_BOTH);
}
else if ((oldState & JFrame.MAXIMIZED_BOTH) != 0 && (newState & JFrame.MAXIMIZED_BOTH) == 0) {
setMainDialogRectangle(medRectangle, JFrame.NORMAL);
}
}
});

Related

Zoom on a JScrollPane with headers

I hava a JFrame containing a table with row and column headers.
My table is a custom component made of 3 panels (row header, column header and grid).
The panels are regular JPanels, containing either JButton or JLabel, in a MigLayout.
I display this component inside a JScrollPane in order to scroll simultaneously my grid and my headers.
This part works fine.
Now, the user should be able to zoom on my component.
I tried to use the pbjar JXLayer but if I put my whole JScrollPane inside the layer, everything is zoomed, event the scrollbars.
I tried to use 3 JXLayers, one for each viewPort of my JScrollPane. But this solution just mess up with my layout as the panels inside the viewPorts get centered instead of being top-left aligned.
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class Matrix extends JScrollPane {
private Grid grid;
private Header rowHeader;
private Header columnHeader;
private DefaultTransformModel zoomTransformModel;
private double zoom = 1;
public Matrix() {
super(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
this.zoomTransformModel1 = new DefaultTransformModel();
this.zoomTransformModel1.setScaleToPreferredSize(true);
this.zoomTransformModel1.setScale(1);
this.zoomTransformModel2 = new DefaultTransformModel();
this.zoomTransformModel2.setScaleToPreferredSize(true);
this.zoomTransformModel2.setScale(1);
this.zoomTransformModel3 = new DefaultTransformModel();
this.zoomTransformModel3.setScaleToPreferredSize(true);
this.zoomTransformModel3.setScale(1);
this.grid = new Grid();
this.setViewportView(TransformUtils.createTransformJXLayer(this.grid,
zoomTransformModel1););
this.matrixRowHeader = new Header(Orientation.VERTICAL);
this.setRowHeader(new JViewport(
TransformUtils.createTransformJXLayer(
this.rowHeader, zoomTransformModel2)));
this.matrixColumnHeader = new Header(Orientation.HORIZONTAL);
this.setColumnHeader(new JViewport(
TransformUtils.createTransformJXLayer(
this.columnHeader, zoomTransformModel2)));
}
public void setScale(double scale) {
this.zoomTransformModel1.setScale(scale);
this.zoomTransformModel2.setScale(scale);
this.zoomTransformModel3.setScale(scale);
}
}
How could I handle the zoom on my JScrollPane without zooming on the scrollBars and without messing up my layout?
First, MigLayout seems to be incompatible with the JXLayer. When using both, the components in the panel using the MigLayout have a unpredictable behaviour.
Then, the original pbjar JXLayer only allows you to put your component in the center of the Layer pane.
Pbjar sources can be download on github. Note this is not the official Piet Blok repository.
The solution I found is to modify the TransformLayout, TransformUI, and the TranformModel classes:
Alignment enum give the possible alignment for the component in the layer.
public enum Alignment {
TOP,
BOTTOM,
LEFT,
RIGHT,
CENTER
}
In TransformLayout :
#Override
public void layoutContainer(Container parent) {
JXLayer<?> layer = (JXLayer<?>) parent;
LayerUI<?> layerUI = layer.getUI();
if (layerUI instanceof CustomTransformUI) {
JComponent view = (JComponent) layer.getView();
JComponent glassPane = layer.getGlassPane();
if (view != null) {
Rectangle innerArea = new Rectangle();
SwingUtilities.calculateInnerArea(layer, innerArea);
view.setSize(view.getPreferredSize());
Rectangle viewRect = new Rectangle(0, 0, view.getWidth(), view
.getHeight());
int x;
int y;
Alignment alignX = ((CustomTransformUI) layerUI).getAlignX();
Alignment alignY = ((CustomTransformUI) layerUI).getAlignY();
if(alignX == Alignment.LEFT) {
x = (int) (innerArea.getX() - viewRect.getX());
} else if(alignX == Alignment.RIGHT) {
x = (int) (innerArea.getX()+innerArea.getWidth()-viewRect.getWidth()-viewRect.getX());
} else {
x = (int) Math.round(innerArea.getCenterX()
- viewRect.getCenterX());
}
if(alignY == Alignment.TOP) {
y = (int) (innerArea.getY() - viewRect.getY());
} else if(alignY == Alignment.BOTTOM) {
y = (int) (innerArea.getY()+innerArea.getHeight()-viewRect.getHeight()-viewRect.getY());
} else {
y = (int) Math.round(innerArea.getCenterY()
- viewRect.getCenterY());
}
viewRect.translate(x, y);
view.setBounds(viewRect);
}
if (glassPane != null) {
glassPane.setLocation(0, 0);
glassPane.setSize(layer.getWidth(), layer.getHeight());
}
return;
}
super.layoutContainer(parent);
}
In TransformUI :
private Alignment alignX; // horizontal alignment
private Alignment alignY; // verticalalignment
public TransformUI(TransformModel model, Alignment alignX, Alignment alignY) {
super();
this.setModel(model);
this.alignX = alignX;
this.alignY = alignY;
}
public Alignment getAlignX() {
return alignX;
}
public Alignment getAlignY() {
return alignY;
}
In TransformModel:
private Alignment alignX = Alignment.CENTER;
private Alignment alignY = Alignment.CENTER;
public CustomTransformModel(Alignment alignX, Alignment alignY) {
super();
this.alignX = alignX;
this.alignY = alignY;
}
#Override
public AffineTransform getTransform(JXLayer<? extends JComponent> layer) {
JComponent view = (JComponent)layer.getView();
/*
* Set the current actual program values in addition to the user
* options.
*/
this.setValue(Type.LayerWidth, layer == null ? 0 : layer.getWidth());
this.setValue(Type.LayerHeight, layer == null ? 0 : layer.getHeight());
this.setValue(Type.ViewWidth, view == null ? 0 : view.getWidth());
this.setValue(Type.ViewHeight, view == null ? 0 : view.getHeight());
/*
* If any change to previous values, recompute the transform.
*/
if (!Arrays.equals(this.prevValues, this.values)) {
System.arraycopy(this.values, 0, this.prevValues, 0, this.values.length);
this.transform.setToIdentity();
if (view != null) {
double scaleX;
double scaleY;
double centerX;
if(this.alignX == Alignment.LEFT) {
centerX = 0.0;
} else if (this.alignX == Alignment.RIGHT){
centerX = layer == null ? 0.0 : (double)layer.getWidth();
} else {
centerX = layer == null ? 0.0 : (double)layer.getWidth() / 2.0;
}
double centerY;
if(this.alignY == Alignment.TOP) {
centerY = 0.0;
} else if(this.alignY == Alignment.BOTTOM){
centerY = layer == null ? 0.0 : (double)layer.getHeight();
} else {
centerY = layer == null ? 0.0 : (double)layer.getHeight() / 2.0;
}
AffineTransform nonScaledTransform = this.transformNoScale(centerX, centerY);
if (((Boolean)this.getValue(Type.ScaleToPreferredSize)).booleanValue()) {
scaleY = scaleX = ((Double)this.getValue(Type.PreferredScale)).doubleValue();
} else {
Area area = new Area(new Rectangle2D.Double(0.0, 0.0, view.getWidth(), view.getHeight()));
area.transform(nonScaledTransform);
Rectangle2D bounds = area.getBounds2D();
scaleX = layer == null ? 0.0 : (double)layer.getWidth() / bounds.getWidth();
scaleY = layer == null ? 0.0 : (double)layer.getHeight() / bounds.getHeight();
if (((Boolean)this.getValue(Type.PreserveAspectRatio)).booleanValue()) {
scaleY = scaleX = Math.min(scaleX, scaleY);
}
}
this.transform.translate(centerX, centerY);
this.transform.scale((Boolean)this.getValue(Type.Mirror) != false ? - scaleX : scaleX, scaleY);
this.transform.translate(- centerX, - centerY);
this.transform.concatenate(nonScaledTransform);
}
}
return this.transform;
}
You can now create a zoomable panel with configurable alignment using:
TransformModel model = new TransformModel(Alignment.LEFT, Alignment.TOP);
TransformUI ui = new TransformUI(model, Alignment.LEFT, Alignment.TOP);
new JXLayer((Component)component, (LayerUI)ui)
Note that's a quick fix. It can probably be improved.

Combining two codes with a mousePressed-event processing

My problem is following:
I wrote a code and managed to display an image when i click on a rectangle (with the loadImage-fuction). The rectangle serves as a button that I want to replace with an image later.
But i actually don't just want an image to be displayed when the button is clicked. I want to call a code, to copy an image onto another:
public static int SQUARE_WIDTH = 30;
public static int SQUARE_HEIGHT = 30;
PImage img1,img2, img3;
void setup() {
size(670, 943);
img1 = loadImage("white.png");
img2 = loadImage("hase.jpg");
img3= loadImage("ohrring.jpg");
image(img1,0,0);
}
void draw() {
if(mousePressed)
copy(img2,
constrain(mouseX-SQUARE_WIDTH/2,0,width),
constrain(mouseY-SQUARE_HEIGHT/2,0,height),
SQUARE_WIDTH,SQUARE_HEIGHT,
constrain(mouseX-SQUARE_WIDTH/2,0,width),
constrain(mouseY-SQUARE_HEIGHT/2,0,height),
SQUARE_WIDTH,SQUARE_HEIGHT);
}
The copy-code doesn't simply copy an image, it uses the mouse as a brush! When you "draw" on an area, the image shows with the "strokes" of the brush pixel after pixel!
processing.org/reference/copy_.html
I happen to have huge problems when I want to combine this one with my main code:
int rectX, rectY;
int rectSize = 90;
boolean rectOver = false;
color rectHighlight;
color currentColor, baseColor;
color rectColor;
public static int SQUARE_WIDTH = 30;
public static int SQUARE_HEIGHT = 30;
PImage img1,img2, img3;
void setup() {
size(670, 943);
rectColor = color(0);
rectX = width/2-rectSize-10;
rectY = height/2-rectSize/2;
baseColor = color(102);
currentColor = baseColor;
img1 = loadImage("frida.jpg");
img2 = loadImage("hase.jpg");
img3 = loadImage("white.png");
background(img3);
}
void draw() {
update(mouseX, mouseY);
if (rectOver) {
fill(rectHighlight);
} else {
fill(rectColor);
}
stroke(255);
rect(rectX, rectY, rectSize, rectSize);
}
void update(int x, int y) {
if ( overRect(rectX, rectY, rectSize, rectSize) ) {
rectOver = true;
}else {
rectOver = false;
}
}
void mousePressed() {
if (rectOver) {
background(img2);
}
}
boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}
Theoretically I got the tip to set a boolean in mousePressed() to do the copy-operation in draw(), and then to check this boolean in draw(): if set (true) it shall do the copy. But I'm unfortunately not the brightest star in the programming-sky , so could anybody show me what this part is supposed to look like? Of course, I'm open to other suggestions how to solve this problem!
Thank you!
I hope this is what you are looking for. If you want to copy an image you don't need to call a function to copy an image, you can simply invoke the = sign and the image will be copied.
In my example code buttonImage is the image on the button. Whenever you don't want an image on the button assign it the following way:
buttonImage = null;
If you want to have an image instead of the rectangle do the following:
buttonImage = yourImage;
buttonImage.resize(w, h); //fit it on the button.
I think this is what you want to achieve?
PImage buttonImage;
void setup()
{
}
void draw()
{
if(buttonImage == null || buttonImage.width < 2) rect(x, y, w, h);
else image(buttonImage, x, y);
}
void mouseReleased()
{
if(mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h)
{
//copy any image onto the buttonImage
buttonImage = loadImage("newPath.png"); //update / overwrite image
buttonImage.resize(w, h); //fit it on the button
}
}
x and y are the position of the button, w and h are the width and the height of the button in my example.
EDIT:
Ok, so basically you want to have a white background and you want to scrap it using your tool so an image appears? I'm still not 100% sure of what you're asking, but if that is the case try this:
I used img.get() instead of img.copy(), because it has less parameters to deal with. I really hoped i understood this correctly, if not maybe link a video to something similar? I have a hard time understanding what you want.
The toolSelected integer is a counter to which tool you are using. Depending on its value, it is executing a different code.
My code:
PImage img1;
int toolSelected = 0; //Normal tool;
int widthOfBrush = 20; //Now you are drawing 20x20
int buttonX = 200;
int buttonY = 200;
int buttonW = 40;
int buttonH = 20;
void setup()
{
size(640, 480);
background(255);
img1 = loadImage("yourImg.png");
img1.resize(width, height); //Fit it on processing screen
}
void draw()
{
if(toolSelected == 0) {}//other tool
//Instead of using copy we will be using buttonImage.get(x, y, w, h) --> makes more sense
if(toolSelected == 1 && mousePressed)
{
float yourX = mouseX;
float yourY = mouseY;
yourX -= floor(widthOfBrush / 2);
yourY -= floor(widthOfBrush / 2);
//scale & copy:
PImage brushImage = img1.get((int)yourX, (int)yourY, widthOfBrush * (width / img1.width), widthOfBrush * (width / img1.width)); //Draw the image at your destination
image(brushImage, mouseX, mouseY);
}
stroke(0);
fill(255);
rect(buttonX, buttonY, buttonW, buttonH);
}
void mouseReleased()
{
if (mouseX > buttonX && mouseX < buttonX + buttonW && mouseY > buttonY && mouseY < buttonY + buttonH)
{
//copy any image onto the buttonImage
//buttonImage = loadImage("newPath.png"); //update / overwrite image
toolSelected = 1; //Our tool is the image brush now.
}
}

Unpleasant little square locomotion (Moving a JComponent in a JPanel with the keys)

Ok, I'm trying to reinvent the wheel and program a snake game only with Swing and AWT and things that come with Java by default. But, far before that, I'm learning how to MOVE a square on the window. Cutting the mimimi, here's the thing:
I have a custom JComponent class called HeadSquare:
public class HeadSquare extends JComponent {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = (int) screenSize.getWidth();
int screenHeight = (int) screenSize.getHeight();
int defaultSize = screenWidth / 2;
int snakeSquareDefaultSize = 10;
int headSquareXPos = ((defaultSize / 2) - (snakeSquareDefaultSize / 2));
int headSquareYPos = ((defaultSize / 2) - (snakeSquareDefaultSize / 2));
Rectangle head = new Rectangle(headSquareXPos, headSquareYPos, snakeSquareDefaultSize, snakeSquareDefaultSize);
Color snakeColor = Color.green;
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(0, 0);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(defaultSize, defaultSize);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
g.setColor(snakeColor);
super.paintComponent(g);
graphics2D.fill(head);
}
}
And I have a brilliant JPanel (that was declared up-somewhere-else):
snakePanel = new JPanel();
Main bla bla bla:
headSquare = new HeadSquare();
snakePanel.requestFocus();
snakePanel.add(headSquare);
And here it goes:
snakePanel.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
headSquare.headSquareXPos -= 10;
headSquare.head.setLocation(headSquare.headSquareXPos, headSquare.headSquareYPos);
snakePanel.repaint();
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
headSquare.headSquareXPos += 10;
headSquare.head.setLocation(headSquare.headSquareXPos, headSquare.headSquareYPos);
snakePanel.repaint();
} else if (arg0.getKeyCode() == KeyEvent.VK_UP) {
headSquare.headSquareYPos -= 10;
headSquare.head.setLocation(headSquare.headSquareXPos, headSquare.headSquareYPos);
snakePanel.repaint();
} else if (arg0.getKeyCode() == KeyEvent.VK_DOWN) {
headSquare.headSquareYPos += 10;
headSquare.head.setLocation(headSquare.headSquareXPos, headSquare.headSquareYPos);
snakePanel.repaint();
}
}
});
contentPane.add(snakePanel, BorderLayout.CENTER);
The problem may become evident for the skilled ones (Everyone but me): The bloody square STOPS when you press an arrow, and thereafter press another arrow. E.g: Up.....Left. If you press both at the same time, it goes perfect, but if you press LEFT (-= 10), and then UP (it STOPS the (-= 10) of LEFT, it waits some time and it starts going UP, ignoring the old LEFT).
Well, I hope the topic is not too prolix, and that I've expressed myself clearly. I want it to combine the movement of the arrows without inconvenient haltings (in Allegro, in C, this happens by default). Do you know in depth why this happens? Do you know how to improve this movement?
I thank you very much!
See Motion Using the Keyboard that explains the problems of using a Key Listener. The preferred solution is to use Key Bindings.

JPanel inside a JScrollPane

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);
}
}
}

XY Layout JAVA

is there any sort of XY-Layout to Java?
So I can set a Button at the X and Y cordinate and that it is suppose to be that big etc.... Because this border layout and grid and panel thing is driven me crazy. :)
They are flowing every were and getting strecht up. And to make them small you have to put panel in panel in panel in panel ^^,
When setting the container's layout to null (no LayoutManager), you can set the component's bounds individually with component.setBounds(x,y,w,h).
Fixed layouts are in 99% of all cases bad UI design (if your labels, for example, don't get their preferred size you run into mayor problems when your application supports multiple languages), so my advice for you is to rather write a specialized layout manager for your specific needs.
Writing your custom layout manager is quite easy, all you have to do is to be able to calculate the preferred size for a container with given components and your layout, and to do the layout by setting the (calculated) bounds of your components.
I got rid of the GridBagLayout and started coding my own layouts long ago, and layouting has never been easier.
Here's an example of a custom layout, that layouts pairs of key and value components:
public class KeyValueLayout implements LayoutManager {
public static enum KeyAlignment {
LEFT, RIGHT;
}
private KeyAlignment keyAlignment = KeyAlignment.LEFT;
private int hgap;
private int vgap;
public KeyValueLayout () {
this(KeyAlignment.LEFT);
}
public KeyValueLayout (KeyAlignment keyAlignment) {
this(keyAlignment, 5, 5);
}
public KeyValueLayout (int hgap, int vgap) {
this(KeyAlignment.LEFT, hgap, vgap);
}
public KeyValueLayout (KeyAlignment keyAlignment, int hgap, int vgap) {
this.keyAlignment = keyAlignment != null ? keyAlignment : KeyAlignment.LEFT;
this.hgap = hgap;
this.vgap = vgap;
}
public void addLayoutComponent (String name, Component comp) {
}
public void addLayoutComponent (Component comp, Object constraints) {
}
public void removeLayoutComponent (Component comp) {
}
public void layoutContainer (Container parent) {
Rectangle canvas = getLayoutCanvas(parent);
int ypos = canvas.y;
int preferredKeyWidth = getPreferredKeyWidth(parent);
for (Iterator<Component> iter = new ComponentIterator(parent); iter.hasNext();) {
Component key = (Component) iter.next();
Component value = iter.hasNext() ? (Component) iter.next() : null;
int xpos = canvas.x;
int preferredHeight = Math.max(key.getPreferredSize().height, value != null ? value.getPreferredSize().height : 0);
if (keyAlignment == KeyAlignment.LEFT)
key.setBounds(xpos, ypos, key.getPreferredSize().width, key.getPreferredSize().height);
else
key.setBounds(xpos + preferredKeyWidth - key.getPreferredSize().width, ypos, key.getPreferredSize().width,
key.getPreferredSize().height);
xpos += preferredKeyWidth + hgap;
if (value != null)
value.setBounds(xpos, ypos, canvas.x + canvas.width - xpos, preferredHeight);
ypos += preferredHeight + vgap;
}
}
public Dimension minimumLayoutSize (Container parent) {
int preferredKeyWidth = getPreferredKeyWidth(parent);
int minimumValueWidth = 0;
int minimumHeight = 0;
int lines = 0;
for (Iterator<Component> iter = new ComponentIterator(parent); iter.hasNext();) {
lines++;
Component key = (Component) iter.next();
Component value = iter.hasNext() ? (Component) iter.next() : null;
minimumHeight += Math.max(key.getPreferredSize().height, value != null ? value.getMinimumSize().height : 0);
minimumValueWidth = Math.max(minimumValueWidth, value != null ? value.getMinimumSize().width : 0);
}
Insets insets = parent.getInsets();
int minimumWidth = insets.left + preferredKeyWidth + hgap + minimumValueWidth + insets.right;
minimumHeight += insets.top + insets.bottom;
if (lines > 0)
minimumHeight += (lines - 1) * vgap;
return new Dimension(minimumWidth, minimumHeight);
}
public Dimension preferredLayoutSize (Container parent) {
int preferredKeyWidth = getPreferredKeyWidth(parent);
int preferredValueWidth = 0;
int preferredHeight = 0;
int lines = 0;
for (Iterator<Component> iter = new ComponentIterator(parent); iter.hasNext();) {
lines++;
Component key = (Component) iter.next();
Component value = iter.hasNext() ? (Component) iter.next() : null;
preferredHeight += Math.max(key.getPreferredSize().height, value != null ? value.getPreferredSize().height : 0);
preferredValueWidth = Math.max(preferredValueWidth, value != null ? value.getPreferredSize().width : 0);
}
Insets insets = parent.getInsets();
int preferredWidth = insets.left + preferredKeyWidth + hgap + preferredValueWidth + insets.right;
preferredHeight += insets.top + insets.bottom;
if (lines > 0)
preferredHeight += (lines - 1) * vgap;
return new Dimension(preferredWidth, preferredHeight);
}
public Dimension maximumLayoutSize (Container target) {
return preferredLayoutSize(target);
}
private int getPreferredKeyWidth (Container parent) {
int preferredWidth = 0;
for (Iterator<Component> iter = new ComponentIterator(parent); iter.hasNext();) {
Component key = (Component) iter.next();
if (iter.hasNext())
iter.next();
preferredWidth = Math.max(preferredWidth, key.getPreferredSize().width);
}
return preferredWidth;
}
private Rectangle getLayoutCanvas (Container parent) {
Insets insets = parent.getInsets();
int x = insets.left;
int y = insets.top;
int width = parent.getSize().width - insets.left - insets.right;
int height = parent.getSize().height - insets.top - insets.bottom;
return new Rectangle(x, y, width, height);
}
private class ComponentIterator implements Iterator<Component> {
private Container container;
private int index = 0;
public ComponentIterator (Container container) {
this.container = container;
}
public boolean hasNext () {
return index < container.getComponentCount();
}
public Component next () {
return container.getComponent(index++);
}
public void remove () {
}
}
}
Just set the layout and add alternatingly labels and value components. It's easy to use, especially compared to GridBagLayout or nested panels with custom layouts.
The reason components resize is so stuff looks nice whatever size the window is, so Swing discourages straight X-Y position. You might want to have a look at GroupLayout http://java.sun.com/docs/books/tutorial/uiswing/layout/group.html which is designed for GUI builders, and the page mentioned above describes using invisible components to absorb the stretches. eg:layout.setAutoCreateGaps(true);
SpringLayout might also be useful - see the visual guide
If you really want X and Y then set the layout manager to null, and use setLocation() or setBounds(). I REALLY REALLY wouldn't recommend this, but it does work. Have a read of this tutorial
If you really want to do this, use
setLayout(null);
on the component you're putting things into, then use
setBounds(x, y, width, height);
to set the absolute coordinates of the elements. Example:
setLayout(null);
JButton myButton = new JButton("Do Stuff");
add(myButton);
myButton.setBounds(30, 30, 100, 30);
However, the resulting GUI will look non-standard, won't be resizable, and if it's of any complexity, will be a pain to maintain.
I know that layouting is frustrating, but in the end you will be better off using a combination of BorderLayouts and FlowLayouts, orGridBagLayout - or an IDE interface builder like Eclipse's or Netbeans'.
This doesn't answer your specific question, but as well as agreeing with the other comments, have a look at MiG Layout. I have been equally frustrated with layouts as a Swing newbie, but this has helped a lot.

Categories

Resources