【问题标题】:Remove JavaFX 2 LineChart Legend Items删除 JavaFX 2 LineChart 图例项
【发布时间】:2014-01-25 19:32:56
【问题描述】:

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

我已经能够成功克服几个技术问题,例如图表为每个新系列分配新颜色,图表线符号颜色与系列颜色不匹配等。现在一切都很好,除了每次我向图表添加一个新系列时,它都会向图例添加一个项目。

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

【问题讨论】:

    标签: charts javafx-2


    【解决方案1】:

    Don't show the legend:

    chart.setLegendVisible(false);
    

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

    【讨论】:

    • 谢谢!这就是我害怕的。 :)
    • 嘿@jewelsea,还有一个问题,如果你不介意的话。我创建了自己的自定义窗格来显示我的自定义图例。它看起来几乎和原版一模一样。我唯一遇到的问题是当只有几个系列时让它缩小它的宽度。图表和图例位于对话框的 VBox 中,图例位于 VBox 底部的 StackPane 中。高度适当变化。你还有什么智慧之言可以帮助我吗?
    • 是的,但是提出一个新问题并将您当前的代码作为sscce 包含在其中。
    • 再次感谢。我能够在我的图例中使用setPrefColumns() 方法克服我的挑战,这是TilePane 的一个实例。
    • 您可以更改标准图例,示例代码见下文。
    【解决方案2】:

    在多次尝试实施各种建议失败后,我发现允许用户在 JavaFx 图表(或其子类)中显示/隐藏数据系列的最佳方法是扩展您要使用的图表类并覆盖它的 updateLegend() 方法。

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

    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);
       }
    }
    

    我将把它作为练习留给读者,以使图例更具可读性,例如,每个复选框周围的边框并将系列的颜色绑定到系列复选框中显示该颜色的内容。

    另一件事,您可能需要检查 getLegendSide() 方法来决定为图例使用哪种布局容器,即 HBox 用于 TOP 和 BOTTOM 而 VBOX 用于 LEFT 和 RIGHT。您的选择。

    【讨论】:

      【解决方案3】:

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

      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;
      }
      

      使用相关图表调用它以检索图例节点:

      Legend legend = (Legend) findNode(chart, Legend.class.getName(), "chart-legend");
      

      然后您可以遍历子项并删除您不想显示的子项:

          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 还有一个lookup 函数,它“根据给定的 CSS 选择器查找此节点或第一个子节点”。并且行为类似于此答案中的findNode 函数。

      【讨论】:

      • getChildren() 方法对 Legend 类不可见。方法 getchildren() 在 Parent 类中受到保护,并由 Region 扩展,因此扩展到 Legend 类。此外,我们无法从 legend.getChildrenUnmodifiable() 中删除元素
      • @Kishore 针对 Java 8.0.25 运行,Legend 的类层次结构是:Object --> Node --> Parent --> Region --> Pane --> TilePane --> Legend。窗格有一个公共 getChildren。
      • @Kishore 似乎这就是使之成为可能的改变:hg.openjdk.java.net/openjfx/8u-dev/rt/rev/9823085ebf0e
      【解决方案4】:

      来自类似案例,https://stackoverflow.com/a/27819227/2341336

      该方案利用Java中的流,直接修改Legend对象。

      但是,这已被弃用,因此不推荐。

      由于您已经在处理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"));
      
          ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-14
        • 2012-08-03
        相关资源
        最近更新 更多