【问题标题】:JavaFX Use Chart Legend to toggle show/hide Series possible?JavaFX 使用图表图例切换显示/隐藏系列可能吗?
【发布时间】:2017-07-06 19:01:47
【问题描述】:

是否可以使用图表的图例来切换显示/隐藏系列?

我得到了一个带有图例的LineChartSeries 太多,所以你不能很好地读出信息。我想知道是否有可能使用图例来切换系列以显示/隐藏?

我的Series 的大多数名字都很长,如果它们在图例中写两次看起来很奇怪,所以你知道哪个颜色属于哪个Series,第二次除了CheckBox切换它们。

Edit1:也许我不清楚,即使没有内置函数,我也可以使用一些输入来了解解决方法的外观,因为我想不出任何东西。

【问题讨论】:

  • 据我所知,没有内置的方法,但我有一个可行的迂回解决方案,我看看能不能把它挖出来贴出来。
  • @sillyfly 太棒了

标签: java javafx charts legend


【解决方案1】:

这是我解决这个问题的方法 - 我不知道有任何更简单的内置解决方案

LineChart<Number, Number> chart;

for (Node n : chart.getChildrenUnmodifiable()) {
    if (n instanceof Legend) {
        Legend l = (Legend) n;
        for (Legend.LegendItem li : l.getItems()) {
            for (XYChart.Series<Number, Number> s : chart.getData()) {
                if (s.getName().equals(li.getText())) {
                    li.getSymbol().setCursor(Cursor.HAND); // Hint user that legend symbol is clickable
                    li.getSymbol().setOnMouseClicked(me -> {
                        if (me.getButton() == MouseButton.PRIMARY) {
                            s.getNode().setVisible(!s.getNode().isVisible()); // Toggle visibility of line
                            for (XYChart.Data<Number, Number> d : s.getData()) {
                                if (d.getNode() != null) {
                                    d.getNode().setVisible(s.getNode().isVisible()); // Toggle visibility of every node in the series
                                }
                            }
                        }
                    });
                    break;
                }
            }
        }
    }
}

您需要在图表上运行一次此代码(在此示例中为LineChart,但您可以将其调整到任何其他图表)。我找到Legend 孩子,然后遍历它的所有项目。我根据名称将图例项目与正确的系列匹配 - 根据我的经验,它们总是匹配的,我找不到更好的方法来匹配它们。然后只需将正确的事件处理程序添加到该特定图例项即可。

【讨论】:

    【解决方案2】:

    作为参考,类似的方法适用于 JavaFX 中的 JFreeChart,如 here 所示。改编自此example,下面的变体在ChartViewer 中添加了ChartMouseListenerFX。单击一个系列或其图例项,使系列不可见;单击其他任何地方以恢复它。

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.axis.NumberAxis;
    import org.jfree.chart.entity.ChartEntity;
    import org.jfree.chart.entity.LegendItemEntity;
    import org.jfree.chart.entity.XYItemEntity;
    import org.jfree.chart.fx.ChartViewer;
    import org.jfree.chart.fx.interaction.ChartMouseEventFX;
    import org.jfree.chart.fx.interaction.ChartMouseListenerFX;
    import org.jfree.chart.labels.StandardXYToolTipGenerator;
    import org.jfree.chart.plot.XYPlot;
    import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
    import org.jfree.data.xy.XYSeries;
    import org.jfree.data.xy.XYSeriesCollection;
    
    /**
     * @see https://stackoverflow.com/a/44967809/230513
     * @see https://stackoverflow.com/a/43286042/230513
     */
    public class VisibleTest extends Application {
    
        @Override
        public void start(Stage stage) {
            XYSeriesCollection dataset = new XYSeriesCollection();
            for (int i = 0; i < 3; i++) {
                XYSeries series = new XYSeries("value" + i);
                for (double t = 0; t < 2 * Math.PI; t += 0.5) {
                    series.add(t, Math.sin(t) + i);
                }
                dataset.addSeries(series);
            }
            NumberAxis xAxis = new NumberAxis("domain");
            NumberAxis yAxis = new NumberAxis("range");
            XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, true);
            renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
            XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
            JFreeChart chart = new JFreeChart("Test", plot);
            ChartViewer viewer = new ChartViewer(chart);
            viewer.addChartMouseListener(new ChartMouseListenerFX() {
                @Override
                public void chartMouseClicked(ChartMouseEventFX e) {
                    ChartEntity ce = e.getEntity();
                    if (ce instanceof XYItemEntity) {
                        XYItemEntity item = (XYItemEntity) ce;
                        renderer.setSeriesVisible(item.getSeriesIndex(), false);
                    } else if (ce instanceof LegendItemEntity) {
                        LegendItemEntity item = (LegendItemEntity) ce;
                        Comparable key = item.getSeriesKey();
                        renderer.setSeriesVisible(dataset.getSeriesIndex(key), false);
                    } else {
                        for (int i = 0; i < dataset.getSeriesCount(); i++) {
                            renderer.setSeriesVisible(i, true);
                        }
                    }
                }
    
                @Override
                public void chartMouseMoved(ChartMouseEventFX e) {}
            });
            stage.setScene(new Scene(viewer));
            stage.setTitle("JFreeChartFX");
            stage.setWidth(640);
            stage.setHeight(480);
            stage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 也是一个选项,谢谢,但第一个选项与我的程序的其余部分结合使用效果更好。
    • @Daniel:我同意;我主要只是想举一个将现有 JFreeChart 移植到 JavaFX 的示例。
    【解决方案3】:

    感谢@sillyfly 的回答。我能够将其移植到 Kotlin。它以forEachfilter 表示法简洁明了。

    (Kotlin-folk,请让我知道任何改进,谢谢)。

            lineChart.childrenUnmodifiable.forEach { if (it is Legend) {
                it.items.forEach {
                    val li = it
                    lineChart.data.filter { it.name == li.text }.forEach {
                        li.symbol.cursor = Cursor.HAND
                        val s = it
                        li.symbol.setOnMouseClicked { if (it.button == MouseButton.PRIMARY) {
                            s.node.isVisible = !s.node.isVisible
                            s.data.forEach { it.node.isVisible = !it.node.isVisible }
                        }}
                    }
                }
            }
        }
    

    【讨论】:

      【解决方案4】:

      有点离题,但如果您已切换到 TornadoFX(基于 JavaFX),评论太长了,并且很有用。隐藏系列可以通过添加扩展在幕后实现

      fun XYChart<Number, Number>.showExtra(on: Boolean) {
      
          for (s in getData()) {
      
              s.getNode().setVisible(on)
      
              for (d in s.getData()) {
      
                  if (d.getNode() != null) {
      
                      d.getNode().setVisible(on)
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        此代码适用于新版本的 JavaFX 和 Kotlin。也解决了导入com.sun.javafx.charts包的问题:

        val items: Set<Node> = lineChart.lookupAll("Label.chart-legend-item")
            items.forEach { label ->
                if(label is Label)
                    label.setOnMouseClicked {
                        lineChart.data.forEach {
                            if(it.name == label.text) {
                                it.node.isVisible = !it.node.isVisible
                            }
                        }
                    }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多