JavaFX使用图表图例切换显示/隐藏系列可能吗?

时间:2017-07-06 19:01:48

标签: java javafx charts legend

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

我有一个带有图例的influencedCharactersScoring,而LineChart太多了,所以您无法很好地读出这些信息。我想知道是否有可能使用图例来切换系列以显示/隐藏?

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

Edit1:也许我不清楚,即使没有内置功能,我也可以使用一些输入来查看workaroud的样子,因为我无法想出任何东西。

4 个答案:

答案 0 :(得分:6)

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

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子项,然后遍历其所有项目。我根据名称将图例项目与正确的系列相匹配 - 根据我的经验,他们总是匹配,我找不到更好的方法来匹配它们。然后,只需将正确的事件处理程序添加到该特定图例项即可。

答案 1 :(得分:3)

作为参考,类似的方法适用于JavaFX中的JFreeChart,如here所示。根据此example进行调整,下面的变体会向ChartMouseListenerFX添加ChartViewer。单击系列或其图例项以使系列不可见;点击其他任何地方恢复它。

image

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

答案 2 :(得分:1)

感谢@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 }
                    }}
                }
            }
        }
    }

答案 3 :(得分:0)

有点题外话,但是太长了,无法发表评论,如果您切换到基于JavaFX的TornadoFX,就很有用。通过添加扩展名,可以在后台隐藏该系列

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)
            }
        }
    }
}
相关问题