< prev index next >

modules/controls/src/main/java/javafx/scene/chart/BarChart.java

Print this page
rev 9945 : 8089755: AreaChart area color change when series is removed


  37 import javafx.animation.Timeline;
  38 import javafx.application.Platform;
  39 import javafx.beans.NamedArg;
  40 import javafx.beans.property.DoubleProperty;
  41 import javafx.beans.value.WritableValue;
  42 import javafx.collections.FXCollections;
  43 import javafx.collections.ObservableList;
  44 import javafx.geometry.Orientation;
  45 import javafx.scene.Node;
  46 import javafx.scene.layout.StackPane;
  47 import javafx.util.Duration;
  48 
  49 import com.sun.javafx.charts.Legend;
  50 import com.sun.javafx.charts.Legend.LegendItem;
  51 
  52 import javafx.css.StyleableDoubleProperty;
  53 import javafx.css.CssMetaData;
  54 import javafx.css.PseudoClass;
  55 
  56 import javafx.css.converter.SizeConverter;

  57 
  58 import javafx.css.Styleable;
  59 import javafx.css.StyleableProperty;
  60 
  61 /**
  62  * A chart that plots bars indicating data values for a category. The bars can be vertical or horizontal depending on
  63  * which axis is a category axis.
  64  * @since JavaFX 2.0
  65  */
  66 public class BarChart<X,Y> extends XYChart<X,Y> {
  67 
  68     // -------------- PRIVATE FIELDS -------------------------------------------
  69 
  70     private Map<Series<X,Y>, Map<String, Data<X,Y>>> seriesCategoryMap = new HashMap<>();
  71     private Legend legend = new Legend();
  72     private final Orientation orientation;
  73     private CategoryAxis categoryAxis;
  74     private ValueAxis valueAxis;
  75     private Timeline dataRemoveTimeline;
  76     private double bottomPos  = 0;


 250          double barVal;
 251          double currentVal;
 252         if (orientation == Orientation.VERTICAL) {
 253              barVal = ((Number)item.getYValue()).doubleValue();
 254              currentVal = ((Number)item.getCurrentY()).doubleValue();
 255         } else {
 256              barVal = ((Number)item.getXValue()).doubleValue();
 257              currentVal = ((Number)item.getCurrentX()).doubleValue();
 258         }
 259          if (currentVal > 0 && barVal < 0) { // going from positive to negative
 260              // add style class negative
 261              item.getNode().getStyleClass().add(NEGATIVE_STYLE);
 262          } else if (currentVal < 0 && barVal > 0) { // going from negative to positive
 263              // remove style class negative
 264              // RT-21164 upside down bars: was adding NEGATIVE_STYLE styleclass
 265              // instead of removing it; when going from negative to positive
 266              item.getNode().getStyleClass().remove(NEGATIVE_STYLE);
 267          }
 268     }
 269 













 270     @Override protected void seriesAdded(Series<X,Y> series, int seriesIndex) {
 271         // handle any data already in series
 272         // create entry in the map
 273         Map<String, Data<X,Y>> categoryMap = new HashMap<String, Data<X,Y>>();
 274         for (int j=0; j<series.getData().size(); j++) {
 275             Data<X,Y> item = series.getData().get(j);
 276             Node bar = createBar(series, seriesIndex, item, j);
 277             String category;
 278             if (orientation == Orientation.VERTICAL) {
 279                 category = (String)item.getXValue();
 280             } else {
 281                 category = (String)item.getYValue();
 282             }
 283             categoryMap.put(category, item);
 284             if (shouldAnimate()) {
 285                 animateDataAdd(item, bar);
 286             } else {
 287                 // RT-21164 check if bar value is negative to add NEGATIVE_STYLE style class
 288                 double barVal = (orientation == Orientation.VERTICAL) ? ((Number)item.getYValue()).doubleValue() :
 289                         ((Number)item.getXValue()).doubleValue();
 290                 if (barVal < 0) {
 291                     bar.getStyleClass().add(NEGATIVE_STYLE);
 292                 }
 293                 getPlotChildren().add(bar);
 294             }
 295         }
 296         if (categoryMap.size() > 0) seriesCategoryMap.put(series, categoryMap);
 297     }
 298 
 299     @Override protected void seriesRemoved(final Series<X,Y> series) {
 300         updateDefaultColorIndex(series);
 301         // remove all symbol nodes
 302         if (shouldAnimate()) {
 303             pt = new ParallelTransition();
 304             pt.setOnFinished(event -> {
 305                 removeSeriesFromDisplay(series);
 306             });
 307 
 308             boolean lastSeries = (getSeriesSize() > 1) ? false : true;
 309             XYValueMap.clear();
 310             for (final Data<X,Y> d : series.getData()) {
 311                 final Node bar = d.getNode();
 312                 // Animate series deletion
 313                 if (!lastSeries) {
 314                         Timeline t = createDataRemoveTimeline(d, bar, series);
 315                         pt.getChildren().add(t);
 316                 } else {
 317                     // fade out last series
 318                     FadeTransition ft = new FadeTransition(Duration.millis(700),bar);
 319                     ft.setFromValue(1);
 320                     ft.setToValue(0);


 524         if (pt!= null) {
 525             if (!pt.getChildren().isEmpty()) {
 526                 for (Animation a : pt.getChildren()) {
 527                     a.setOnFinished(null);
 528                 }
 529             }
 530             for (Data<X,Y> item : series.getData()) {
 531                 processDataRemove(series, item);
 532                 if (!lastSeries) {
 533                     restoreDataValues(item);
 534                 }
 535             }
 536             XYValueMap.clear();
 537             pt.setOnFinished(null);
 538             pt.getChildren().clear();
 539             pt.stop();
 540             removeSeriesFromDisplay(series);
 541         }
 542     }
 543 
 544     private void updateDefaultColorIndex(final Series<X,Y> series) {
 545         int clearIndex = seriesColorMap.get(series);
 546         for (Data<X,Y> d : series.getData()) {
 547             final Node bar = d.getNode();
 548             if (bar != null) {
 549                 bar.getStyleClass().remove(DEFAULT_COLOR+clearIndex);
 550             }
 551         }
 552     }
 553 
 554     private Node createBar(Series<X,Y> series, int seriesIndex, final Data<X,Y> item, int itemIndex) {
 555         Node bar = item.getNode();
 556         if (bar == null) {
 557             bar = new StackPane();
 558             bar.setAccessibleRole(AccessibleRole.TEXT);
 559             bar.setAccessibleRoleDescription("Bar");
 560             bar.focusTraversableProperty().bind(Platform.accessibilityActiveProperty());
 561             item.setNode(bar);
 562         }
 563         bar.getStyleClass().addAll("chart-bar", "series" + seriesIndex, "data" + itemIndex,series.defaultColorStyleClass);
 564         return bar;
 565     }
 566 
 567     private Data<X,Y> getDataItem(Series<X,Y> series, int seriesIndex, int itemIndex, String category) {
 568         Map<String, Data<X,Y>> catmap = seriesCategoryMap.get(series);
 569         return (catmap != null) ? catmap.get(category) : null;
 570     }
 571 
 572     // -------------- STYLESHEET HANDLING ------------------------------------------------------------------------------
 573 
 574     /*
 575      * Super-lazy instantiation pattern from Bill Pugh.
 576      */
 577      private static class StyleableProperties {
 578          private static final CssMetaData<BarChart<?,?>,Number> BAR_GAP =
 579              new CssMetaData<BarChart<?,?>,Number>("-fx-bar-gap",
 580                  SizeConverter.getInstance(), 4.0) {
 581 
 582             @Override
 583             public boolean isSettable(BarChart<?,?> node) {




  37 import javafx.animation.Timeline;
  38 import javafx.application.Platform;
  39 import javafx.beans.NamedArg;
  40 import javafx.beans.property.DoubleProperty;
  41 import javafx.beans.value.WritableValue;
  42 import javafx.collections.FXCollections;
  43 import javafx.collections.ObservableList;
  44 import javafx.geometry.Orientation;
  45 import javafx.scene.Node;
  46 import javafx.scene.layout.StackPane;
  47 import javafx.util.Duration;
  48 
  49 import com.sun.javafx.charts.Legend;
  50 import com.sun.javafx.charts.Legend.LegendItem;
  51 
  52 import javafx.css.StyleableDoubleProperty;
  53 import javafx.css.CssMetaData;
  54 import javafx.css.PseudoClass;
  55 
  56 import javafx.css.converter.SizeConverter;
  57 import javafx.collections.ListChangeListener;
  58 
  59 import javafx.css.Styleable;
  60 import javafx.css.StyleableProperty;
  61 
  62 /**
  63  * A chart that plots bars indicating data values for a category. The bars can be vertical or horizontal depending on
  64  * which axis is a category axis.
  65  * @since JavaFX 2.0
  66  */
  67 public class BarChart<X,Y> extends XYChart<X,Y> {
  68 
  69     // -------------- PRIVATE FIELDS -------------------------------------------
  70 
  71     private Map<Series<X,Y>, Map<String, Data<X,Y>>> seriesCategoryMap = new HashMap<>();
  72     private Legend legend = new Legend();
  73     private final Orientation orientation;
  74     private CategoryAxis categoryAxis;
  75     private ValueAxis valueAxis;
  76     private Timeline dataRemoveTimeline;
  77     private double bottomPos  = 0;


 251          double barVal;
 252          double currentVal;
 253         if (orientation == Orientation.VERTICAL) {
 254              barVal = ((Number)item.getYValue()).doubleValue();
 255              currentVal = ((Number)item.getCurrentY()).doubleValue();
 256         } else {
 257              barVal = ((Number)item.getXValue()).doubleValue();
 258              currentVal = ((Number)item.getCurrentX()).doubleValue();
 259         }
 260          if (currentVal > 0 && barVal < 0) { // going from positive to negative
 261              // add style class negative
 262              item.getNode().getStyleClass().add(NEGATIVE_STYLE);
 263          } else if (currentVal < 0 && barVal > 0) { // going from negative to positive
 264              // remove style class negative
 265              // RT-21164 upside down bars: was adding NEGATIVE_STYLE styleclass
 266              // instead of removing it; when going from negative to positive
 267              item.getNode().getStyleClass().remove(NEGATIVE_STYLE);
 268          }
 269     }
 270 
 271     @Override protected void seriesChanged(ListChangeListener.Change<? extends Series> c) {
 272         // Update style classes for all series lines and symbols
 273         // Note: is there a more efficient way of doing this?
 274         for (int i = 0; i < getDataSize(); i++) {
 275             final Series<X,Y> series = getData().get(i);
 276             for (int j=0; j<series.getData().size(); j++) {
 277                 Data<X,Y> item = series.getData().get(j);
 278                 Node bar = item.getNode();
 279                 bar.getStyleClass().setAll("chart-bar", "series" + i, "data" + j, series.defaultColorStyleClass);
 280             }
 281         }
 282     }
 283 
 284     @Override protected void seriesAdded(Series<X,Y> series, int seriesIndex) {
 285         // handle any data already in series
 286         // create entry in the map
 287         Map<String, Data<X,Y>> categoryMap = new HashMap<String, Data<X,Y>>();
 288         for (int j=0; j<series.getData().size(); j++) {
 289             Data<X,Y> item = series.getData().get(j);
 290             Node bar = createBar(series, seriesIndex, item, j);
 291             String category;
 292             if (orientation == Orientation.VERTICAL) {
 293                 category = (String)item.getXValue();
 294             } else {
 295                 category = (String)item.getYValue();
 296             }
 297             categoryMap.put(category, item);
 298             if (shouldAnimate()) {
 299                 animateDataAdd(item, bar);
 300             } else {
 301                 // RT-21164 check if bar value is negative to add NEGATIVE_STYLE style class
 302                 double barVal = (orientation == Orientation.VERTICAL) ? ((Number)item.getYValue()).doubleValue() :
 303                         ((Number)item.getXValue()).doubleValue();
 304                 if (barVal < 0) {
 305                     bar.getStyleClass().add(NEGATIVE_STYLE);
 306                 }
 307                 getPlotChildren().add(bar);
 308             }
 309         }
 310         if (categoryMap.size() > 0) seriesCategoryMap.put(series, categoryMap);
 311     }
 312 
 313     @Override protected void seriesRemoved(final Series<X,Y> series) {

 314         // remove all symbol nodes
 315         if (shouldAnimate()) {
 316             pt = new ParallelTransition();
 317             pt.setOnFinished(event -> {
 318                 removeSeriesFromDisplay(series);
 319             });
 320 
 321             boolean lastSeries = (getSeriesSize() > 1) ? false : true;
 322             XYValueMap.clear();
 323             for (final Data<X,Y> d : series.getData()) {
 324                 final Node bar = d.getNode();
 325                 // Animate series deletion
 326                 if (!lastSeries) {
 327                         Timeline t = createDataRemoveTimeline(d, bar, series);
 328                         pt.getChildren().add(t);
 329                 } else {
 330                     // fade out last series
 331                     FadeTransition ft = new FadeTransition(Duration.millis(700),bar);
 332                     ft.setFromValue(1);
 333                     ft.setToValue(0);


 537         if (pt!= null) {
 538             if (!pt.getChildren().isEmpty()) {
 539                 for (Animation a : pt.getChildren()) {
 540                     a.setOnFinished(null);
 541                 }
 542             }
 543             for (Data<X,Y> item : series.getData()) {
 544                 processDataRemove(series, item);
 545                 if (!lastSeries) {
 546                     restoreDataValues(item);
 547                 }
 548             }
 549             XYValueMap.clear();
 550             pt.setOnFinished(null);
 551             pt.getChildren().clear();
 552             pt.stop();
 553             removeSeriesFromDisplay(series);
 554         }
 555     }
 556 










 557     private Node createBar(Series<X,Y> series, int seriesIndex, final Data<X,Y> item, int itemIndex) {
 558         Node bar = item.getNode();
 559         if (bar == null) {
 560             bar = new StackPane();
 561             bar.setAccessibleRole(AccessibleRole.TEXT);
 562             bar.setAccessibleRoleDescription("Bar");
 563             bar.focusTraversableProperty().bind(Platform.accessibilityActiveProperty());
 564             item.setNode(bar);
 565         }
 566         bar.getStyleClass().setAll("chart-bar", "series" + seriesIndex, "data" + itemIndex,series.defaultColorStyleClass);
 567         return bar;
 568     }
 569 
 570     private Data<X,Y> getDataItem(Series<X,Y> series, int seriesIndex, int itemIndex, String category) {
 571         Map<String, Data<X,Y>> catmap = seriesCategoryMap.get(series);
 572         return (catmap != null) ? catmap.get(category) : null;
 573     }
 574 
 575     // -------------- STYLESHEET HANDLING ------------------------------------------------------------------------------
 576 
 577     /*
 578      * Super-lazy instantiation pattern from Bill Pugh.
 579      */
 580      private static class StyleableProperties {
 581          private static final CssMetaData<BarChart<?,?>,Number> BAR_GAP =
 582              new CssMetaData<BarChart<?,?>,Number>("-fx-bar-gap",
 583                  SizeConverter.getInstance(), 4.0) {
 584 
 585             @Override
 586             public boolean isSettable(BarChart<?,?> node) {


< prev index next >