In case anyone ever wants an arrangement that in the very specific case of having 2 legends, or items placed at either edge of a container, and wanting the widths of those legends to adjust based on which contains more/less, here is some code:
Code: Select all
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import org.jfree.chart.block.Block;
import org.jfree.chart.block.BlockContainer;
import org.jfree.chart.block.FlowArrangement;
import org.jfree.chart.block.RectangleConstraint;
import org.jfree.ui.Size2D;
/**
* A modification of the FlowArrangment class for arranging two legends on the left and right side under the chart
* @author GarbageCan
*/
public class FlowArrangementVariableWidth extends FlowArrangement {
private final double horizontalGap = 1.0;
FlowArrangementVariableWidth() {
super();
}
/**
* Arranges the blocks in the container with a fixed width and no height
* constraint.
*
* @param container the container.
* @param constraint the constraint.
* @param g2 the graphics device.
*
* @return The size.
*/
@Override
protected Size2D arrangeFN(BlockContainer container, Graphics2D g2,
RectangleConstraint constraint) {
List blocks = container.getBlocks();
double width = constraint.getWidth();
Block block1 = (Block) blocks.get(0);
Block block2 = (Block) blocks.get(1);
Size2D size1 = block1.arrange(g2, RectangleConstraint.NONE);
Size2D size2 = block2.arrange(g2, RectangleConstraint.NONE);
double maxHeight;
if(size1.width+size2.width+horizontalGap >= width ) {
//Determine weighting of 80-20, 33-67, 50-50 and in which way based
//on the width of each block
double maxWidth1;
double maxWidth2;
if( size1.width >= 4*size2.width ) { // 80-20
maxWidth1 = (width*0.8d) - horizontalGap;
maxWidth2 = Math.min((width*0.2d) - horizontalGap, size2.width);
} else if( size1.width >= 2*size2.width ) { //67-33
maxWidth1 = (width*0.67d) - horizontalGap;
maxWidth2 = Math.min((width*0.33d) - horizontalGap, size2.width);
} else if( size2.width >= 4*size1.width ) { //20-80
maxWidth1 = Math.min((width*0.2d) - horizontalGap, size1.width);
maxWidth2 = (width*0.8d) - horizontalGap;
} else if( size2.width >= 2*size1.width ) { //33-67
maxWidth1 = Math.min((width*0.33d) - horizontalGap, size1.width);
maxWidth2 = (width*0.67d) - horizontalGap;
} else { // 50-50
maxWidth1 = Math.min((width*0.5d) - horizontalGap, size1.width);
maxWidth2 = Math.min((width*0.5d) - horizontalGap, size2.width);
}
size1 = block1.arrange(g2, RectangleConstraint.NONE.toFixedWidth(maxWidth1));
size2 = block2.arrange(g2, RectangleConstraint.NONE.toFixedWidth(maxWidth2));
block1.setBounds(new Rectangle2D.Double(0, 0, size1.width, size1.height));
block2.setBounds(new Rectangle2D.Double(width-size2.width, 0, size2.width,size2.height));
maxHeight = Math.max(size1.height, size2.height);
} else {//everything fits across
block1.setBounds(new Rectangle2D.Double(0, 0, size1.width, size1.height));
block2.setBounds(new Rectangle2D.Double(width-size2.width, 0, size2.width, size2.height));
maxHeight = Math.max(size1.height, size2.height);
}
return new Size2D(constraint.getWidth(), maxHeight);
}
/**
* Changes the arrangeRR so immediately calls arrangeFR to make sure that the legends remain at the edges
*
* @param container the container.
* @param constraint the constraint.
* @param g2 the graphics device.
*
* @return The size after the arrangement.
*/
@Override
protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) {
RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().getUpperBound() );
return arrangeFR(container, g2, c);
}
}
In my case, the contents of each legend could contain from 1-15 data series and so the variableWidth was important.