There is good option in JavaFX 2. I can turn on visibility
of grid lines for GridPane
. I like it. With
Swing and GridBagLayout
it takes time to find
out that element is placed in wrong cell and that is why
form looks bad. New options helps.
I was happy but not for long. The lines cross elements if
elements span two or more cells. Gaps are not visible. The
lines are gray and it is inpossible to change color.
I desided to draw my lines.
Method that draw lines is private. I can override it if I
rewrite almost whole GridPane
and not just
it. Adding new classes to project just for debugging is
not good idea.
So I replaced GridPane
in
jfxrt.jar
.
Lines
The method that draw lines is
private void layoutGridLines(double x, double y, double columnHeight, double rowWidth) {...}
, where (x
,y
) are top left
corner coordinates of the grid.
columnHeight
и rowWidth
- colomn
height and row width respectively.
This method has drawn lines from start to end with a gap between them. I had to add new checks.
At first I draw vertical lines. To do this, I iterate over
all columns. And inner loop checks rows to find cells that
span columns. If sush cells exist, line does not cross
them.
If the gap between columns (Hgap
) is more
than 0, I draw a rectangle, otherwise a line.\
The same algorithm is used for horizontal lines.
private void layoutGridLines(double x, double y, double columnHeight, double rowWidth)
{
if (!isGridLinesVisible()) {
return;
}
if (!gridLines.getChildren().isEmpty()) {
gridLines.getChildren().clear();
}
// create vertical lines
double linex = x;
double liney = y;
//iterate through the columns
for (int columnIndex = 0; columnIndex <= columnWidths.length; columnIndex++)
{
//iterate through the rows
rows:for(int rowIndex = 0; rowIndex < rowHeights.length; rowIndex++)
{
//look for children that span more then 1 columns
for(int k=0; k < getChildren().size(); k++)
{
Node node = getChildren().get(k);
//line crosses the node
if(getNodeColumnIndex(node) < columnIndex && getNodeColumnEnd(node) >= columnIndex
&& getNodeRowIndex(node) <= rowIndex && getNodeRowEnd(node) >= rowIndex)
{
//no draw line
liney += rowHeights[rowIndex];
//draw vgap
if (rowIndex < rowHeights.length - 1 && getVgap() != 0)
{
//draw rectangle
if(getHgap() != 0 && columnIndex < columnWidths.length && columnIndex > 0)
{
if(getNodeRowEnd(node) <= rowIndex)
{
gridLines.getChildren().add(createGridRectangle(linex, liney
, getHgap(), getVgap()));
}
}
//draw line
else
{
if(getNodeRowEnd(node) <= rowIndex || columnIndex == columnWidths.length || columnIndex == 0)
gridLines.getChildren().add(createGridLine(linex, liney
, linex, liney + getVgap()));
}
}
liney += getVgap();
continue rows;
}
}
//draw row
if(getHgap() != 0 && columnIndex < columnWidths.length && columnIndex > 0)
{
gridLines.getChildren().add(createGridRectangle(linex, liney
, getHgap(), rowHeights[rowIndex]));
}
else
{
gridLines.getChildren().add(createGridLine(linex, liney
, linex, liney + rowHeights[rowIndex]));
}
liney += rowHeights[rowIndex];
//draw vgap
if(getVgap() != 0 && rowIndex < rowHeights.length - 1)
{
if(getHgap() != 0 && columnIndex < columnWidths.length && columnIndex > 0)
{
gridLines.getChildren().add(createGridRectangle(linex, liney
, getHgap(), getVgap()));
}
else
gridLines.getChildren().add(createGridLine(linex, liney
, linex, liney + getVgap()));
liney += getVgap();
}
}
//to new column
liney = y;
if(columnIndex < columnWidths.length)
{
if(getHgap() != 0 && columnIndex > 0)
linex += getHgap();
linex += columnWidths[columnIndex];
}
}
// create horizontal lines
linex = x;
liney = y;
//iterate through the rows
for (int rowIndex = 0; rowIndex <= rowHeights.length; rowIndex++)
{
//iterate through the column
columns:for(int columnIndex = 0; columnIndex < columnWidths.length; columnIndex++)
{
//look for children that span more then 1 row
for(int k=0; k < getChildren().size(); k++)
{
Node node = getChildren().get(k);
//line crosses the node
if(getNodeColumnIndex(node) <= columnIndex && getNodeColumnEnd(node) >= columnIndex
&& getNodeRowIndex(node) < rowIndex && getNodeRowEnd(node) >= rowIndex)
{
//no draw line
linex += columnWidths[columnIndex];
//draw hgap
if (columnIndex < columnWidths.length - 1 && getHgap() != 0)
{
//draw rectangle
if(getVgap() != 0 && rowIndex < rowHeights.length && rowIndex > 0)
{
if(getNodeColumnEnd(node) <= columnIndex)
{
gridLines.getChildren().add(createGridRectangle(linex, liney
, getHgap(), getVgap()));
}
}
//draw line
else
{
if(getNodeColumnEnd(node) <= columnIndex || rowIndex == rowHeights.length || rowIndex == 0)
gridLines.getChildren().add(createGridLine(linex, liney
, linex, liney + getHgap()));
}
}
linex += getHgap();
continue columns;
}
}
//draw column
if(getVgap() != 0 && rowIndex < rowHeights.length && rowIndex > 0)
{
gridLines.getChildren().add(createGridRectangle(linex, liney
, columnWidths[columnIndex], getVgap()));
}
else
{
gridLines.getChildren().add(createGridLine(linex, liney
, linex + columnWidths[columnIndex], liney));
}
linex += columnWidths[columnIndex];
//draw hgap
if(getHgap() != 0 && columnIndex < columnWidths.length - 1)
{
if(getVgap() != 0 && rowIndex < rowHeights.length && rowIndex > 0)
{
gridLines.getChildren().add(createGridRectangle(linex, liney
, getHgap(), getVgap()));
}
else
gridLines.getChildren().add(createGridLine(linex, liney
, linex + getHgap(), liney));
linex += getHgap();
}
}
//to new row
linex = x;
if(rowIndex < rowHeights.length)
{
if(getVgap() != 0 && rowIndex > 0)
liney += getVgap();
liney += rowHeights[rowIndex];
}
}
}
Color
I want to change color of lines.
I added alpha channel to default color for grid lines.
private static final Color GRID_LINE_COLOR = Color.rgb(0, 0, 0, 0.3);
New Property
for color.
/**
* Grid line color.
*/
public final ObjectProperty<Paint> gridLineColorProperty()
{
if (gridLineColor == null)
{
gridLineColor = new StyleableObjectProperty<Paint>(Color.rgb(0, 0, 0, 0.3))
{
@Override
public void invalidated()
{
requestLayout();
}
@Override
public StyleableProperty getStyleableProperty()
{
return StyleableProperties.GRID_LINE_COLOR;
}
@Override
public Object getBean()
{
return GridPane.this;
}
@Override
public String getName()
{
return "gridLineColor";
}
};
}
return gridLineColor;
}
private ObjectProperty<Paint> gridLineColor;
public final void setGridLineColor(Paint value) {
gridLineColorProperty().set(value);
}
public final Paint getGridLineColor() {
return gridLineColor == null ? Color.rgb(0, 0, 0, 0.3) : gridLineColor.get();
}
Now I need to add new StyleableProperty
to
internal class StyleableProperties
.
private static final StyleableProperty<GridPane,Paint> GRID_LINE_COLOR =
new StyleableProperty<GridPane,Paint>("-fx-grid-line-color",
PaintConverter.getInstance(), Color.rgb(0, 0, 0, 0.3))
{
@Override
public boolean isSettable(GridPane node)
{
return node.gridLineColor == null || !node.gridLineColor.isBound();
}
@Override
public WritableValue<Paint> getWritableValue(GridPane node)
{
return node.gridLineColorProperty();
}
};
Now I can set color.
pane.setGridLinesVisible(true);
pane.setStyle("-fx-grid-line-color: rgba(200,0,0,.3);");
pane.setGridLineColor(Color.rgb(0,200,0,0.3));
Style overrides property. Grid lines will be red. Not green.
Test example:
@Override
public void start(Stage stage) throws Exception
{
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setPadding(new Insets(15,15,15,15));
pane.setVgap(30);
pane.setHgap(10);
pane.setGridLinesVisible(true);
pane.setStyle("-fx-grid-line-color: rgba(200,0,0,.3);");
// pane.setGridLineColor(Color.rgb(0,200,0,0.3));
Text column0row0cs4 = new Text("columnSpan:4");
pane.add(column0row0cs4, 0, 0, 4, 1);
Text column0row1 = new Text("column0row1");
pane.add(column0row1, 0, 1);
Text column1row1cs2 = new Text("columnSpan:2");
pane.add(column1row1cs2, 1, 1, 2, 1);
Text column3row0rs2 = new Text("rowSpan:2");
pane.add(column3row0rs2, 3, 1, 1, 2);
Text column0row2 = new Text("column0row2");
pane.add(column0row2, 0, 2);
Text column1row2cs2rs2 = new Text("columnSpan:2\n" +
"rowSpan:2");
pane.add(column1row2cs2rs2, 1, 2, 2, 2);
Text column0row3 = new Text("column0row3");
Text column3row3 = new Text("column3row3");
pane.add(column0row3, 0, 3);
pane.add(column3row3, 3, 3);
Text column0row4cs3 = new Text("columnSpan:3");
pane.add(column0row4cs3, 0, 4, 3, 1);
Text column3row4 = new Text("column3row4");
pane.add(column3row4, 3, 4);
Text column0row5 = new Text("column0row5");
pane.add(column0row5, 0, 5);
Text column1row5 = new Text("column1row5");
pane.add(column1row5, 1, 5);
Text column2row5 = new Text("column2row5");
pane.add(column2row5, 2, 5);
Text column3row5 = new Text("column3row5");
pane.add(column3row5, 3, 5);
Scene scene = null;
scene = new Scene(pane, 360, 330);
stage.setScene(scene);
stage.setTitle("Custom grid lines");
stage.show();
}
It will look like that (before and after):