2013-08-02 20 views
5

Utilizzando i grafici JavaFX, ho bisogno di invertire l'asse y di un grafico ad area in pila in modo che uno zero positivo sia in alto e che i numeri positivi lavorino verso il basso sull'asse y. Di seguito è riportato un mock-up di ciò che sto cercando di ottenere.Grafico con asse y invertito

Inverted Y-Axis with positive numbers

Qual è la migliore (leggi: più breve tempo di sviluppo e di alta code-riutilizzo) modo per raggiungere questo obiettivo in JavaFX?

UPDATE

convertendo i dati in numeri negativi non è un'opzione. Sto cercando risposte che funzionino con i numeri positivi, "intatto".

risposta

2

È possibile utilizzare l'asse normale con valori negativi, ma aggiungere TickLabelFormatter che rimuoverà il segno meno.

final NumberAxis yAxis = new NumberAxis(-25, 0, 5); 

    yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) { 
     @Override 
     public String toString(Number value) { 
      // note we are printing minus value 
      return String.format("%7.1f", -value.doubleValue()); 
     } 
    }); 

    series1.getData().add(new XYChart.Data("Jan", -1)); 
    series1.getData().add(new XYChart.Data("Feb", -5)); 
    series1.getData().add(new XYChart.Data("Mar", -20)); 
+0

Stai suggerendo di convertire tutti i miei dati in numeri negativi prima di usarlo nei grafici? – mawcsco

+0

si. Ovviamente non è il modo migliore, ma molto più veloce quindi estendere il grafico con funzionalità di inversione. Per evitare l'aggiornamento diretto dei dati, è possibile estendere la classe 'XYChart.Data' con la logica di inversione o introdurre un metodo di utilità che aggiunga numeri invertiti al grafico. –

0

Non facile opzione è quella di consentire il funzionamento inverse() su assi. Senza applicare patch alle classi JRE è piuttosto complicato.

alcuni suggerimenti su come avvicinarsi a questo:

1) estendere ValueAxis o asse (NumberAxis è definitiva purtroppo)

2) aggiungere campo booleano e inverse() metodo per la classe all'asse

public void inverse() { 
    inversed = !inversed; // boolean property 
    invalidateRange(); 
    requestAxisLayout(); 
} 

3) se estendi ValueAxis - dovrai compensare l'offset applicato dalla superclasse (e intercettare il codice in cui cambia la dimensione dell'asse)

@Override 
public Long getValueForDisplay(double displayPosition) { 
    if (inversed) 
     return super.getValueForDisplay(offset - displayPosition); 
    else 
     return super.getValueForDisplay(displayPosition); 
} 

@Override 
public double getDisplayPosition(Long value) { 
    if (inversed) 
     return offset - super.getDisplayPosition(value); 
    else 
     return super.getDisplayPosition(value); 
} 

4) (il pezzo più brutto) nasconde le tacche dell'etichetta soppresse dalla classe Axis - l'implementazione originale dipende dall'ordine predefinito di tick. Non ho trovato nessun altro modo che lo sblocchi tramite la riflessione. Quindi questo è molto fragile.

@Override 
protected void layoutChildren() { 
    final Side side = getSide(); 
    boolean isHorisontal = null == side || side.isHorizontal(); 
    this.offset = isHorisontal ? getWidth() : getHeight(); 
    super.layoutChildren(); 
    if (inversed) { 
     double prevEnd = isHorisontal ? offset + getTickLabelGap() : 0; 
     for (TickMark m : getTickMarks()) { 
      double position = m.getPosition(); 
      try { 
       final Text textNode = (Text) textNodeField.get(m); 
       final Bounds bounds = textNode.getLayoutBounds(); 
       if (0 <= position && position <= offset) 
        if (isHorisontal) { 
         textNode.setVisible(position < prevEnd); 
         prevEnd = position - (bounds.getWidth() + getTickLabelGap()); 
        } else { 
         textNode.setVisible(position > prevEnd); 
         prevEnd = position + (bounds.getHeight() + getTickLabelGap()); 
        } 
      } catch (IllegalAccessException ignored) { 
      } 
     } 
    } 
} 

asse Y è invertito in questo esempio this is how it looks like

0

utilizzare le proprietà lowerbound e limitesuperiore dell'asse. Il LowerBound deve essere superiore a upperBound. Ecco come si può fare usando fxml:

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.*?> 
<?import javafx.scene.control.*?> 
<?import javafx.scene.layout.*?> 

<HBox   
    prefHeight="600.0" 
    prefWidth="800.0" 
    xmlns:fx="http://javafx.com/fxml/1">  

    <javafx.scene.chart.LineChart 
     fx:id="chart" > 
     <xAxis > 
      <javafx.scene.chart.NumberAxis 
       label="X Axis" 
       lowerBound="0.01" 
       upperBound="100" 
       tickUnit="10" 
       autoRanging="false" /> 
     </xAxis> 
     <yAxis> 
      <javafx.scene.chart.NumberAxis 
        label="Y Axis" 
        lowerBound="100" 
        upperBound="0.001" 
        tickUnit="10" 
        autoRanging="false" 
       /> 
     </yAxis> 
    </javafx.scene.chart.LineChart>    
</Hbox>