2016-01-08 37 views
1

我已經編寫了一個示例程序,它在JavaFX圖表上實現了縮放功能,我從GitHub項目中找到Zoom類,然後重新使用它。我的挑戰是,當我拖動鼠標選擇某個區域進行縮放時,所選區域矩形不會在Windows 7,Linux,Mac OS X中顯示,但在Windows 10中可以正常工作。 我錯過了什麼,怎麼可以我使selectedRectangle顯示,以便用戶可以知道他們正在放大哪個區域?在JavaFX圖表中沒有顯示縮放選擇矩形雖然縮放工作

下面是所有需要編譯並運行此程序的文件:

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package testlinechartgraphs; 

import java.util.ArrayList; 
import java.util.Random; 
import javafx.application.Application; 
import static javafx.application.Application.launch; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.geometry.Pos; 
import javafx.geometry.Side; 
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.CheckBox; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.HBox; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 

public class TestLineChartGraphs extends Application { 

    final static ObservableList<XYChart.Series<Number, Number>> lineChartData = FXCollections.observableArrayList(); 

    @Override 
    public void start(Stage stage) { 
     stage.setTitle("Line Chart Sample"); 
     //defining the axes 
     final NumberAxis xAxis = new NumberAxis(); 
     final NumberAxis yAxis = new NumberAxis(); 
     xAxis.setLabel("Number of Month"); 
     Random randomNumbers = new Random(); 
     ArrayList<Integer> arrayList = new ArrayList<>(); 

     //creating the chart 
     final LineChart<Number, Number> lineChart 
       = new LineChart<Number, Number>(xAxis, yAxis); 

     lineChart.setTitle("Stock Monitoring, 2010"); 
     lineChart.setLegendSide(Side.RIGHT); 

     int randomCount = randomNumbers.nextInt(14)+1; 
     //System.out.println("randomCount = " + randomCount); 
     for (int i = 0; i < randomCount; i++) { 
      XYChart.Series series = new XYChart.Series(); 
      series.setName("series_" + i); 
      for (int k = 0; k < 20; k++) { 
       int x = randomNumbers.nextInt(50); 

       series.getData().add(new XYChart.Data(k, x)); 
      } 
      //seriesList.add(series); 
      lineChartData.add(series); 
     } 


     lineChart.setData(lineChartData); 

     final StackPane chartContainer = new StackPane(); 

     Zoom zoom = new Zoom(lineChart, chartContainer); 

     chartContainer.getChildren() 
       .add(lineChart); 

     BorderPane borderPane = new BorderPane(); 

     borderPane.setCenter(chartContainer); 
     //borderPane.setCenter(lineChart); 

     borderPane.setBottom(getLegend()); 
////   
     //Scene scene = new Scene(lineChart, 800, 600); 
     Scene scene = new Scene(borderPane, 800, 600); 
     //lineChart.getData().addAll(series, series1); 

     stage.setScene(scene); 
     scene.getStylesheets().addAll("file:///C:/Users/siphoh/Documents/NetBeansProjects/WiresharkSeqNum/src/fancychart.css"); 
     //scene.getStylesheets().addAll(getClass().getResource("fancychart.css").toExternalForm()); 

     stage.show(); 
    } 

    public static Node getLegend() { 
     HBox hBox = new HBox(); 

     for (final XYChart.Series<Number, Number> series : lineChartData) { 
      CheckBox checkBox = new CheckBox(series.getName()); 


      checkBox.setSelected(true); 
      checkBox.setOnAction(event -> { 
       if (lineChartData.contains(series)) { 

        lineChartData.remove(series); 
       } else { 
        lineChartData.add(series); 
       } 
      }); 

      hBox.getChildren().add(checkBox); 
     } 

     hBox.setAlignment(Pos.CENTER); 
     hBox.setSpacing(20); 
     hBox.setStyle("-fx-padding: 0 10 20 10"); 

     return hBox; 
    } 

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

//Zoom.java類:

package testlinechartgraphs; 

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 

/** 
* 
* @author ************* 
*/ 
import javafx.event.EventHandler; 
import javafx.geometry.Point2D; 
import javafx.geometry.Pos; 
import javafx.scene.Node; 
import javafx.scene.chart.NumberAxis; 
import javafx.scene.chart.XYChart; 
import javafx.scene.control.Label; 
import javafx.scene.input.KeyCode; 
import javafx.scene.input.KeyEvent; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.Pane; 
import javafx.scene.layout.StackPane; 
import javafx.scene.paint.Color; 

/** 
* This class adds a zoom functionality to a given XY chart. Zoom means that a 
user can select a region in the chart that should be displayed at a larger 
scale. 
* 
*/ 
public class Zoom { 

    private static final String INFO_LABEL_ID = "zoomInfoLabel"; 
    private final Pane pane; 
    private final XYChart<Number, Number> chart; 
    private final NumberAxis xAxis; 
    private final NumberAxis yAxis; 
    private final SelectionRectangle selectionRectangle; 
    private Label infoLabel; 

    private Point2D selectionRectangleStart; 
    private Point2D selectionRectangleEnd; 

    /** 
    * Create a new instance of this class with the given chart and pane 
    * instances. The {@link Pane} instance is needed as a parent for the 
    * rectangle that represents the user selection. 
    * 
    * @param chart the xy chart to which the zoom support should be added 
    * @param pane the pane on which the selection rectangle will be drawn. 
    */ 
    public Zoom(XYChart<Number, Number> chart, Pane pane) { 
     this.pane = pane; 
     this.chart = chart; 
     this.xAxis = (NumberAxis) chart.getXAxis(); 
     this.yAxis = (NumberAxis) chart.getYAxis(); 
     selectionRectangle = new SelectionRectangle(); 

     pane.getChildren().add(selectionRectangle); 
     addDragSelectionMechanism(); 
     addInfoLabel(); 
    } 

    /** 
    * The info label shows a short info text that tells the user how to unreset 
    * the zoom level. 
    */ 
    private void addInfoLabel() { 
     infoLabel = new Label("Click ESC to reset the zoom level."); 
     infoLabel.setId(INFO_LABEL_ID); 
     pane.getChildren().add(infoLabel); 
     StackPane.setAlignment(infoLabel, Pos.TOP_RIGHT); 
     infoLabel.setVisible(false); 
    } 

    /** 
    * Adds a mechanism to select an area in the chart that should be displayed 
    * at larged scale. 
    */ 
    private void addDragSelectionMechanism() { 
     pane.addEventHandler(MouseEvent.MOUSE_PRESSED, new MousePressedHandler()); 
     pane.addEventHandler(MouseEvent.MOUSE_DRAGGED, new MouseDraggedHandler()); 
     pane.addEventHandler(MouseEvent.MOUSE_RELEASED, new MouseReleasedHandler()); 
     pane.addEventHandler(KeyEvent.KEY_RELEASED, new EscapeKeyHandler()); 
    } 

    private Point2D computeRectanglePoint(double eventX, double eventY) { 
     double lowerBoundX = computeOffsetInChart(xAxis, false); 
     double upperBoundX = lowerBoundX + xAxis.getWidth(); 
     double lowerBoundY = computeOffsetInChart(yAxis, true); 
     double upperBoundY = lowerBoundY + yAxis.getHeight(); 
     // make sure the rectangle's end point is in the interval defined by the lower and upper bounds for each 
     // dimension 
     double x = Math.max(lowerBoundX, Math.min(eventX, upperBoundX)); 
     double y = Math.max(lowerBoundY, Math.min(eventY, upperBoundY)); 
     return new Point2D(x, y); 
    } 

    /** 
    * Computes the pixel offset of the given node inside the chart node. 
    * 
    * @param node the node for which to compute the pixel offset 
    * @param vertical flag that indicates whether the horizontal or the 
    * vertical dimension should be taken into account 
    * @return the offset inside the chart node 
    */ 
    private double computeOffsetInChart(Node node, boolean vertical) { 
     double offset = 0; 
     do { 
      if (vertical) { 
       offset += node.getLayoutY(); 
      } else { 
       offset += node.getLayoutX(); 
      } 
      node = node.getParent(); 
     } while (node != chart); 
     return offset; 
    } 

    /** 
    * 
    */ 
    private final class MousePressedHandler implements EventHandler<MouseEvent> { 

     @Override 
     public void handle(final MouseEvent event) { 

      // do nothing for a right-click 
      if (event.isSecondaryButtonDown()) { 
       return; 
      } 

      // store position of initial click 
      selectionRectangleStart = computeRectanglePoint(event.getX(), event.getY()); 
      event.consume(); 
     } 
    } 

    /** 
    * 
    */ 
    private final class MouseDraggedHandler implements EventHandler<MouseEvent> { 

     @Override 
     public void handle(final MouseEvent event) { 

      // do nothing for a right-click 
      if (event.isSecondaryButtonDown()) { 
       return; 
      } 

      // store current cursor position 
      selectionRectangleEnd = computeRectanglePoint(event.getX(), event.getY()); 

      double x = Math.min(selectionRectangleStart.getX(), selectionRectangleEnd.getX()); 
      double y = Math.min(selectionRectangleStart.getY(), selectionRectangleEnd.getY()); 
      double width = Math.abs(selectionRectangleStart.getX() - selectionRectangleEnd.getX()); 
      double height = Math.abs(selectionRectangleStart.getY() - selectionRectangleEnd.getY()); 

      drawSelectionRectangle(x, y, width, height); 
      event.consume(); 
     } 

     /** 
     * Draws a selection box in the view. 
     * 
     * @param x the x position of the selection box 
     * @param y the y position of the selection box 
     * @param width the width of the selection box 
     * @param height the height of the selection box 
     */ 
     private void drawSelectionRectangle(final double x, final double y, final double width, final double height) { 
      selectionRectangle.setVisible(true); 
      selectionRectangle.setX(x); 
      selectionRectangle.setY(y); 
      selectionRectangle.setWidth(width); 
      selectionRectangle.setHeight(height); 
      //selectionRectangle.setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5)); 
      //System.out.println("Draw the rectangle ..."); 
     } 
    } 

    /** 
    * 
    */ 
    private final class MouseReleasedHandler implements EventHandler<MouseEvent> { 

     /** 
     * Defines a minimum width for the selected area. If the selected 
     * rectangle is not wider than this value, no zooming will take place. 
     * This helps prevent accidental zooming. 
     */ 
     private static final double MIN_RECTANGE_WIDTH = 10; 

     /** 
     * Defines a minimum height for the selected area. If the selected 
     * rectangle is not wider than this value, no zooming will take place. 
     * This helps prevent accidental zooming. 
     */ 
     private static final double MIN_RECTANGLE_HEIGHT = 10; 

     @Override 
     public void handle(final MouseEvent event) { 
      hideSelectionRectangle(); 

      if (selectionRectangleStart == null || selectionRectangleEnd == null) { 
       return; 
      } 

      if (isRectangleSizeTooSmall()) { 
       return; 
      } 

      setAxisBounds(); 
      showInfo(); 
      selectionRectangleStart = null; 
      selectionRectangleEnd = null; 

      // needed for the key event handler to receive events 
      pane.requestFocus(); 
      event.consume(); 
     } 

     private boolean isRectangleSizeTooSmall() { 
      double width = Math.abs(selectionRectangleEnd.getX() - selectionRectangleStart.getX()); 
      double height = Math.abs(selectionRectangleEnd.getY() - selectionRectangleStart.getY()); 
      return width < MIN_RECTANGE_WIDTH || height < MIN_RECTANGLE_HEIGHT; 
     } 

     /** 
     * Hides the selection rectangle. 
     */ 
     private void hideSelectionRectangle() { 
      selectionRectangle.setVisible(false); 
     } 

     private void setAxisBounds() { 
      disableAutoRanging(); 

      // compute new bounds for the chart's x and y axes 
      double selectionMinX = Math.min(selectionRectangleStart.getX(), selectionRectangleEnd.getX()); 
      double selectionMaxX = Math.max(selectionRectangleStart.getX(), selectionRectangleEnd.getX()); 
      double selectionMinY = Math.min(selectionRectangleStart.getY(), selectionRectangleEnd.getY()); 
      double selectionMaxY = Math.max(selectionRectangleStart.getY(), selectionRectangleEnd.getY()); 

      setHorizontalBounds(selectionMinX, selectionMaxX); 
      setVerticalBounds(selectionMinY, selectionMaxY); 
     } 

     private void disableAutoRanging() { 
      xAxis.setAutoRanging(false); 
      yAxis.setAutoRanging(false); 
     } 

     private void showInfo() { 
      infoLabel.setVisible(true); 
     } 

     /** 
     * Sets new bounds for the chart's x axis. 
     * 
     * @param minPixelPosition the x position of the selection rectangle's 
     * left edge (in pixels) 
     * @param maxPixelPosition the x position of the selection rectangle's 
     * right edge (in pixels) 
     */ 
     private void setHorizontalBounds(double minPixelPosition, double maxPixelPosition) { 
      double currentLowerBound = xAxis.getLowerBound(); 
      double currentUpperBound = xAxis.getUpperBound(); 
      double offset = computeOffsetInChart(xAxis, false); 
      setLowerBoundX(minPixelPosition, currentLowerBound, currentUpperBound, offset); 
      setUpperBoundX(maxPixelPosition, currentLowerBound, currentUpperBound, offset); 
     } 

     /** 
     * Sets new bounds for the chart's y axis. 
     * 
     * @param minPixelPosition the y position of the selection rectangle's 
     * upper edge (in pixels) 
     * @param maxPixelPosition the y position of the selection rectangle's 
     * lower edge (in pixels) 
     */ 
     private void setVerticalBounds(double minPixelPosition, double maxPixelPosition) { 
      double currentLowerBound = yAxis.getLowerBound(); 
      double currentUpperBound = yAxis.getUpperBound(); 
      double offset = computeOffsetInChart(yAxis, true); 
      setLowerBoundY(maxPixelPosition, currentLowerBound, currentUpperBound, offset); 
      setUpperBoundY(minPixelPosition, currentLowerBound, currentUpperBound, offset); 
     } 

     private void setLowerBoundX(double pixelPosition, double currentLowerBound, double currentUpperBound, 
       double offset) { 
      double newLowerBound = computeBound(pixelPosition, offset, xAxis.getWidth(), currentLowerBound, 
        currentUpperBound, false); 
      xAxis.setLowerBound(newLowerBound); 
     } 

     private void setUpperBoundX(double pixelPosition, double currentLowerBound, double currentUpperBound, 
       double offset) { 
      double newUpperBound = computeBound(pixelPosition, offset, xAxis.getWidth(), currentLowerBound, 
        currentUpperBound, false); 
      xAxis.setUpperBound(newUpperBound); 
     } 

     private void setLowerBoundY(double pixelPosition, double currentLowerBound, double currentUpperBound, 
       double offset) { 
      double newLowerBound = computeBound(pixelPosition, offset, yAxis.getHeight(), currentLowerBound, 
        currentUpperBound, true); 
      yAxis.setLowerBound(newLowerBound); 
     } 

     private void setUpperBoundY(double pixelPosition, double currentLowerBound, double currentUpperBound, 
       double offset) { 
      double newUpperBound = computeBound(pixelPosition, offset, yAxis.getHeight(), currentLowerBound, 
        currentUpperBound, true); 
      yAxis.setUpperBound(newUpperBound); 
     } 

     private double computeBound(double pixelPosition, double pixelOffset, double pixelLength, double lowerBound, 
       double upperBound, boolean axisInverted) { 
      double pixelPositionWithoutOffset = pixelPosition - pixelOffset; 
      double relativePosition = pixelPositionWithoutOffset/pixelLength; 
      double axisLength = upperBound - lowerBound; 

      // The screen's y axis grows from top to bottom, whereas the chart's y axis goes from bottom to top. 
      // That's 
      // why we need to have this distinction here. 
      double offset = 0; 
      int sign = 0; 
      if (axisInverted) { 
       offset = upperBound; 
       sign = -1; 
      } else { 
       offset = lowerBound; 
       sign = 1; 
      } 

      double newBound = offset + sign * relativePosition * axisLength; 
      return newBound; 
     } 
    } 

    /** 
    * 
    */ 
    private final class EscapeKeyHandler implements EventHandler<KeyEvent> { 

     @Override 
     public void handle(KeyEvent event) { 

      // the ESCAPE key lets the user reset the zoom level 
      if (KeyCode.ESCAPE.equals(event.getCode())) { 
       resetAxisBounds(); 
       hideInfo(); 
      } 
     } 

     private void resetAxisBounds() { 
      xAxis.setAutoRanging(true); 
      yAxis.setAutoRanging(true); 
     } 

     private void hideInfo() { 
      infoLabel.setVisible(false); 
     } 
    } 

} 

//SelectionRectangle.java類

package testlinechartgraphs; 

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 

/** 
* 
* @author ************** 
*/ 
import javafx.scene.shape.Rectangle; 

/** 
* Represents an area on the screen that was selected by a mouse drag operation. 
* 
*/ 
public class SelectionRectangle extends Rectangle { 

    private static final String STYLE_CLASS_SELECTION_BOX = "chart-selection-rectangle"; 

    public SelectionRectangle() { 

     getStyleClass().addAll(STYLE_CLASS_SELECTION_BOX); 
     setVisible(false); 
     setManaged(false); 
     setMouseTransparent(true); 

    } 
} 

// fancychart.css文件

.chart-line-symbol { 
    -fx-scale-x: 0.5; 
    -fx-scale-y: 0.5; 
} 

.chart-popup-label { 
    -fx-padding: 1 3 1 3; 
    -fx-border-radius: 1; 
    -fx-border-width: 1; 
    -fx-opacity: 0.7; 
    -fx-effect: dropshadow(two-pass-box , rgba(0,0,0,0.3) , 8, 0.0 , 0 , 3); 
} 

.chart-legend-item { 
    -fx-padding : 1 23 1 23; 
} 

.chart-legend-item-symbol { 
    -fx-scale-x: 0.8; 
    -fx-scale-y: 0.8; 
} 



.chart-selection-rectangle { 
    -fx-stroke: rgba(135, 206, 250, 0.8); 
    -fx-stroke-type: inside; 
    -fx-fill: rgba(135, 206, 250, 0.2); 
} 







#zoomInfoLabel { 
    -fx-background-color: rgba(135, 206, 250, 0.8); 
    -fx-font-size: 14; 
    -fx-padding: 3; 
    -fx-background-radius: 2; 
} 

任何幫助將不勝感激解決這個問題。

謝謝,

回答

0

您的訂單有誤。你有

final StackPane chartContainer = new StackPane(); 
    Zoom zoom = new Zoom(lineChart, chartContainer); 
    chartContainer.getChildren().add(lineChart); 

這意味着你首先創建容器,然後添加縮放矩形,然後添加圖表。所以縮放矩形位於圖表的背面。

你需要有這樣說:

final StackPane chartContainer = new StackPane(); 
    chartContainer.getChildren().add(lineChart); 
    Zoom zoom = new Zoom(lineChart, chartContainer); 
+0

非常感謝羅蘭,是工作!我花了幾天時間...再次感謝。 – FredH