Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.control; 027 028import javafx.beans.InvalidationListener; 029import javafx.beans.Observable; 030import javafx.beans.property.BooleanProperty; 031import javafx.beans.property.ObjectProperty; 032import javafx.beans.property.SimpleBooleanProperty; 033import javafx.beans.property.SimpleObjectProperty; 034import javafx.collections.ObservableList; 035import javafx.scene.Node; 036import javafx.scene.layout.GridPane; 037import javafx.scene.layout.HBox; 038import javafx.scene.shape.Rectangle; 039import javafx.css.CssMetaData; 040import com.sun.javafx.scene.control.accessible.AccessibleListItem; 041import com.sun.javafx.accessible.providers.AccessibleProvider; 042import javafx.css.PseudoClass; 043import javafx.beans.property.ReadOnlyBooleanProperty; 044import javafx.beans.property.ReadOnlyBooleanWrapper; 045import javafx.css.StyleableProperty; 046 047/** 048 * The Cell API is used for virtualized controls such as {@link ListView}, 049 * {@link TreeView}, and {@link TableView}. 050 * A Cell is a {@link Labeled} {@link Control}, and is used to render a single 051 * "row" inside a ListView, TreeView or TableView. Cells are also used for each 052 * individual 'cell' inside a TableView (i.e. each row/column intersection). See 053 * the JavaDoc for each control separately for more detail. 054 * <p> 055 * Every Cell is associated with a single data item (represented by the 056 * {@link #itemProperty() item} property). The Cell is responsible for 057 * rendering that item and, where appropriate, for editing the item. An item 058 * within a Cell may be represented by text or some other control such as a 059 * {@link CheckBox}, {@link ChoiceBox} or any other {@link Node} such as a 060 * {@link HBox}, {@link GridPane}, or even a {@link Rectangle}. 061 * <p> 062 * Because TreeView, ListView, TableView and other such controls can potentially 063 * be used for displaying incredibly large amounts of data, it is not feasible 064 * to create an actual Cell for every single item in the control. 065 * We represent extremely large data sets using only very few Cells. Each Cell 066 * is "recycled", or reused. This is what we mean when we say that these controls 067 * are virtualized. 068 * <p> 069 * Since Cell is a Control, it is essentially a "model". Its Skin is responsible 070 * for defining the look and layout, while the Behavior is responsible for 071 * handling all input events and using that information to modify the Control 072 * state. Also, the Cell is styled from CSS just like any other Control. 073 * However, it is not necessary to implement a Skin for most uses of a Cell. 074 * This is because a cell factory can be set - this is detailed more shortly. 075 * <p> 076 * Because by far the most common use case for cells is to show text to a user, 077 * this use case is specially optimized for within Cell. This is done by Cell 078 * extending from {@link Labeled}. This means that subclasses of Cell need only 079 * set the {@link #textProperty() text} property, rather than create a separate 080 * {@link Label} and set that within the Cell. However, for situations where 081 * something more than just plain text is called for, it is possible to place 082 * any {@link Node} in the Cell {@link #graphicProperty() graphic} property. 083 * Despite the term, a graphic can be any Node, and will be fully interactive. 084 * For example, a ListCell might be configured with a {@link Button} as its 085 * graphic. The Button text could then be bound to the cells 086 * {@link #itemProperty() item} property. In this way, whenever the item in the 087 * Cell changes, the Button text is automatically updated. 088 * <p> 089 * Cell sets focusTraversable to false. 090 * </p> 091 * <p> 092 * <b>Cell Factories</b> 093 * <p> 094 * The default representation of the Cell <code>item</code> is up to the various 095 * virtualized container's skins to render. For example, the ListView by default 096 * will convert the item to a String and call {@link #setText(java.lang.String)} 097 * with this value. If you want to specialize the Cell used for the 098 * ListView (for example), then you must provide an implementation of the 099 * {@link ListView#cellFactoryProperty() cellFactory} callback function defined 100 * on the ListView. Similar API exists on most controls that use Cells (for example, 101 * {@link TreeView#cellFactoryProperty() TreeView}, 102 * {@link TableView#rowFactoryProperty() TableView}, 103 * {@link TableColumn#cellFactoryProperty() TableColumn} and 104 * {@link ListView#cellFactoryProperty() ListView}. 105 * <p> 106 * The cell factory is called by the platform whenever it determines that a new 107 * cell needs to be created. For example, perhaps your ListView has 10 million 108 * items. Creating all 10 million cells would be prohibitively expensive. So 109 * instead the ListView skin implementation might only create just enough cells 110 * to fit the visual space. If the ListView is resized to be larger, the system 111 * will determine that it needs to create some additional cells. In this case 112 * it will call the cellFactory callback function (if one is provided) to create 113 * the Cell implementation that should be used. If no cell factory is provided, 114 * the built-in default implementation will be used. 115 * <p> 116 * The implementation of the cell factory is then responsible not just for 117 * creating a Cell instance, but also configuring that Cell such that it reacts 118 * to changes in its state. For example, if I were to create 119 * a custom Cell which formatted Numbers such that they would appear as currency 120 * types, I might do so like this: 121 * 122 * <pre> 123 * public class MoneyFormatCell extends ListCell<Number> { 124 * 125 * public MoneyFormatCell() { } 126 * 127 * @Override protected void updateItem(Number item, boolean empty) { 128 * // calling super here is very important - don't skip this! 129 * super.updateItem(item, empty); 130 * 131 * // format the number as if it were a monetary value using the 132 * // formatting relevant to the current locale. This would format 133 * // 43.68 as "$43.68", and -23.67 as "-$23.67" 134 * setText(item == null ? "" : NumberFormat.getCurrencyInstance().format(item)); 135 * 136 * // change the text fill based on whether it is positive (green) 137 * // or negative (red). If the cell is selected, the text will 138 * // always be white (so that it can be read against the blue 139 * // background), and if the value is zero, we'll make it black. 140 * if (item != null) { 141 * double value = item.doubleValue(); 142 * setTextFill(isSelected() ? Color.WHITE : 143 * value == 0 ? Color.BLACK : 144 * value < 0 ? Color.RED : Color.GREEN); 145 * } 146 * } 147 * }</pre> 148 * 149 * This class could then be used inside a ListView as such: 150 * 151 * <pre> 152 * ObservableList<Number> money = ...; 153 * final ListView<Number> listView = new ListView<Number>(money); 154 * listView.setCellFactory(new Callback<ListView<Number>, ListCell<Number>>() { 155 * @Override public ListCell<Number> call(ListView<Number> list) { 156 * return new MoneyFormatCell(); 157 * } 158 * });</pre> 159 * 160 * In this example an anonymous inner class is created, that simply returns 161 * instances of MoneyFormatCell whenever it is called. The MoneyFormatCell class 162 * extends {@link ListCell}, overriding the 163 * {@link #updateItem(java.lang.Object, boolean) updateItem} method. This method 164 * is called whenever the item in the cell changes, for example when the user 165 * scrolls the ListView or the content of the underlying data model changes 166 * (and the cell is reused to represent some different item in the ListView). 167 * Because of this, there is no need to manage bindings - simply react to the 168 * change in items when this method occurs. In the example above, whenever the 169 * item changes, we update the cell text property, and also modify the text fill 170 * to ensure that we get the correct visuals. In addition, if the cell is "empty" 171 * (meaning it is used to fill out space in the ListView but doesn't have any 172 * data associated with it), then we just use the empty String. 173 * <p> 174 * Note that there are additional 175 * methods prefixed with 'update' that may be of interest, so be 176 * sure to read the API documentation for Cell, and subclasses of Cell, closely. 177 * <p> 178 * Of course, we can also use the binding API rather than overriding the 179 * 'update' methods. Shown below is a very trivial example of how this could 180 * be achieved. 181 * 182 * 183 * <pre> 184 * public class BoundLabelCell extends ListCell<String> { 185 * 186 * public TextFieldCell() { 187 * textProperty().bind(itemProperty()); 188 * } 189 * } 190 * </pre> 191 * 192 * <h3>Key Design Goals</h3> 193 * <ul> 194 * <li>Both time and memory efficient for large data sets</li> 195 * <li>Easy to build and use libraries for custom cells</li> 196 * <li>Easy to customize cell visuals</li> 197 * <li>Easy to customize display formatting (12.34 as $12.34 or 1234% etc)</li> 198 * <li>Easy to extend for custom visuals</li> 199 * <li>Easy to have "panels" of data for the visuals</li> 200 * <li>Easy to animate the cell size or other properties</li> 201 * </ul> 202 * 203 * <h3>Key Use Cases</h3> 204 * Following are a number of key use cases used to drive the Cell API design, 205 * along with code examples showing how those use cases are satisfied by this 206 * API. This is by no means to be considered the definitive list of capabilities 207 * or features supported, but rather, to provide some guidance as to how to use 208 * the Cell API. The examples below are focused on the ListView, but the same 209 * philosophy applies to TreeCells or other kinds of cells. 210 * <p> 211 * <b>Changing the Cell's Colors</b> 212 * <p> 213 * This should be extraordinarily simple in JavaFX. Each Cell can be styled 214 * directly from CSS. So for example, if you wanted to change the default 215 * background of every cell in a ListView to be WHITE you could do the 216 * following CSS: 217 * 218 * <pre> 219 * .list-cell { 220 * -fx-padding: 3 3 3 3; 221 * -fx-background-color: white; 222 * }</pre> 223 * 224 * If you wanted to set the color of selected ListView cells to be blue, you 225 * could add this to your CSS file: 226 * 227 * <pre> 228 * .list-cell:selected { 229 * -fx-background-color: blue; 230 * }</pre> 231 * 232 * Most Cell implementations extend from {@link IndexedCell} rather than Cell. 233 * IndexedCell adds two other pseudoclass states: "odd" and "even". Using this 234 * you can get alternate row striping by doing something like this in your CSS 235 * file: 236 * 237 * <pre> 238 * .list-cell:odd { 239 * -fx-background-color: grey; 240 * }</pre> 241 * 242 * Each of these examples require no code changes. Simply update your CSS 243 * file to alter the colors. You can also use the "hover" and other 244 * pseudoclasses in CSS the same as with other controls. 245 * <p> 246 * Another approach to the first example above (formatting a list of numbers) would 247 * be to use style classes. Suppose you had an {@link ObservableList} of Numbers 248 * to display in a ListView and wanted to color all of the negative values red 249 * and all positive or 0 values black. 250 * One way to achieve this is with a custom cellFactory which changes the 251 * styleClass of the Cell based on whether the value is negative or positive. This 252 * is as simple as adding code to test if the number in the cell is negative, 253 * and adding a "negative" styleClass. If the number is not negative, the "negative" 254 * string should be removed. This approach allows for the colors to be defined 255 * from CSS, allowing for simple customization. The CSS file would then include 256 * something like the following: 257 * 258 * <pre> 259 * .list-cell { 260 * -fx-text-fill: black; 261 * } 262 * 263 * .list-cell .negative { 264 * -fx-text-fill: red; 265 * }</pre> 266 * 267 * 268 * @param <T> The type of the item contained within the Cell. 269 * 270 */ 271public class Cell<T> extends Labeled { 272 273 /*************************************************************************** 274 * * 275 * Constructors * 276 * * 277 **************************************************************************/ 278 279 /** 280 * Creates a default Cell with the default style class of 'cell'. 281 */ 282 public Cell() { 283 setText(null); // default to null text, to match the null item 284 // focusTraversable is styleable through css. Calling setFocusTraversable 285 // makes it look to css like the user set the value and css will not 286 // override. Initializing focusTraversable by calling set on the 287 // CssMetaData ensures that css will be able to override the value. 288 ((StyleableProperty)focusTraversableProperty()).applyStyle(null, Boolean.FALSE); 289 getStyleClass().addAll(DEFAULT_STYLE_CLASS); 290 291 /** 292 * Indicates whether or not this cell has focus. For example, a 293 * ListView defines zero or one cell as being the "focused" cell. This cell 294 * would have focused set to true. 295 */ 296 super.focusedProperty().addListener(new InvalidationListener() { 297 @Override public void invalidated(Observable property) { 298 pseudoClassStateChanged(PSEUDO_CLASS_FOCUSED, isFocused()); // TODO is this necessary?? 299 300 // The user has shifted focus, so we should cancel the editing on this cell 301 if (!isFocused() && isEditing()) { 302 cancelEdit(); 303 } 304 } 305 }); 306 307 // initialize default pseudo-class state 308 pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, true); 309 } 310 311 312 313 /*************************************************************************** 314 * * 315 * Properties * 316 * * 317 **************************************************************************/ 318 319 // --- item 320 private ObjectProperty<T> item = new SimpleObjectProperty<T>(this, "item"); 321 322 /** 323 * The data value associated with this Cell. This value is set by the 324 * virtualized Control when the Cell is created or updated. This represents 325 * the raw data value. 326 * 327 * <p>This value should only be set in subclasses of Cell by the virtualised 328 * user interface controls that know how to properly work with the Cell 329 * class. 330 */ 331 public final ObjectProperty<T> itemProperty() { return item; } 332 333 /** 334 * Sets the item to the given value - should not be called directly as the 335 * item is managed by the virtualized control. 336 */ 337 public final void setItem(T value) { item.set(value); } 338 339 /** 340 * Returns the data value associated with this Cell. 341 */ 342 public final T getItem() { return item.get(); } 343 344 345 346 // --- empty 347 private ReadOnlyBooleanWrapper empty = new ReadOnlyBooleanWrapper(true) { 348 @Override protected void invalidated() { 349 final boolean active = get(); 350 pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, active); 351 pseudoClassStateChanged(PSEUDO_CLASS_FILLED, !active); 352 } 353 354 @Override 355 public Object getBean() { 356 return Cell.this; 357 } 358 359 @Override 360 public String getName() { 361 return "empty"; 362 } 363 }; 364 365 /** 366 * A property used to represent whether the cell has any contents. 367 * If true, then the Cell contains no data and is not associated with any 368 * data item in the virtualized Control. 369 * 370 * <p>When a cell is empty, it can be styled differently via the 'empty' 371 * CSS pseudo class state. For example, it may not receive any 372 * alternate row highlighting, or it may not receive hover background 373 * fill when hovered. 374 */ 375 public final ReadOnlyBooleanProperty emptyProperty() { return empty.getReadOnlyProperty(); } 376 377 private void setEmpty(boolean value) { empty.set(value); } 378 379 /** 380 * Returns a boolean representing whether the cell is considered to be empty 381 * or not. 382 */ 383 public final boolean isEmpty() { return empty.get(); } 384 385 386 387 // --- selected 388 private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper() { 389 @Override protected void invalidated() { 390 pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, get()); 391 } 392 393 @Override 394 public Object getBean() { 395 return Cell.this; 396 } 397 398 @Override 399 public String getName() { 400 return "selected"; 401 } 402 }; 403 404 /** 405 * Indicates whether or not this cell has been selected. For example, a 406 * ListView defines zero or more cells as being the "selected" cells. 407 */ 408 public final ReadOnlyBooleanProperty selectedProperty() { return selected.getReadOnlyProperty(); } 409 410 void setSelected(boolean value) { selected.set(value); } 411 412 /** 413 * Returns whether this cell is currently selected or not. 414 * @return True if the cell is selected, false otherwise. 415 */ 416 public final boolean isSelected() { return selected.get(); } 417 418 419 420 // --- Editing 421 private ReadOnlyBooleanWrapper editing; 422 423 private void setEditing(boolean value) { 424 editingPropertyImpl().set(value); 425 } 426 427 /** 428 * Represents whether the cell is currently in its editing state or not. 429 */ 430 public final boolean isEditing() { 431 return editing == null ? false : editing.get(); 432 } 433 434 /** 435 * Property representing whether this cell is currently in its editing state. 436 */ 437 public final ReadOnlyBooleanProperty editingProperty() { 438 return editingPropertyImpl().getReadOnlyProperty(); 439 } 440 441 private ReadOnlyBooleanWrapper editingPropertyImpl() { 442 if (editing == null) { 443 editing = new ReadOnlyBooleanWrapper(this, "editing"); 444 } 445 return editing; 446 } 447 448 449 450 // --- Editable 451 private BooleanProperty editable; 452 453 /** 454 * Allows for certain cells to not be able to be edited. This is useful in 455 * cases where, say, a List has 'header rows' - it does not make sense for 456 * the header rows to be editable, so they should have editable set to 457 * false. 458 * 459 * @param value A boolean representing whether the cell is editable or not. 460 * If true, the cell is editable, and if it is false, the cell can not 461 * be edited. 462 */ 463 public final void setEditable(boolean value) { 464 editableProperty().set(value); 465 } 466 467 /** 468 * Returns whether this cell is allowed to be put into an editing state. 469 */ 470 public final boolean isEditable() { 471 return editable == null ? true : editable.get(); 472 } 473 474 /** 475 * A property representing whether this cell is allowed to be put into an 476 * editing state. By default editable is set to true in Cells (although for 477 * a subclass of Cell to be allowed to enter its editing state, it may have 478 * to satisfy additional criteria. For example, ListCell requires that the 479 * ListView {@link ListView#editableProperty() editable} property is also 480 * true. 481 */ 482 public final BooleanProperty editableProperty() { 483 if (editable == null) { 484 editable = new SimpleBooleanProperty(this, "editable", true); 485 } 486 return editable; 487 } 488 489 490 491 /*************************************************************************** 492 * * 493 * Public API * 494 * * 495 **************************************************************************/ 496 497 /** 498 * Call this function to transition from a non-editing state into an editing 499 * state, if the cell is editable. If this cell is already in an editing 500 * state, it will stay in it. 501 */ 502 public void startEdit() { 503 if (isEditable() && !isEditing() && !isEmpty()) { 504 setEditing(true); 505 } 506 } 507 508 /** 509 * Call this function to transition from an editing state into a non-editing 510 * state, without saving any user input. 511 */ 512 public void cancelEdit() { 513 if (isEditing()) { 514 setEditing(false); 515 } 516 } 517 518 /** 519 * Call this function to transition from an editing state into a non-editing 520 * state, and in the process saving any user input. 521 * 522 * @param newValue The value as input by the end user, which should be 523 * persisted in the relevant way given the data source underpinning the 524 * user interface. 525 */ 526 public void commitEdit(T newValue) { 527 if (isEditing()) { 528 setEditing(false); 529 } 530 } 531 532 533 534 /*************************************************************************** 535 * * 536 * Expert API * 537 * * 538 **************************************************************************/ 539 540 /** 541 * <p> 542 * Updates the item associated with this Cell. This method should <strong> 543 * only</strong> be called by Skin implementations of ListView, TableView, 544 * TreeView, or other controls using Cells. It is not intended to be called 545 * by application developers. 546 * </p> 547 * <p> 548 * Because <code>null</code> is a perfectly valid value in the application 549 * domain, Cell needs some way to distinguish whether or not the cell 550 * actually holds a value. The <code>empty</code> flag indicates this. 551 * It is an error to supply a non-null <code>item</code> but a true value for 552 * <code>empty</code>. 553 * </p> 554 * @param item The new item for the cell 555 * @param empty whether or not this cell represents data from the list. If it 556 * is empty, then it does not represent any domain data, but is a cell 557 * being used to render an "empty" row. 558 * @expert 559 */ 560 protected void updateItem(T item, boolean empty) { 561 if (isEditing()) cancelEdit(); 562 setItem(item); 563 setEmpty(empty); 564 if (empty && isSelected()) { 565 updateSelected(false); 566 } 567 } 568 569 /** 570 * Updates whether this cell is in a selected state or not. 571 * @expert 572 * @param selected whether or not to select this cell. 573 */ 574 public void updateSelected(boolean selected) { 575 if (selected && isEmpty()) return; 576 setSelected(selected); 577 } 578 579 private AccessibleListItem accListItem ; 580 /** 581 * @treatAsPrivate implementation detail 582 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 583 */ 584 @Deprecated @Override public AccessibleProvider impl_getAccessible() { 585 if( accListItem == null) 586 accListItem = new AccessibleListItem(this); 587 return (AccessibleProvider)accListItem ; 588 } 589 590 591 /*************************************************************************** 592 * * 593 * Stylesheet Handling * 594 * * 595 **************************************************************************/ 596 597 private static final String DEFAULT_STYLE_CLASS = "cell"; 598 private static final PseudoClass PSEUDO_CLASS_SELECTED = 599 PseudoClass.getPseudoClass("selected"); 600 private static final PseudoClass PSEUDO_CLASS_FOCUSED = 601 PseudoClass.getPseudoClass("focused"); 602 private static final PseudoClass PSEUDO_CLASS_EMPTY = 603 PseudoClass.getPseudoClass("empty"); 604 private static final PseudoClass PSEUDO_CLASS_FILLED = 605 PseudoClass.getPseudoClass("filled"); 606 607 /** 608 * Most Controls return true for focusTraversable, so Control overrides 609 * this method to return true, but Cell returns false for 610 * focusTraversable's initial value; hence the override of the override. 611 * This method is called from CSS code to get the correct initial value. 612 * @treatAsPrivate implementation detail 613 */ 614 @Deprecated @Override 615 protected /*do not make final*/ Boolean impl_cssGetFocusTraversableInitialValue() { 616 return Boolean.FALSE; 617 } 618 619}