2012-06-18 21 views
2

我有這樣的代碼來對XYChart線型圖<號碼進行變焦,數字>的JavaFX 2.x的放大XYChart與類別AXYS

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

我想用<字符串具有相同的變焦效果,號碼>,因爲我想在X AXYS

+1

阿爾貝託嗨,我有一些代碼,創建了一個可縮放的折線圖與<日期,編號>,你還是有興趣嗎? –

回答

3

OK使用日期和時間作爲字符串,所以這是我使用的應用程序,它不是很整潔,但它會做的工作:

首先我使用的Javafx的日期軸類:

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

IVE加入日期軸的版本使用即時通訊(其舊,以及i刪除,由於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