JavaFX 2.x使用类别axys放大XYChart

时间:2012-06-18 17:29:21

标签: charts zoom javafx-2

我有这个代码在XYChart LineChart上执行缩放<数字,数字>

public class Zoom extends Application {

BorderPane pane;
Rectangle rect;
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
SimpleDoubleProperty rectX = new SimpleDoubleProperty();
SimpleDoubleProperty rectY = new SimpleDoubleProperty();

double initXLowerBound = 0, initXUpperBound = 0, initYLowerBound = 0, initYUpperBound = 0;
@Override
public void start(Stage stage) {    
stage.setTitle("Lines plot");
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);

yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {

@Override
public String toString(Number object) {
    return String.format("%7.5f", object);
}
});

final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);

lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(true);

XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
series1.getData().add(new XYChart.Data(11, 0.530123));
series1.getData().add(new XYChart.Data(12, 0.53035));

pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);

initXLowerBound = ((NumberAxis) lineChart.getXAxis()).getLowerBound();
initXUpperBound = ((NumberAxis) lineChart.getXAxis()).getUpperBound();
initYLowerBound = ((NumberAxis) lineChart.getYAxis()).getLowerBound();
initYUpperBound = ((NumberAxis) lineChart.getYAxis()).getUpperBound();

stage.setScene(scene);        

scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);

rect = new Rectangle();
rect.setFill(Color.web("blue", 0.1));
rect.setStroke(Color.BLUE);
rect.setStrokeDashOffset(50);

rect.widthProperty().bind(rectX.subtract(rectinitX));
rect.heightProperty().bind(rectY.subtract(rectinitY));
pane.getChildren().add(rect);

stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {

@Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getButton() == MouseButton.PRIMARY)
{
        if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
            rect.setX(mouseEvent.getX());
            rect.setY(mouseEvent.getY());
            rectinitX.set(mouseEvent.getX());
            rectinitY.set(mouseEvent.getY());
        } else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
            rectX.set(mouseEvent.getX());
            rectY.set(mouseEvent.getY());
        } else if (mouseEvent.getEventType() == MouseEvent.MOUSE_RELEASED) {

            if ((rectinitX.get() >= rectX.get())&&(rectinitY.get() >= rectY.get()))
            {
                LineChart<Number, Number> lineChart = (LineChart<Number, Number>)    pane.getCenter();

                ((NumberAxis) lineChart.getXAxis()).setLowerBound(initXLowerBound);
                ((NumberAxis) lineChart.getXAxis()).setUpperBound(initXUpperBound);

                ((NumberAxis) lineChart.getYAxis()).setLowerBound(initYLowerBound);
                ((NumberAxis) lineChart.getYAxis()).setUpperBound(initYUpperBound);

            }
            else
            {

                double Tgap = 0;
                double newLowerBound, newUpperBound, axisShift;
                double xScaleFactor, yScaleFactor;
                double xaxisShift, yaxisShift;

                LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();

                // Zoom in Y-axis by changing bound range.            
                NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
                Tgap = yAxis.getHeight()/(yAxis.getUpperBound() - yAxis.getLowerBound());
                axisShift = getSceneShiftY(yAxis);
                yaxisShift = axisShift;

                newUpperBound = yAxis.getUpperBound() - ((rectinitY.get() - axisShift) / Tgap);
                newLowerBound = yAxis.getUpperBound() - (( rectY.get() - axisShift) / Tgap);


                if (newUpperBound > yAxis.getUpperBound())
                    newUpperBound = yAxis.getUpperBound();

                yScaleFactor = (yAxis.getUpperBound() - yAxis.getLowerBound())/(newUpperBound - newLowerBound);
                yAxis.setLowerBound(newLowerBound);
                yAxis.setUpperBound(newUpperBound);


                NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();

                Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());            
                axisShift = getSceneShiftX(xAxis);                        
                xaxisShift = axisShift;                                                                                
                newLowerBound = ((rectinitX.get() - axisShift) / Tgap) + xAxis.getLowerBound();
                newUpperBound = ((rectX.get() - axisShift) / Tgap) + xAxis.getLowerBound();                

                if (newUpperBound > xAxis.getUpperBound())
                    newUpperBound = xAxis.getUpperBound();

                xScaleFactor = (xAxis.getUpperBound() - xAxis.getLowerBound())/(newUpperBound - newLowerBound);
                xAxis.setLowerBound( newLowerBound );
                xAxis.setUpperBound( newUpperBound );                      


            }
            // Hide the rectangle
            rectX.set(0);
            rectY.set(0);
        }
}

 }
 };
private static double getSceneShiftX(Node node) {
double shift = 0;
do { 
shift += node.getLayoutX(); 
node = node.getParent();
} while (node != null);
return shift;
}
private static double getSceneShiftY(Node node) {
double shift = 0;
do { 
shift += node.getLayoutY(); 
node = node.getParent();
} while (node != null);
return shift;
}

public static void main(String[] args) {
launch(args);
}        
}

我想通过使用&lt;来获得相同的缩放结果。字符串,数字&gt;因为我想在x axys上使用日期和时间作为字符串

1 个答案:

答案 0 :(得分:3)

好的,所以这是我使用的应用程序,它不是很整洁,但它会完成这项工作:

首先我使用了Javafx的Date轴类:

https://github.com/dukke/FXCharts/blob/master/DateAxis.java

然后我添加了另一个类,这个必需,但是我更容易使用它,所以:

package linechartwithdateaxis;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;

/**
 *
 * @author yschellekens
 */
      class HoveredThresholdNode extends StackPane {

      DateFormat df = new SimpleDateFormat("MM/dd/yyyy");

    HoveredThresholdNode(Date date,  double value) {
      setPrefSize(5, 5);



      final Label label = createDataThresholdLabel(date, value);

      setOnMouseEntered(new EventHandler<MouseEvent>() {
        @Override public void handle(MouseEvent mouseEvent) {
          getChildren().setAll(label);
          setCursor(Cursor.NONE);
          toFront();
        }
      });

       setOnMouseClicked(new EventHandler<MouseEvent>() {
        @Override public void handle(MouseEvent mouseEvent) {

                 }
     });


      setOnMouseDragEntered(new EventHandler<MouseEvent>() {
        @Override public void handle(MouseEvent mouseEvent) {
         getChildren().setAll(label);

        }
     });


      setOnMouseExited(new EventHandler<MouseEvent>() {
        @Override public void handle(MouseEvent mouseEvent) {
          getChildren().clear();
          setCursor(Cursor.CROSSHAIR);
          toBack();
        }
      });
    }

    private Label createDataThresholdLabel(Date date, double value) {
      final Label label = new Label( java.lang.Math.round(value)+", On " +df.format(date));
              label.setStyle("-fx-font-size: 20; -fx-font-weight: bold; -fx-background-color: transparent; -fx-color:transparent;");

        label.setTextFill(Color.BLACK);

      label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
      return label;
    }
  }

还有实际的代码,它的丑陋却有效:

package linechartwithdateaxis;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

/**
 *
 * @author yschellekens
 */
public class LineChartWithDateAxis extends Application {  

    DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
    final DateAxis dateAxis = new DateAxis(new Date(2014-1900,11,10),new Date(2014-1900,11,25));
    final NumberAxis yAxis = new NumberAxis(0,20,1);
    Date [] xData ;
    ObservableList<XYChart.Data<Date, Number>> series1Data;
     ObservableList<XYChart.Series<Date, Number>> series;
    private double[] anArray;
    final LineChart<Date, Number> lineChart = new LineChart<>(dateAxis, yAxis);
    private int i;


    @Override
    public void start(Stage primaryStage) {     

        xData = new Date[9];
        for (i = 0; i < xData.length  ; i++) {xData[i]= new Date(2014-1900,11,i+15);  }
         dateAxis.setLowerBound(xData[0]);
         dateAxis.setUpperBound(xData[xData.length-1]);

        anArray = new double[9];
        anArray[0] = 2;
        anArray[1] = 19;
        anArray[2] = 3;
        anArray[3] = 5;
        anArray[4] = 12;
        anArray[5] = 6;
        anArray[6] = 2;
        anArray[7] = 12;
        anArray[8] = 6;


           XYChart.Series Dates = new XYChart.Series( "Dates", plotWithVisableLabeles(xData,anArray)  );

            lineChart.getData().add(Dates);

            final  BorderPane chartContainer = new  BorderPane();
            final Button zoomButton = new Button("Zoom");
            chartContainer.setCenter(lineChart);
             final Button unZoomButton = new Button("Un Zoom");
            chartContainer.setBottom(zoomButton);
            chartContainer.setRight(unZoomButton);
           final Rectangle zoomRect = new Rectangle();
        zoomRect.setManaged(false);
        zoomRect.setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5));


        chartContainer.getChildren().add(zoomRect);
        setUpZooming(zoomRect, lineChart);

        StackPane root = new StackPane();
        root.getChildren().add(chartContainer);

        Scene scene = new Scene(root, 600, 600);


       //     final Button resetButton = new Button("Reset");
              final BooleanBinding disableControls = 
                 zoomRect.widthProperty().lessThan(5)
                .or(zoomRect.heightProperty().lessThan(5));
                zoomButton.disableProperty().bind(disableControls);

        zoomButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                doZoom(zoomRect);
            }
        });

            unZoomButton.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
               dateAxis.setLowerBound(new GregorianCalendar(2014, 11, 10).getTime());
               dateAxis.setUpperBound(new GregorianCalendar(2014, 11, 25).getTime());
                   }
        });  

             primaryStage.setTitle("zoomable Line chart with Date axis");
        primaryStage.setScene(scene);
        primaryStage.show();

    }

     private void setUpZooming(final Rectangle rect, final Node zoomingNode) {
        final ObjectProperty<Point2D> mouseAnchor = new SimpleObjectProperty<>();
        zoomingNode.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                mouseAnchor.set(new Point2D(event.getX(), event.getY()));
                rect.setWidth(0);
                rect.setHeight(0);
            }
        });
        zoomingNode.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                double x = event.getX();
                double y = event.getY();
                rect.setX(Math.min(x, mouseAnchor.get().getX()));
                rect.setY(Math.min(y, mouseAnchor.get().getY()));
                rect.setWidth(Math.abs(x - mouseAnchor.get().getX()));
                rect.setHeight(Math.abs(y - mouseAnchor.get().getY()));
            }
        });
    }
        private void doZoom(Rectangle zoomRect) {
        Date leftBorder = dateAxis.getValueForDisplay(zoomRect.getX());        
        Date RightBorder = dateAxis.getValueForDisplay(zoomRect.getX() + zoomRect.getWidth());

       dateAxis.setLowerBound(leftBorder);
       dateAxis.setUpperBound(RightBorder);


        zoomRect.setWidth(0);
        zoomRect.setHeight(0); 
    }

        public ObservableList<XYChart.Data<Date, Double>> plotWithVisableLabeles(Date[] x ,double[] y) {
    final ObservableList<XYChart.Data<Date, Double>> dataset = FXCollections.observableArrayList();
     i = 0;
    while (i < y.length) {
      final XYChart.Data< Date,  Double> data = new XYChart.Data<>(x[i], y[i]);
      final StackPane node =  new HoveredThresholdNode(x[i],y[i]);
      node.setStyle("-fx-background-color: linear-gradient(black,white);");
     data.setNode(node);     
      dataset.add(data);
      i++;
    }
      return dataset;
  }

    public static void main(String[] args) {
        launch(args);
    }
}

我添加了即时使用的日期轴版本(它的旧版本,由于SO限制我删除了所有评论)

package linechartwithdateaxis;


import com.sun.javafx.charts.ChartLayoutAnimator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.SimpleLongProperty;
import javafx.scene.chart.Axis;
import javafx.util.Duration;
import javafx.util.StringConverter;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;


public final class DateAxis extends Axis<Date> {


    private final LongProperty currentLowerBound = new SimpleLongProperty(this, "currentLowerBound");

    private final LongProperty currentUpperBound = new SimpleLongProperty(this, "currentUpperBound");

    private final ObjectProperty<StringConverter<Date>> tickLabelFormatter = new ObjectPropertyBase<StringConverter<Date>>() {
        @Override
        protected void invalidated() {
            if (!isAutoRanging()) {
                invalidateRange();
                requestAxisLayout();
            }
        }

        @Override
        public Object getBean() {
            return DateAxis.this;
        }

        @Override
        public String getName() {
            return "tickLabelFormatter";
        }
    };


    private Date minDate, maxDate;

    private ObjectProperty<Date> lowerBound = new ObjectPropertyBase<Date>() {
        @Override
        protected void invalidated() {
            if (!isAutoRanging()) {
                invalidateRange();
                requestAxisLayout();
            }
        }

        @Override
        public Object getBean() {
            return DateAxis.this;
        }

        @Override
        public String getName() {
            return "lowerBound";
        }
    };

    private ObjectProperty<Date> upperBound = new ObjectPropertyBase<Date>() {
        @Override
        protected void invalidated() {
            if (!isAutoRanging()) {
                invalidateRange();
                requestAxisLayout();
            }
        }

        @Override
        public Object getBean() {
            return DateAxis.this;
        }

        @Override
        public String getName() {
            return "upperBound";
        }
    };

    private ChartLayoutAnimator animator = new ChartLayoutAnimator(this);

    private Object currentAnimationID;

    private DateAxis.Interval actualInterval = DateAxis.Interval.DECADE;

    public DateAxis() {
    }


    public DateAxis(Date lowerBound, Date upperBound) {
        this();
        setAutoRanging(false);
        setLowerBound(lowerBound);
        setUpperBound(upperBound);
    }

    public DateAxis(String axisLabel, Date lowerBound, Date upperBound) {
        this(lowerBound, upperBound);
        setLabel(axisLabel);
    }

    @Override
    public void invalidateRange(List<Date> list) {
        super.invalidateRange(list);

        Collections.sort(list);
        if (list.isEmpty()) {
            minDate = maxDate = new Date();
        } else if (list.size() == 1) {
            minDate = maxDate = list.get(0);
        } else if (list.size() > 1) {
            minDate = list.get(0);
            maxDate = list.get(list.size() - 1);
        }
    }

    @Override
    protected Object autoRange(double length) {
        if (isAutoRanging()) {
            return new Object[]{minDate, maxDate};
        } else {
            if (getLowerBound() == null || getUpperBound() == null) {
                throw new IllegalArgumentException("If autoRanging is false, a lower and upper bound must be set.");
            }
            return getRange();
        }
    }

    @Override
    protected void setRange(Object range, boolean animating) {
        Object[] r = (Object[]) range;
        Date oldLowerBound = getLowerBound();
        Date oldUpperBound = getUpperBound();
        Date lower = (Date) r[0];
        Date upper = (Date) r[1];
        setLowerBound(lower);
        setUpperBound(upper);

        if (animating) {


            animator.stop(currentAnimationID);
            currentAnimationID = animator.animate(
                    new KeyFrame(Duration.ZERO,
                            new KeyValue(currentLowerBound, oldLowerBound.getTime()),
                            new KeyValue(currentUpperBound, oldUpperBound.getTime())
                    ),
                    new KeyFrame(Duration.millis(700),
                            new KeyValue(currentLowerBound, lower.getTime()),
                            new KeyValue(currentUpperBound, upper.getTime())
                    )
            );

        } else {
            currentLowerBound.set(getLowerBound().getTime());
            currentUpperBound.set(getUpperBound().getTime());
        }
    }

    @Override
    protected Object getRange() {
        return new Object[]{getLowerBound(), getUpperBound()};
    }

    @Override
    public double getZeroPosition() {
        return 0;
    }

    @Override
    public double getDisplayPosition(Date date) {
        final double length = getSide().isHorizontal() ? getWidth() : getHeight();
        double diff = currentUpperBound.get() - currentLowerBound.get();
        double range = length - getZeroPosition();
        double d = (date.getTime() - currentLowerBound.get()) / diff;
        if (getSide().isVertical()) {
            return getHeight() - d * range + getZeroPosition();
        } else {
            return d * range + getZeroPosition();
        }
    }

    @Override
    public Date getValueForDisplay(double displayPosition) {
        final double length = getSide().isHorizontal() ? getWidth() : getHeight();
      double diff = currentUpperBound.get() - currentLowerBound.get();
      double range = length - getZeroPosition();

        if (getSide().isVertical()) {
            return new Date((long) ((displayPosition - getZeroPosition() - getHeight()) / -range * diff + currentLowerBound.get()));
        } else {
            return new Date((long) ((displayPosition - getZeroPosition()) / range * diff + currentLowerBound.get()));
        }
    }

    @Override
    public boolean isValueOnAxis(Date date) {
        return date.getTime() > currentLowerBound.get() && date.getTime() < currentUpperBound.get();
    }

    @Override
    public double toNumericValue(Date date) {
        return date.getTime();
    }

    @Override
    public Date toRealValue(double v) {
        return new Date((long) v);
    }

    @Override
    protected List<Date> calculateTickValues(double v, Object range) {
        Object[] r = (Object[]) range;
        Date lower = (Date) r[0];
        Date upper = (Date) r[1];

        List<Date> dateList = new ArrayList<Date>();
        Calendar calendar = Calendar.getInstance();

        // The preferred gap which should be between two tick marks.
        double averageTickGap = 100;
        double averageTicks = v / averageTickGap;

        List<Date> previousDateList = new ArrayList<Date>();

        DateAxis.Interval previousInterval = DateAxis.Interval.values()[0];

        // Starting with the greatest interval, add one of each calendar unit.
        for (DateAxis.Interval interval : DateAxis.Interval.values()) {
            // Reset the calendar.
            calendar.setTime(lower);
            dateList.clear();
            previousDateList.clear();
            actualInterval = interval;

            while (calendar.getTime().getTime() <= upper.getTime()) {
                dateList.add(calendar.getTime());
                calendar.add(interval.interval, interval.amount);
            }

            if (dateList.size() > averageTicks) {
                calendar.setTime(lower);
                // Recheck if the previous interval is better suited.
                while (calendar.getTime().getTime() <= upper.getTime()) {
                    previousDateList.add(calendar.getTime());
                    calendar.add(previousInterval.interval, previousInterval.amount);
                }
                break;
            }

            previousInterval = interval;
        }
        if (previousDateList.size() - averageTicks > averageTicks - dateList.size()) {
            dateList = previousDateList;
            actualInterval = previousInterval;
        }

        // At last add the upper bound.
        dateList.add(upper);

        List<Date> evenDateList = makeDatesEven(dateList, calendar);
        if (evenDateList.size() > 2) {

            Date secondDate = evenDateList.get(1);
            Date thirdDate = evenDateList.get(2);
            Date lastDate = evenDateList.get(dateList.size() - 2);
            Date previousLastDate = evenDateList.get(dateList.size() - 3);


            if (secondDate.getTime() - lower.getTime() < (thirdDate.getTime() - secondDate.getTime()) / 2) {
                evenDateList.remove(secondDate);
            }

          if (upper.getTime() - lastDate.getTime() < (lastDate.getTime() - previousLastDate.getTime()) / 2) {
                evenDateList.remove(lastDate);
            }
        }

        return evenDateList;
    }

    @Override
    protected void layoutChildren() {
        if (!isAutoRanging()) {
            currentLowerBound.set(getLowerBound().getTime());
            currentUpperBound.set(getUpperBound().getTime());
        }
        super.layoutChildren();
    }

    @Override
    protected String getTickMarkLabel(Date date) {

        StringConverter<Date> converter = getTickLabelFormatter();
        if (converter != null) {
            return converter.toString(date);
        }

        DateFormat dateFormat;
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        if (actualInterval.interval == Calendar.YEAR && calendar.get(Calendar.MONTH) == 0 && calendar.get(Calendar.DATE) == 1) {
            dateFormat = new SimpleDateFormat("yyyy");
        } else if (actualInterval.interval == Calendar.MONTH && calendar.get(Calendar.DATE) == 1) {
            dateFormat = new SimpleDateFormat("MMM yy");
        } else {
            switch (actualInterval.interval) {
                case Calendar.DATE:
                case Calendar.WEEK_OF_YEAR:
                default:
                    dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
                    break;
                case Calendar.HOUR:
                case Calendar.MINUTE:
                    dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
                    break;
                case Calendar.SECOND:
                    dateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM);
                    break;
                case Calendar.MILLISECOND:
                    dateFormat = DateFormat.getTimeInstance(DateFormat.FULL);
                    break;
            }
        }
        return dateFormat.format(date);
    }


    private List<Date> makeDatesEven(List<Date> dates, Calendar calendar) {

        if (dates.size() > 2) {
            List<Date> evenDates = new ArrayList<Date>();
 for (int i = 0; i < dates.size(); i++) {
                calendar.setTime(dates.get(i));
                switch (actualInterval.interval) {
                    case Calendar.YEAR:
                           if (i != 0 && i != dates.size() - 1) {
                            calendar.set(Calendar.MONTH, 0);
                            calendar.set(Calendar.DATE, 1);
                        }
                        calendar.set(Calendar.HOUR_OF_DAY, 0);
                        calendar.set(Calendar.MINUTE, 0);
                        calendar.set(Calendar.SECOND, 0);
                        calendar.set(Calendar.MILLISECOND, 6);
                        break;
                    case Calendar.MONTH:
                                             if (i != 0 && i != dates.size() - 1) {
                            calendar.set(Calendar.DATE, 1);
                        }
                        calendar.set(Calendar.HOUR_OF_DAY, 0);
                        calendar.set(Calendar.MINUTE, 0);
                        calendar.set(Calendar.SECOND, 0);
                        calendar.set(Calendar.MILLISECOND, 5);
                        break;
                    case Calendar.WEEK_OF_YEAR:
                        // Make weeks begin with first day of week?
                        calendar.set(Calendar.HOUR_OF_DAY, 0);
                        calendar.set(Calendar.MINUTE, 0);
                        calendar.set(Calendar.SECOND, 0);
                        calendar.set(Calendar.MILLISECOND, 4);
                        break;
                    case Calendar.DATE:
                        calendar.set(Calendar.HOUR_OF_DAY, 0);
                        calendar.set(Calendar.MINUTE, 0);
                        calendar.set(Calendar.SECOND, 0);
                        calendar.set(Calendar.MILLISECOND, 3);
                        break;
                    case Calendar.HOUR:
                        if (i != 0 && i != dates.size() - 1) {
                            calendar.set(Calendar.MINUTE, 0);
                            calendar.set(Calendar.SECOND, 0);
                        }
                        calendar.set(Calendar.MILLISECOND, 2);
                        break;
                    case Calendar.MINUTE:
                        if (i != 0 && i != dates.size() - 1) {
                            calendar.set(Calendar.SECOND, 0);
                        }
                        calendar.set(Calendar.MILLISECOND, 1);
                        break;
                    case Calendar.SECOND:
                        calendar.set(Calendar.MILLISECOND, 0);
                        break;

                }
                evenDates.add(calendar.getTime());
            }

            return evenDates;
        } else {
            return dates;
        }
    }

    public final ObjectProperty<Date> lowerBoundProperty() {
        return lowerBound;
    }

    public final Date getLowerBound() {
        return lowerBound.get();
    }

    public final void setLowerBound(Date date) {
        lowerBound.set(date);
    }

    public final ObjectProperty<Date> upperBoundProperty() {
        return upperBound;
    }

    public final Date getUpperBound() {
        return upperBound.get();
    }

    public final void setUpperBound(Date date) {
        upperBound.set(date);
    }

    public final StringConverter<Date> getTickLabelFormatter() {
        return tickLabelFormatter.getValue();
    }

    public final void setTickLabelFormatter(StringConverter<Date> value) {
        tickLabelFormatter.setValue(value);
    }

    public final ObjectProperty<StringConverter<Date>> tickLabelFormatterProperty() {
        return tickLabelFormatter;
    }

    private enum Interval {
        DECADE(Calendar.YEAR, 10),
        YEAR(Calendar.YEAR, 1),
        MONTH_6(Calendar.MONTH, 6),
        MONTH_3(Calendar.MONTH, 3),
        MONTH_1(Calendar.MONTH, 1),
        WEEK(Calendar.WEEK_OF_YEAR, 1),
        DAY(Calendar.DATE, 1),
        HOUR_12(Calendar.HOUR, 12),
        HOUR_6(Calendar.HOUR, 6),
        HOUR_3(Calendar.HOUR, 3),
        HOUR_1(Calendar.HOUR, 1),
        MINUTE_15(Calendar.MINUTE, 15),
        MINUTE_5(Calendar.MINUTE, 5),
        MINUTE_1(Calendar.MINUTE, 1),
        SECOND_15(Calendar.SECOND, 15),
        SECOND_5(Calendar.SECOND, 5),
        SECOND_1(Calendar.SECOND, 1),
        MILLISECOND(Calendar.MILLISECOND, 1);

        private final int amount;

        private final int interval;

        private Interval(int interval, int amount) {
            this.interval = interval;
            this.amount = amount;
        }
    }
}

就是:

enter image description here