Java – How to properly add MouseHandler to my JFreeChart-FX to drag the chart from left to right

How to properly add MouseHandler to my JFreeChart-FX to drag the chart from left to right… here is a solution to the problem.

How to properly add MouseHandler to my JFreeChart-FX to drag the chart from left to right

I managed to create a candlestick chart using JFreeChart-FX and display it using the fxgraphics2d API. But I’m very confused about how to enable any interaction with my charts and need some help.

I would appreciate any help in the right direction.

I start with this for example, Build my initial chart and change it so it uses my data. Then I use a custom canvas that leverages fxgraphics2d to make JPanel components accessible to the application as nodes for my JavaFX.
So I know there’s a specific PanHandlerFX class , but I can’t take advantage of it. According to my research (e.g. here), I need to convert The PanHandlerFX class is added to my list of available MouseHandlers in ChartCanvas. But my canvas doesn’t offer anything like availableMouseHandlers. I feel lost now because there are few tutorials and information on JFree-FX charts, and the documentation is not helpful to me either.

Here is my custom Canvas class:

import javafx.scene.canvas.Canvas;
import org.jfree.chart.JFreeChart;
import org.jfree.fx.FXGraphics2D;

import java.awt.geom.Rectangle2D;

public class ChartCanvas extends Canvas {

JFreeChart chart;
    private FXGraphics2D graphics2D;

public ChartCanvas(JFreeChart chart) {
        this.chart = chart;
        this.graphics2D = new FXGraphics2D(getGraphicsContext2D());
         Redraw canvas when size changes.
        widthProperty().addListener(e -> draw());
        heightProperty().addListener(e -> draw());

}

private void draw() {
        double width = getWidth();
        double height = getHeight();
        getGraphicsContext2D().clearRect(0, 0, width, height);
        this.chart.draw(this.graphics2D, new Rectangle2D.Double(0, 0, width, height));
        (this.graphics2D,, new Rectangle2D.Double(0, 0, width, height));
    }
}

Here is my custom JFreeChart:

import javafx.collections.ObservableList;
import org.ezstrats.model.chartData.Candlestick;
import org.ezstrats.model.chartData.Exchange;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

public class JFreeCandlestickChart extends JPanel {

private static final DateFormat READABLE_TIME_FORMAT = new SimpleDateFormat("kk:mm:ss");

private OHLCSeries ohlcSeries;
    private TimeSeries volumeSeries;
    private JFreeChart candlestickChart;

public JFreeCandlestickChart(String title) {
        ObservableList<Candlestick> candlesticks = Exchange.getCandlesticks();
         Create new chart
        candlestickChart = createChart(title, candlesticks);
         Create new chart panel
        final ChartPanel chartPanel = new ChartPanel(candlestickChart);
        chartPanel.setPreferredSize(new Dimension(832, 468));
        chartPanel.getChart().getXYPlot().getDomainAxis().setAutoRange(false);
        chartPanel.getChart().getXYPlot().getDomainAxis().setLowerBound(candlesticks.get(candlesticks.size() - 300).getTimestampOpen());
        chartPanel.getChart().getXYPlot().getDomainAxis().setUpperBound(candlesticks.get(candlesticks.size() - 1).getTimestampOpen());
         Enable zooming - not workign?! ...
        chartPanel.setMouseZoomable(true);
        chartPanel.setMouseWheelEnabled(true);
        chartPanel.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                 process before
                super.mouseDragged(e);
                chartPanel.getChart().getXYPlot().getDomainAxis().configure();
                 process after
            }
        });

add(chartPanel, BorderLayout.CENTER);
    }

public JFreeChart createChart(String title, ObservableList<Candlestick> candlesticks){

/**
         * 1st:
         * Creating candlestick subplot
         */
         Create OHLCSeriesCollection as a price dataset for candlestick chart
        OHLCSeriesCollection candlestickDataset = new OHLCSeriesCollection();
        ohlcSeries = new OHLCSeries("Price");
        candlestickDataset.addSeries(ohlcSeries);

 Create candlestick chart priceAxis
        NumberAxis priceAxis = new NumberAxis("Price");
        priceAxis.setAutoRangeIncludesZero(false);

 Create candlestick chart renderer
        CandlestickRenderer candlestickRenderer = new CandlestickRenderer(CandlestickRenderer.WIDTHMETHOD_AVERAGE,
                false,
                new HighLowItemLabelGenerator(new SimpleDateFormat("kk:mm"), new DecimalFormat("0.00000000")));

 Create candlestickSubplot
        XYPlot candlestickSubplot = new XYPlot(candlestickDataset, null, priceAxis, candlestickRenderer);
        candlestickSubplot.setBackgroundPaint(Color.white);

/**
         * 2nd:
         * Creating volume subplot
         */
         creates TimeSeriesCollection as a volume dataset for volume chart
        TimeSeriesCollection volumeDataset = new TimeSeriesCollection();
        volumeSeries = new TimeSeries("Volume");
        volumeDataset.addSeries(volumeSeries);

 Create volume chart volumeAxis
        NumberAxis volumeAxis = new NumberAxis("Volume");
        volumeAxis.setAutoRangeIncludesZero(true);

 Set to no decimal
        volumeAxis.setNumberFormatOverride(new DecimalFormat("0"));

 Create volume chart renderer
        XYBarRenderer timeRenderer = new XYBarRenderer();
        timeRenderer.setShadowVisible(false);
        timeRenderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator("Volume--> Time={1} Size={2}",
                new SimpleDateFormat("kk:mm"), new DecimalFormat("0")));

 Create volumeSubplot
        XYPlot volumeSubplot = new XYPlot(volumeDataset, null, volumeAxis, timeRenderer);
        volumeSubplot.setBackgroundPaint(Color.white);

/**
         * 3rd:
         * Adding Candles to this chart
         **/
         for (Candlestick candle: candlesticks){
            addCandleToChart(candle.getTimestampOpen(),
                    candle.getPriceOpen(),
                    candle.getPriceHigh(),
                    candle.getPriceLow(),
                    candle.getPriceClose(),
                    candle.getVolumeQuote());
        }

/**
         * 4th:
         * Create chart main plot with two subplots (candlestickSubplot,
         * volumeSubplot) and one common dateAxis
         */
         Creating charts common dateAxis
        DateAxis dateAxis = new DateAxis("Time");
        dateAxis.setDateFormatOverride(new SimpleDateFormat("dd.mm.yy kk:mm"));
        dateAxis.setRange();
         reduce the default left/right margin from 0.05 to 0.02
        dateAxis.setLowerMargin(0.02);
        dateAxis.setUpperMargin(0.02);
        dateAxis.setLabelAngle(0);

 Create mainPlot
        CombinedDomainXYPlot mainPlot = new CombinedDomainXYPlot(dateAxis);
        mainPlot.setGap(10.0);
        mainPlot.add(candlestickSubplot, 4);
        mainPlot.add(volumeSubplot, 1);
        mainPlot.setOrientation(PlotOrientation.VERTICAL);
        mainPlot.setDomainPannable(true);

JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, mainPlot, false);
        chart.removeLegend();

 Einbetten in JScrollPaenl??? um Scrollen zu ermöglichen...
        ChartPanel chartPanel = new ChartPanel(chart);

return chart;
    }

/**
     * Fill series with data.
     *
     * @param c opentime
     * @param o openprice
     * @param h highprice
     * @param l lowprice
     * @param c closeprice
     * @param v volume
     */
    private void addCandleToChart(long time, double o, double h, double l, double c, double v) {
         Add bar to the data. Let's repeat the same bar
        FixedMillisecond t = new FixedMillisecond(time);
        READABLE_TIME_FORMAT.parse(String.valueOf(time)));
        ohlcSeries.add(t, o, h, l, c);
        volumeSeries.add(t, v);
    }

public void setOhlcSeries(OHLCSeries ohlcSeries) {
        this.ohlcSeries = ohlcSeries;
    }
    public void setVolumeSeries(TimeSeries volumeSeries) {
        this.volumeSeries = volumeSeries;
    }
    public OHLCSeries getOhlcSeries() {
        return ohlcSeries;
    }
    public TimeSeries getVolumeSeries() {
        return volumeSeries;
    }
    public JFreeChart getCandlestickChart() {
        return candlestickChart;
    }
}

This is how I print the chart (main.class:

    // Switching Views
    public void drawNewChart(JFreeChart newChart){
        centerChart.getChildren().removeAll();

ChartCanvas chartCanvas = new ChartCanvas(newChart);

centerChart.getChildren().add(chartCanvas);
        chartCanvas.widthProperty().bind(centerChart.widthProperty());
        chartCanvas.heightProperty().bind(centerChart.heightProperty());

}

Solution

As shown in the figure< a href="https://stackoverflow.com/a/44967809/230513" rel="noreferrer noopener nofollow"> here to construct one ChartViewer Use your JFreeChart to create interactive charts. The closed < of the viewer a href="https://github.com/jfree/jfreechart-fx/blob/master/src/main/java/org/jfree/chart/fx/ChartCanvas.java" rel="noreferrer noopener nofollow" > ChartCanvas will manage PanHandlerFX for you. As a concrete example, add the following line to example and drag as described here :

plot.setDomainPannable(true);

Original:

original

After dragging to the right:

drag right

By the way, you may find JavaFX Demos helpful in this regard.

Related Problems and Solutions