任务
我想从头开始用JavaFX创建一个甘特图.
例
假设我有2台机器A和机器B,它们有2种状态在线(绿色)和离线(红色).我想以给定的时间间隔在水平彩色条中显示它们的状态,X轴是日期轴,Y轴是机器轴.
题
我从哪里开始?我需要使用哪些课程?如果有人有一个最小的例子并且可以分享它会很棒.
非常感谢你.
事实证明,BubbleChart源是一个很好的例子.
基本上,您可以使用XYChart的修改版本及其数据.您需要做的是添加额外数据,例如值有效的时间长度以及着色的一些样式.
剩下的就是使用日期轴,因此使用日期值而不是数字值.
这是我想出来的,以防任何人想要玩弄它:
另一个例子:
来源:
GanttChart.java:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javafx.beans.NamedArg; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Node; import javafx.scene.chart.Axis; import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.ValueAxis; import javafx.scene.chart.XYChart; import javafx.scene.layout.StackPane; import javafx.scene.shape.Rectangle; public class GanttChartextends XYChart { public static class ExtraData { public long length; public String styleClass; public ExtraData(long lengthMs, String styleClass) { super(); this.length = lengthMs; this.styleClass = styleClass; } public long getLength() { return length; } public void setLength(long length) { this.length = length; } public String getStyleClass() { return styleClass; } public void setStyleClass(String styleClass) { this.styleClass = styleClass; } } private double blockHeight = 10; public GanttChart(@NamedArg("xAxis") Axis xAxis, @NamedArg("yAxis") Axis yAxis) { this(xAxis, yAxis, FXCollections. >observableArrayList()); } public GanttChart(@NamedArg("xAxis") Axis xAxis, @NamedArg("yAxis") Axis yAxis, @NamedArg("data") ObservableList > data) { super(xAxis, yAxis); if (!(xAxis instanceof ValueAxis && yAxis instanceof CategoryAxis)) { throw new IllegalArgumentException("Axis type incorrect, X and Y should both be NumberAxis"); } setData(data); } private static String getStyleClass( Object obj) { return ((ExtraData) obj).getStyleClass(); } private static double getLength( Object obj) { return ((ExtraData) obj).getLength(); } @Override protected void layoutPlotChildren() { for (int seriesIndex=0; seriesIndex series = getData().get(seriesIndex); Iterator> iter = getDisplayedDataIterator(series); while(iter.hasNext()) { Data item = iter.next(); double x = getXAxis().getDisplayPosition(item.getXValue()); double y = getYAxis().getDisplayPosition(item.getYValue()); if (Double.isNaN(x) || Double.isNaN(y)) { continue; } Node block = item.getNode(); Rectangle ellipse; if (block != null) { if (block instanceof StackPane) { StackPane region = (StackPane)item.getNode(); if (region.getShape() == null) { ellipse = new Rectangle( getLength( item.getExtraValue()), getBlockHeight()); } else if (region.getShape() instanceof Rectangle) { ellipse = (Rectangle)region.getShape(); } else { return; } ellipse.setWidth( getLength( item.getExtraValue()) * ((getXAxis() instanceof NumberAxis) ? Math.abs(((NumberAxis)getXAxis()).getScale()) : 1)); ellipse.setHeight(getBlockHeight() * ((getYAxis() instanceof NumberAxis) ? Math.abs(((NumberAxis)getYAxis()).getScale()) : 1)); y -= getBlockHeight() / 2.0; // Note: workaround for RT-7689 - saw this in ProgressControlSkin // The region doesn't update itself when the shape is mutated in place, so we // null out and then restore the shape in order to force invalidation. region.setShape(null); region.setShape(ellipse); region.setScaleShape(false); region.setCenterShape(false); region.setCacheShape(false); block.setLayoutX(x); block.setLayoutY(y); } } } } } public double getBlockHeight() { return blockHeight; } public void setBlockHeight( double blockHeight) { this.blockHeight = blockHeight; } @Override protected void dataItemAdded(Series series, int itemIndex, Data item) { Node block = createContainer(series, getData().indexOf(series), item, itemIndex); getPlotChildren().add(block); } @Override protected void dataItemRemoved(final Data item, final Series series) { final Node block = item.getNode(); getPlotChildren().remove(block); removeDataItemFromDisplay(series, item); } @Override protected void dataItemChanged(Data item) { } @Override protected void seriesAdded(Series series, int seriesIndex) { for (int j=0; j item = series.getData().get(j); Node cOntainer= createContainer(series, seriesIndex, item, j); getPlotChildren().add(container); } } @Override protected void seriesRemoved(final Series series) { for (XYChart.Data d : series.getData()) { final Node cOntainer= d.getNode(); getPlotChildren().remove(container); } removeSeriesFromDisplay(series); } private Node createContainer(Series series, int seriesIndex, final Data item, int itemIndex) { Node cOntainer= item.getNode(); if (cOntainer== null) { cOntainer= new StackPane(); item.setNode(container); } container.getStyleClass().add( getStyleClass( item.getExtraValue())); return container; } @Override protected void updateAxisRange() { final Axis xa = getXAxis(); final Axis ya = getYAxis(); List xData = null; List yData = null; if(xa.isAutoRanging()) xData = new ArrayList (); if(ya.isAutoRanging()) yData = new ArrayList (); if(xData != null || yData != null) { for(Series series : getData()) { for(Data data: series.getData()) { if(xData != null) { xData.add(data.getXValue()); xData.add(xa.toRealValue(xa.toNumericValue(data.getXValue()) + getLength(data.getExtraValue()))); } if(yData != null){ yData.add(data.getYValue()); } } } if(xData != null) xa.invalidateRange(xData); if(yData != null) ya.invalidateRange(yData); } } }
GanttChartSample.java:
import java.util.Arrays; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.scene.Scene; import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.paint.Color; import javafx.stage.Stage; import chart.gantt_04.GanttChart.ExtraData; // TODO: use date for x-axis public class GanttChartSample extends Application { @Override public void start(Stage stage) { stage.setTitle("Gantt Chart Sample"); String[] machines = new String[] { "Machine 1", "Machine 2", "Machine 3" }; final NumberAxis xAxis = new NumberAxis(); final CategoryAxis yAxis = new CategoryAxis(); final GanttChartchart = new GanttChart (xAxis,yAxis); xAxis.setLabel(""); xAxis.setTickLabelFill(Color.CHOCOLATE); xAxis.setMinorTickCount(4); yAxis.setLabel(""); yAxis.setTickLabelFill(Color.CHOCOLATE); yAxis.setTickLabelGap(10); yAxis.setCategories(FXCollections. observableArrayList(Arrays.asList(machines))); chart.setTitle("Machine Monitoring"); chart.setLegendVisible(false); chart.setBlockHeight( 50); String machine; machine = machines[0]; XYChart.Series series1 = new XYChart.Series(); series1.getData().add(new XYChart.Data(0, machine, new ExtraData( 1, "status-red"))); series1.getData().add(new XYChart.Data(1, machine, new ExtraData( 1, "status-green"))); series1.getData().add(new XYChart.Data(2, machine, new ExtraData( 1, "status-red"))); series1.getData().add(new XYChart.Data(3, machine, new ExtraData( 1, "status-green"))); machine = machines[1]; XYChart.Series series2 = new XYChart.Series(); series2.getData().add(new XYChart.Data(0, machine, new ExtraData( 1, "status-green"))); series2.getData().add(new XYChart.Data(1, machine, new ExtraData( 1, "status-green"))); series2.getData().add(new XYChart.Data(2, machine, new ExtraData( 2, "status-red"))); machine = machines[2]; XYChart.Series series3 = new XYChart.Series(); series3.getData().add(new XYChart.Data(0, machine, new ExtraData( 1, "status-blue"))); series3.getData().add(new XYChart.Data(1, machine, new ExtraData( 2, "status-red"))); series3.getData().add(new XYChart.Data(3, machine, new ExtraData( 1, "status-green"))); chart.getData().addAll(series1, series2, series3); chart.getStylesheets().add(getClass().getResource("ganttchart.css").toExternalForm()); Scene scene = new Scene(chart,620,350); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
ganttchart.css
.status-red { -fx-background-color:rgba(128,0,0,0.7); } .status-green { -fx-background-color:rgba(0,128,0,0.7); } .status-blue { -fx-background-color:rgba(0,0,128,0.7); }