Charts 删除JavaFX2线条图图例项

Charts 删除JavaFX2线条图图例项,charts,javafx-2,Charts,Javafx 2,我有很多系列的折线图。这些系列分为一个或多个超级系列。每一个超级系列在一行中可能有许多“中断”,以便准确地描述监视器进程何时没有积极地收集数据。每个数据中断实际上都在开始一个新的系列 我已经成功地克服了一些技术问题,例如图表为每个新系列分配了新颜色,图表线条符号颜色与系列颜色不匹配,等等。现在一切都运行得很好,只是每次我向图表添加新系列时,都会在图例中添加一个项目 有没有办法从图例中删除项目,或者我必须隐藏默认图例并添加自己的自定义图例窗格?: 然后,您可以创建自己的自定义窗格来创建自己的图例,

我有很多系列的折线图。这些系列分为一个或多个超级系列。每一个超级系列在一行中可能有许多“中断”,以便准确地描述监视器进程何时没有积极地收集数据。每个数据中断实际上都在开始一个新的系列

我已经成功地克服了一些技术问题,例如图表为每个新系列分配了新颜色,图表线条符号颜色与系列颜色不匹配,等等。现在一切都运行得很好,只是每次我向图表添加新系列时,都会在图例中添加一个项目

有没有办法从图例中删除项目,或者我必须隐藏默认图例并添加自己的自定义图例窗格?


然后,您可以创建自己的自定义窗格来创建自己的图例,并按照自己的意愿进行渲染。

您可以使用以下方法根据节点的类型(以及可选的样式名称)查找节点:

private static Node findNode(final Parent aParent, final String aClassname, final String aStyleName) {

    if (null != aParent) {
        final ObservableList<Node> children = aParent.getChildrenUnmodifiable();
        if (null != children) {
            for (final Node child : children) {

                String className = child.getClass().getName();

                if (className.contains("$")) {
                    className = className.substring(0, className.indexOf("$"));
                }

                if (0 == aClassname.compareToIgnoreCase(className)) {
                    if ((null == aStyleName) || (0 == aStyleName.length())) {
                        // No aStyleName, just return this:
                        return child;
                    }
                    else {
                        final String styleName = child.getStyleClass().toString();
                        if (0 == aStyleName.compareToIgnoreCase(styleName)) {
                            return child;
                        }
                    }
                }

                if (child instanceof Parent) {
                    final Node node = findNode((Parent) child, aClassname, aStyleName);

                    if (null != node) {
                        return node;
                    }
                }
            }
        }
    }

    return null;
}
然后,您可以遍历子项并删除不希望显示的子项:

    for (final Node legendItem : legend.getChildren()) {

        final Label legendLabel = (Label) legendItem;

        if (0 == legendLabel.getText().compareToIgnoreCase("the name of the legend I want hidden (or replaced with some other test)")) {
            legend.getChildren().remove(legendItem);
            break;
        }
    }


JavaFX还有一个函数,它“根据给定的CSS选择器查找此节点或第一个子节点”,其作用类似于此答案中的
findNode
函数。

在多次尝试实现各种建议失败后,我发现允许用户在JavaFX图表中显示/隐藏数据序列的最佳方法(或其子类)是扩展要使用的图表类并重写其UpdateGend()方法

实际上很简单。下面是一个使用基本HBox作为图例的示例,其中包含复选框作为图例项。在这个示例中,我决定使用固定轴类型(CategoryAxis和NumberAxis)制作线条图。您可以选择在子类中留下轴的泛型

public class AtopLineChart<X, Y> extends LineChart<String, Number>
{

   /**
    * @param xAxis
    * @param yAxis
    */
   public AtopLineChart(final CategoryAxis xAxis, final NumberAxis yAxis)
   {
      super(xAxis, yAxis);
   }

   /* (non-Javadoc)
    * @see javafx.scene.chart.LineChart#updateLegend()
    */
   @Override
   protected void updateLegend()
   {
      final HBox legend = new HBox();
      legend.setVisible(true);
      for (final javafx.scene.chart.XYChart.Series<String, Number> series : getData())
      {
         final CheckBox cb = new CheckBox(series.getName());
         cb.setUserData(series);
         cb.setSelected(true);
         cb.addEventHandler(ActionEvent.ACTION, e ->
         {
            final CheckBox box = (CheckBox) e.getSource();
            @SuppressWarnings("unchecked")
            final Series<String, Number> s = (Series<String, Number>) box.getUserData();
            s.getNode().setVisible(box.isSelected());
         });
         legend.getChildren().add(cb);
      }
      setLegend(legend);
   }
}
公共类AtopLineChart扩展线形图
{
/**
*@param xAxis
*@param yAxis
*/
公共ATOLINECHART(最终分类为yAxis,最终编号为yAxis)
{
超级(xAxis,yAxis);
}
/*(非Javadoc)
*@see javafx.scene.chart.LineChart#updateLegend()
*/
@凌驾
受保护的void updateGend()
{
最终HBox图例=新HBox();
图例.setVisible(真);
对于(最终的javafx.scene.chart.XYChart.Series系列:getData())
{
最终复选框cb=新复选框(series.getName());
cb.setUserData(系列);
cb.已选择(正确);
cb.addEventHandler(ActionEvent.ACTION,e->
{
最终复选框=(复选框)e.getSource();
@抑制警告(“未选中”)
最终系列s=(系列)box.getUserData();
s、 getNode().setVisible(box.isSelected());
});
legend.getChildren().add(cb);
}
设置图例(图例);
}
}
我将把它作为一个练习留给读者,让图例更具可读性,例如,每个复选框周围都有边框,并将系列的颜色绑定到在系列复选框中显示该颜色的内容上

还有一件事,您可能需要检查getLegendSide()方法以确定图例使用哪种布局容器,即HBox表示顶部和底部,而VBOX表示左侧和右侧。您可以选择。

来自类似案例

此解决方案利用Java中的流,直接修改
Legend
对象

但是,不推荐使用此选项,因此不建议使用此选项

由于您已经在处理
图例
,因此可以使用其 项目,删除不需要的项目,因此图例仅显示两个项目 项目

使用streams,您可以将前两项标记为“有效”/“无效” 其他的都是“移除”,例如,最后你只是移除 这些是最后的项目

private void updateStyleSheet() {
    Legend legend = (Legend)lineChart.lookup(".chart-legend");
    AtomicInteger count = new AtomicInteger();
    legend.getItems().forEach(item->{
        if(count.get()==0){
            item.setText("Valid");
        } else if(count.get()==1){
            item.setText("Invalid");
        } else {
            item.setText("Remove");
        }
        count.getAndIncrement();
    });
    legend.getItems().removeIf(item->item.getText().equals("Remove"));

    ...
}

谢谢!这就是我害怕的。:)嘿@jewelsea,如果你不介意的话,还有一个问题。我已经创建了自己的自定义窗格来显示自定义图例。它看起来几乎和原作一模一样。我唯一的问题是,当只有几个系列时,它会缩小宽度。图表和图例位于对话框的VBox中,图例位于VBox底部的堆栈窗格中。高度会适当变化。你还有什么智慧的话可以帮我吗?是的,但是请提出一个新的问题,并将你当前的代码作为一个答案加入其中。再次感谢。我能够在我的图例上使用
setPrefColumns()
方法克服我的挑战,该图例是
TilePane
的一个实例。您可以更改标准图例,请参见下面的示例代码。getChildren()方法对图例类不可见。方法getchildren()在父类中受保护,并按区域等扩展到Legend类。此外,我们无法从图例中删除元素。getChildrenUnmodifiable()@Kishore运行在Java 8.0.25上,图例的类层次结构是:对象-->节点-->父-->区域-->窗格-->平铺-->图例。窗格中有一个公共getChildren。@Kishore似乎正是这样做的:
public class AtopLineChart<X, Y> extends LineChart<String, Number>
{

   /**
    * @param xAxis
    * @param yAxis
    */
   public AtopLineChart(final CategoryAxis xAxis, final NumberAxis yAxis)
   {
      super(xAxis, yAxis);
   }

   /* (non-Javadoc)
    * @see javafx.scene.chart.LineChart#updateLegend()
    */
   @Override
   protected void updateLegend()
   {
      final HBox legend = new HBox();
      legend.setVisible(true);
      for (final javafx.scene.chart.XYChart.Series<String, Number> series : getData())
      {
         final CheckBox cb = new CheckBox(series.getName());
         cb.setUserData(series);
         cb.setSelected(true);
         cb.addEventHandler(ActionEvent.ACTION, e ->
         {
            final CheckBox box = (CheckBox) e.getSource();
            @SuppressWarnings("unchecked")
            final Series<String, Number> s = (Series<String, Number>) box.getUserData();
            s.getNode().setVisible(box.isSelected());
         });
         legend.getChildren().add(cb);
      }
      setLegend(legend);
   }
}
private void updateStyleSheet() {
    Legend legend = (Legend)lineChart.lookup(".chart-legend");
    AtomicInteger count = new AtomicInteger();
    legend.getItems().forEach(item->{
        if(count.get()==0){
            item.setText("Valid");
        } else if(count.get()==1){
            item.setText("Invalid");
        } else {
            item.setText("Remove");
        }
        count.getAndIncrement();
    });
    legend.getItems().removeIf(item->item.getText().equals("Remove"));

    ...
}