Here's what I found useful for laying out components in some circumstances easier and more precise than Java's GridBagLayout. --CostinCozianu.
For the design of the client API, I applied EmulateKeywordAndDefaultParameters.
A layout that describes a grid, pretty much like GridBagLayout, the width of a column is given as a float. If it is greater than 1 than it is converted as int and sets the fixed number of pixels for that column, if it is within (0,1) it represents the weight in the distribution of free space. So is with the rows. Therefore a construction:
float lineParams[] = new float[] { 50f, .7f, .2f, 50f };
float columnParams[] = new float [] { .1f, .5f,.1f,75f};
new GridLayoutEx(lineParams,columnParams,2,2);
Represents a grid with first and fourth line fixed as 50 pixels, the second and third line divide their space proportionally to 7 and 2 (the sum of the "quota" (0,1) parameters need not be 1).The last column has a fixed size of 75 pixels while the first three columns divide the space proportionally with 1,5,1. There's a gap space between rows and columns of 2 pixels.
package home.costin.ui;
import java.awt.*; import java.io.ObjectStreamException; import java.io.Serializable;
/**
public class GridLayoutEx implements LayoutManager2 {
/**
public static final FillOption
NO_FILL= new FillOption(0),
FILL= new FillOption(1);
private final static FillOption[] FILL_CONSTS ={NO_FILL,FILL};
/**
public static class FillOption implements Serializable {
private int i=0;
private FillOption(int i_) {this.i=i_;}
private Object readResolve() throws ObjectStreamException { return FILL_CONSTS[i]; }
public boolean equals(Object o) {
if (this==o) return true;
else return this.i== ((FillOption)o).i;
}
public int hashCode(){ return i; }
}
/**
public static final VAlignOption
VA_CENTER= new VAlignOption(0),
VA_BOTTOM= new VAlignOption(1),
VA_TOP= new VAlignOption(2);
private final static VAlignOption[] VALIGN_CONSTS ={VA_CENTER,VA_BOTTOM,VA_TOP};
/**
public static class VAlignOption implements Serializable {
private int i=0;
private VAlignOption(int i_) {this.i=i_;}
private Object readResolve() throws ObjectStreamException { return VALIGN_CONSTS[i]; }
public boolean equals(Object o) {
if (this==o) return true;
else return this.i== ((VAlignOption)o).i;
}
public int hashCode(){ return i; }
}
/**
public static final HAlignOption
HA_CENTER= new HAlignOption(0),
HA_LEFT= new HAlignOption(1),
HA_RIGHT= new HAlignOption(2);
private final static HAlignOption[] HALIGN_CONSTS ={HA_CENTER,HA_LEFT,HA_RIGHT};
/**
public static class HAlignOption implements Serializable {
private int i=0;
private HAlignOption(int i_) {this.i=i_;}
private Object readResolve() throws ObjectStreamException { return HALIGN_CONSTS[i]; }
public boolean equals(Object o) {
if (this==o) return true;
else return this.i== ((HAlignOption)o).i;
}
public int hashCode(){ return i; }
}
public static class Constraint implements Serializable, Cloneable
{
private int line=0, column=0, rowspan=1, colspan=1;
public Constraint setLine(int line_) { this.line= line_; return this; }
public Constraint setColumn(int column_ ) { this.column= column_; return this; }
public Constraint setRowspan(int rowspan_) {this.rowspan= rowspan_; return this;}
public Constraint setColspan(int colspan_ ) { this.colspan= colspan_; return this; }
public Insets insets= new Insets(0,0,0,0);
public Constraint setInsets(Insets insets_) {this.insets= insets_; return this; }
private FillOption verticalFill= NO_FILL;
private FillOption horizontalFill= NO_FILL;
public Constraint setVerticalFill(FillOption option){ this.verticalFill=option; return this;};
public Constraint setHorizontalFill(FillOption option){ this.horizontalFill=option; return this;};
private VAlignOption verticalAlign= VA_CENTER;
private HAlignOption horizontalAlign= HA_CENTER;
public Constraint setVerticalAlign(VAlignOption option) {this.verticalAlign= option; return this; }
public Constraint setHAlignOption(HAlignOption option) {this.horizontalAlign= option; return this;}
public Object clone() throws CloneNotSupportedException
{
/*Constraint result= new Constraint();
result.colspan= colspan;
result.rowspan= rowspan;
result.column= column;
result.line= line;
result.insets= (Insets) (insets==null? null : insets.clone());*/
return (Constraint) super.clone();
}
private void computeBoundsInsideTheCell(
Component comp,
int x, int y, int w, int h){
x += insets.left; y += insets.top;
w -= insets.right; h -= insets.bottom;
Dimension d= comp.getPreferredSize();
if (horizontalFill==NO_FILL){
if (d.getWidth()< w){
if (horizontalAlign==HA_LEFT){
w= (int)d.getWidth();
}
else if (horizontalAlign==HA_RIGHT){
x += ( w - d.getWidth());
w = (int)d.getWidth();
}
else if (horizontalAlign==HA_CENTER) {
x += (w-d.getWidth())/2;
w= (int) d.getWidth();
}
}}
if (verticalFill==NO_FILL){
if (d.getHeight()< h){
if (verticalAlign==VA_TOP){
h= (int)d.getHeight();
}
else if (verticalAlign==VA_BOTTOM){
y += ( h - d.getHeight());
h = (int)d.getHeight();
}
else if (verticalAlign==VA_CENTER) {
y += (h-d.getHeight())/2;
h= (int) d.getHeight();
}
}}
comp.setBounds(x, y, w, h);
}
}
java.util.Hashtable components= new java.util.Hashtable();
/**
private boolean[] rowParamIsQuota;
private float[] rowParamsQuota;
private int[] rowParamsInt;
private int rowCount;
private boolean[] columnParamIsQuota;
private int[] columnParamsInt;
private float[] columnParamsQuotas;
private int colCount;
private int reservedWidth;
private int hgap;
private int vgap;
private int reservedHeight;
/**
private transient int rowCoordinates[] = null;
/**
private transient int columnCoordinates[] = null;
public GridLayoutEx (float [] lineParams, float [] columnParams)
{
this(lineParams,columnParams,1,1);
}
public GridLayoutEx (float [] rowParams, float [] columnParams, int hgap, int vgap)
{
if (rowParams == null || rowParams.length == 0)
throw new IllegalArgumentException("Illegal lineParams ");
if (columnParams == null || columnParams.length == 0)
throw new IllegalArgumentException("Illegal columnParams ");
rowCount= rowParams.length;
colCount= columnParams.length;
this.hgap= hgap<0 ? 0 : hgap;
this.vgap= vgap<0 ? 0 : vgap;
rowParamIsQuota= new boolean[rowCount];
rowParamsQuota= new float[rowCount];
rowParamsInt= new int[rowCount];
columnParamIsQuota= new boolean [colCount];
columnParamsQuotas= new float [colCount];
columnParamsInt= new int[colCount];
float sumOfLines=0;
for (int i=0; i
{
if (rowParams[i] <=0)
rowParams[i]= .1f;
if (rowParams[i] <= 1)
{
rowParamIsQuota[i]= true;
sumOfLines += rowParams[i];
}
else
{
rowParamIsQuota[i]= false;
reservedHeight += rowParams[i];
rowParamsInt[i]= (int)rowParams[i];
}
}
if (sumOfLines != 0)
for (int i=0; i
if (rowParamIsQuota[i])
rowParamsQuota[i]= rowParams[i]/sumOfLines;
float sumOfColumns=0;
for (int i=0; i
{
if (columnParams[i] <=0)
columnParams[i]= .1f;
if (columnParams[i] <= 1)
{
columnParamIsQuota[i]= true;
sumOfColumns += columnParams[i];
}
else
{
columnParamIsQuota[i]= false;
reservedWidth += columnParams[i];
columnParamsInt[i]= (int)columnParams[i];
}
}
if (sumOfColumns != 0)
for (int i=0; i
if (columnParamIsQuota[i])
columnParamsQuotas[i]= columnParams[i]/sumOfColumns;
// allocate lineCoordinates buffer
rowCoordinates = new int[rowCount + 1];
columnCoordinates= new int[colCount + 1];
}
/**
public void addLayoutComponent(Component comp, Object constraints) {
Constraint source = null;
if ( constraints == null || ! (constraints instanceof Constraint) )
source= new Constraint();
else
source = (Constraint) constraints;
try {
components.put(comp, source.clone()); }
catch (CloneNotSupportedException ex) {}
}
/**
public void addLayoutComponent(String name, Component comp) {
addLayoutComponent(comp, new Constraint());
} /**
private void distributeSpace(int d0, int length, int gap, int reservedSpace, int n, boolean[] paramIsQuota, float quotaParams[], int fixedParams[], int outCoordinates[]) {
int currentWidth = 0;
outCoordinates[0] = d0 + gap;
int h= length- reservedSpace - (n+1)*gap;
for (int i = 0; i < n; i++)
{
if (paramIsQuota[i])
currentWidth = (int) (h * quotaParams[i]);
else
currentWidth = fixedParams[i];
outCoordinates[i + 1] = outCoordinates[i] + currentWidth + gap;
}
}
/**
public float getLayoutAlignmentX(Container target) {
return 0.5f;
}
/**
public float getLayoutAlignmentY(Container target) {
return 0.5f;
}
/**
public void invalidateLayout(Container target) {
//nothing is cached
} /**
public void layoutContainer(Container parent) {
try
{
System.out.println("Layout called with dimensions:" + parent.getBounds());
synchronized (parent.getTreeLock())
{
Insets insets = parent.getInsets();
Component[] compArray = parent.getComponents();
int ncomponents = compArray.length;
if (ncomponents == 0)
return;
int w = parent.getSize().width - (insets.left + insets.right);
int h = parent.getSize().height - (insets.top + insets.bottom);
if (w <= 0 || h <= 0)
return;
// calculates the beginning of each line in pixel width coordinates
distributeSpace(insets.top, h , vgap, reservedHeight, rowCount, rowParamIsQuota,rowParamsQuota,rowParamsInt,rowCoordinates);
// calculates the beginning of each line in pixel width coordinates
distributeSpace(insets.left, w , hgap, reservedWidth, colCount, columnParamIsQuota,columnParamsQuotas,columnParamsInt,columnCoordinates);
for (int c = 0; c < ncomponents; c++)
{
Component comp = compArray[c];
Constraint constraint = null;
try
{
constraint = (Constraint) components.get(comp);
}
catch (ClassCastException ex)
{
};
int xx = 0, yy = 0, ww = 0, hh = 0;
if (constraint == null)
comp.setBounds(-200, -200, 100, 100);
else
try
{
xx = columnCoordinates[constraint.column];
yy = rowCoordinates[constraint.line];
ww = columnCoordinates[constraint.column + constraint.colspan] - xx - hgap;
hh = rowCoordinates[constraint.line + constraint.rowspan] - yy - vgap;
}
catch (ArrayIndexOutOfBoundsException ex)
{
xx = -200;
yy = -200;
ww = 100;
hh = 100;
};
constraint.computeBoundsInsideTheCell(comp, xx, yy, ww, hh);
}
}
}
catch (Throwable ex)
{
System.err.println("Exception caucht in Layout:" + ex);
ex.printStackTrace(System.err);
throw new RuntimeException(ex.getMessage());
}
} /**
public static void main(String[] args) {
try
{
Frame frame= new Frame("Testing GridLayoutEx layout manager");
frame.setSize(600,400);
frame.addWindowListener( new java.awt.event.WindowAdapter()
{
public void windowClosing(java.awt.event.WindowEvent event)
{
System.exit(0);
}
}
);
float lineParams[] = new float[] { 50f, .7f, .2f, 50f };
float columnParams[] = new float [] { .1f, .5f,.1f,75f};
frame.setLayout( new GridLayoutEx(lineParams,columnParams,2,2) );
GridLayoutEx.Constraint c =
new GridLayoutEx.Constraint(). setColumn(0).setLine(0)
.setColspan(3).setHorizontalFill(FILL).setVerticalFill(FILL);
Button pack= new Button("Pack");
pack.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed( java.awt.event.ActionEvent evt)
{
Frame parent= (Frame) ((Button) evt.getSource()).getParent();
parent.pack();
}
}
);
frame.add( pack, c);
c.setColumn(3).setLine(0).setColspan(1).setRowspan(1).setInsets(new Insets(0,0,10,10));
frame.add( new Button("B2"), c);
c.setColumn(0).setLine(1).setColspan(3).setRowspan(1);
c.setHorizontalFill(NO_FILL).setHAlignOption(HA_CENTER);
Button b3= new Button("B3"); b3.setSize(100,100);
frame.add( new Button("B3"), c);
c.setColumn(3).setLine(1).setColspan(1).setRowspan(1)
.setVerticalAlign(VA_BOTTOM).setVerticalFill(NO_FILL);
frame.add( new Button("B4"), c);
c.setColumn(0); c.setLine(2).setColspan(3).setRowspan(1);
frame.add( new Button("B5"), c);
c.setColumn( 3).setLine( 2).setColspan(1).setRowspan(1)
.setVerticalFill(FILL);
frame.add( new Button("B6"), c);
c.setColumn( 0).setLine( 3);
frame.add( new Button("B7"), c);
c.setColumn(1);
frame.add( new Button("B8"), c);
c.setColumn( 2);
frame.add( new Button("B9"), c);
c.setColumn(3).setHorizontalFill(FILL).setVerticalFill(FILL);
frame.add( new Button("B10"), c);
frame.setVisible(true);
}
catch(Exception ex)
{
System.err.println(ex);
ex.printStackTrace(System.err);
}
}
/**
public Dimension maximumLayoutSize(Container target) {
return preferredLayoutSize(target);
}
/**
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
/**
public Dimension preferredLayoutSize(Container parent) {
Insets insets= parent.getInsets();
Component [] compArray= parent.getComponents();
int [] preferredRowSizes= new int[rowCount];
int [] preferredColSizes= new int[colCount];
for (int x=0;x
Component comp= compArray[x];
Constraint constraint= (Constraint) components.get(comp);
Dimension currentDim= comp.getPreferredSize();
currentDim.width += constraint.insets.left + constraint.insets.right;
currentDim.height += constraint.insets.top + constraint.insets.bottom;
int beginRow= constraint.line;
int endRow= beginRow + constraint.rowspan;
float totalRowWeight=0f;
for (int row=beginRow;row
if (rowParamIsQuota[row]) totalRowWeight += rowParamsQuota[row];
for (int r=beginRow; r
if (rowParamIsQuota[r]) {
int rowSize= (int) (currentDim.height * rowParamsQuota[r] /totalRowWeight);
if ( rowSize > preferredRowSizes[r])
preferredRowSizes[r]= rowSize;
}
}
int beginCol= constraint.column;
int endCol= beginCol+ constraint.colspan;
float totalColWeight=0f;
for (int col=beginCol;col
if (columnParamIsQuota[col]) totalColWeight += columnParamsQuotas[col];
for (int col=beginCol; col
if (columnParamIsQuota[col]) {
int colSize= (int) (currentDim.width * columnParamsQuotas[col] / totalColWeight );
if ( colSize > preferredColSizes[col])
preferredColSizes[col]= colSize;
}
}
}
//done cycling through components
// let cycle through rows and columns to get the dimensions
int adjustableHeight=0;
for (int row=0;row < rowCount; row++) {
if (rowParamIsQuota[row]) {
int requiredHeight= ( int) (preferredRowSizes[row] / rowParamsQuota[row]);
if (requiredHeight>adjustableHeight) adjustableHeight= requiredHeight;
}
}
int adjustableWidth=0;
for (int col=0;col < colCount; col++) {
if (columnParamIsQuota[col]) {
int requiredWidth= ( int) (preferredColSizes[col] / columnParamsQuotas[col]);
if (requiredWidth>adjustableWidth) adjustableWidth= requiredWidth;
}
}
return new Dimension(reservedWidth+ adjustableWidth + colCount * hgap
, reservedHeight + adjustableHeight + rowCount * vgap);
}
/**
public void removeLayoutComponent(Component comp) {
components.remove(comp);
}
}