2015-01-31 64 views
1

在工作中,我創建了一個TableView,它需要讓特定單元格同時從一種顏色閃爍到另一種顏色。使用Rectangles,FillTransitions和一個ParallelTransition相對容易,如下面的玩具示例所示。將矩形分配給FillTransition後,我將TableCell的圖形設置爲矩形。然後,我只需要從ParallelTransition中添加/刪除FillTransition,具體取決於單元格是否應該閃爍。在JavaFx中將圖形自動調整爲單元格的形狀

然而,我遇到很多困難的一個領域是想方設法將矩形縮放爲包含它作爲圖形的TableCell的大小。我遇到的問題是TableCell總會自行調整大小,使其邊界與矩形的邊界之間有空的空間。

我不得不用一種非常單調乏味的方式來解決這個問題:我必須調用setFixedCellSize來將表格的單元格高度固定到我的矩形的高度,然後通過反覆試驗將矩形向上和向左重新定位調用setTranslateX/Y,並將列的最小寬度和最小高度設置爲略低於我的矩形寬度和高度設置爲的任何值。它解決了這個問題,但我希望有一些不那麼單調和煩人的事情。

我會假設做一個或多個與電池下面的這可以避免:

  • 調用setScaleShape(真)

  • 調用setContentDisplay(ContentDisplay.GRAPHIC_ONLY)

  • 將單元格的CSS樣式設置爲包含「-fx-scale-shape:true」

可悲的是,沒有這些有任何明顯的效果......

我的問題是三舞伴:

  1. 是否有大小更好的方式分配爲圖形的小區的Shape填補細胞的界限?

  2. 爲什麼上述三種方法都沒有影響我的情況,他們的實際預期目的是什麼?他們只適用於setShape()而不是setGraphic()的形狀嗎?

  3. 是否有任何合法的原因,爲什麼JavaFx不支持設置節點的首選寬度或高度,而不是子類的區域?自動調整似乎是層次結構中所有節點應該具有通用性的東西,並且看起來很直觀,任何父節點都應該能夠在必要時指定其子節點的大小。

import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 
import javafx.animation.Animation; 
import javafx.animation.FillTransition; 
import javafx.animation.ParallelTransition; 
import javafx.application.Application; 
import javafx.beans.property.IntegerProperty; 
import javafx.beans.property.ObjectProperty; 
import javafx.beans.property.SimpleIntegerProperty; 
import javafx.beans.property.SimpleObjectProperty; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.scene.Scene; 
import javafx.scene.control.ContentDisplay; 
import javafx.scene.control.TableCell; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.control.cell.PropertyValueFactory; 
import javafx.scene.layout.Border; 
import javafx.scene.layout.BorderStroke; 
import javafx.scene.layout.BorderStrokeStyle; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 
import javafx.util.Duration; 


public class FlashingPriorityTable extends Application { 

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

    @Override 
    public void start(Stage primaryStage) throws Exception { 

    // periodically add prioritized items to an observable list 
    final ObservableList<PItem> itemList = FXCollections.observableArrayList(); 
    class ItemAdder { 
     private int state, count = 0; private final int states = 3; 
     public synchronized void addItem() { 
     state = count++ % states; 
     PItem item; 
     if(state == 0) 
      item = new PItem(Priority.LOW, count, "bob saget"); 
     else if(state == 1) 
      item = new PItem(Priority.MEDIUM, count, "use the force"); 
     else 
      item = new PItem(Priority.HIGH, count, "one of us is in deep trouble"); 
     itemList.add(item); 
     } 
    }; 
    final ItemAdder itemAdder = new ItemAdder(); 
    Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
     () -> itemAdder.addItem(), 
     0, // initial delay 
     1, // period 
     TimeUnit.SECONDS); // time unit 

    // set up a table view bound to the observable list 
    final TableColumn<PItem, Priority> priCol = new TableColumn<>("Priority"); 
    priCol.setCellValueFactory(new PropertyValueFactory<PItem, Priority>("priority")); 
    priCol.setCellFactory((col) -> new PriorityCell()); // create a blinking cell 
    priCol.setMinWidth(50); 
    priCol.setMaxWidth(50); 

    final TableColumn<PItem, Integer> indexCol = new TableColumn<>("Index"); 
    indexCol.setCellValueFactory(new PropertyValueFactory<PItem, Integer>("index")); 
    indexCol.setCellFactory((col) -> makeBorderedTextCell()); 

    final TableColumn<PItem, String> descriptionCol = new TableColumn<>("Description"); 
    descriptionCol.setCellValueFactory(new PropertyValueFactory<PItem, String>("description")); 
    descriptionCol.setCellFactory((col) -> makeBorderedTextCell()); 
    descriptionCol.setMinWidth(300); 

    final TableView<PItem> table = new TableView<>(itemList); 
    table.getColumns().setAll(priCol, indexCol, descriptionCol); 
    table.setFixedCellSize(25); 

    // display the table view 
    final Scene scene = new Scene(table); 
    primaryStage.setScene(scene); 
    primaryStage.show(); 
    } 

    // render a simple cell text and border 
    private <T> TableCell<PItem, T> makeBorderedTextCell() { 
    return new TableCell<PItem, T>() { 
     @Override protected void updateItem(T item, boolean empty) { 
     super.updateItem(item, empty); 
     if(item == null || empty) { 
      setText(null); 
     } else { 
      setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, null, null))); 
      setText(item.toString()); 
     } 
     } 
    }; 
    } 

    /* for cells labeled as high priority, render an animation that blinks (also include a border) */ 
    public static class PriorityCell extends TableCell<PItem, Priority> { 
    private static final ParallelTransition pt = new ParallelTransition(); 
    private final Rectangle rect = new Rectangle(49.5, 24); 
    private final FillTransition animation = new FillTransition(Duration.millis(100), rect); 
    public PriorityCell() { 
     rect.setTranslateX(-2.75); 
     rect.setTranslateY(-2.7); 
     animation.setCycleCount(Animation.INDEFINITE); animation.setAutoReverse(true); } 
    @Override 
    protected void updateItem(Priority priority, boolean empty) { 
     super.updateItem(priority, empty); 
     if(priority == null || empty) { 
     setGraphic(null); 
     return; 
     } 
     setGraphic(rect); 
     setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 
     setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, null, null))); 
     if(priority == Priority.HIGH) { 
     if(!pt.getChildren().contains(animation)) { 
      animation.setFromValue(Color.BLACK); 
      animation.setToValue(priority.getColor()); 
      animation.setShape(rect); 
      pt.getChildren().add(animation); 
      pt.stop(); pt.play(); 
     } 
     } else { 
     if(pt.getChildren().contains(animation)) { 
      pt.getChildren().remove(animation); 
      pt.stop(); pt.play(); 
     } 
     rect.setFill(priority.getColor()); 
     } 
    } 
    } 

    /* an item that has a priority assigned to it */ 
    public static class PItem { 
    private ObjectProperty<Priority> priority = new SimpleObjectProperty<>(); 
    private IntegerProperty index = new SimpleIntegerProperty(); 
    private StringProperty description = new SimpleStringProperty(); 

    public PItem(Priority priority, Integer index, String description) { 
     setPriority(priority); setIndex(index); setDescription(description); 
    } 

    public void setPriority(Priority priority_) { priority.set(priority_); } 
    public Priority getPriority() { return priority.get(); } 

    public void setIndex(int index_) { index.set(index_); } 
    public Integer getIndex() { return index.get(); } 

    public void setDescription(String description_) { description.set(description_); } 
    public String getDescription() { return description.get(); } 
    } 

    /* a priority */ 
    public enum Priority { 
    HIGH(Color.RED), MEDIUM(Color.ORANGE), LOW(Color.BLUE); 
    private final Color color; 
    private Priority(Color color) { this.color = color; } 
    public Color getColor() { return color; } 
    } 
} 

回答

2

關於:

的TableCell的總是會調整自身有其邊界和矩形邊界之間的空白區域。

這是因爲根據modena,單元默認有2 px的填充。CSS:

.table-cell { 
    -fx-padding: 0.166667em; /* 2px, plus border adds 1px */ 
    -fx-cell-size: 2.0em; /* 24 */ 
} 

一個簡單的方法來擺脫這種空白的只是覆蓋它:

@Override 
protected void updateItem(Priority priority, boolean empty) { 
    super.updateItem(priority, empty); 
    ... 
    setGraphic(rect); 
    setStyle("-fx-padding: 0;"); 
    ... 
} 

下一個問題你也別說是自動調整大小。據的JavaDoc,爲Node.isResizable()

如果該方法返回true,則父將調整由佈局通過期間調用node.resize(寬度,高度)的節點(理想地其大小範圍內)。所有區域,控件和WebView都是可調整大小的類,取決於父母在應用所有尺寸和CSS樣式信息時在佈局期間調整它們的大小。 如果此方法返回false,則父級無法在佈局過程中調整其大小(resize()爲空操作),並且它應返回其最小值,首選值和最大值的layoutBounds。組,文本和所有形狀不可調整大小,因此取決於應用程序通過設置適當的屬性(例如矩形的寬度/高度,文本上的文本等)來確定其大小。佈局過程中,不可調整大小的節點仍可能重新定位。

顯然,Rectangle是不可調整大小,但這並不意味着你不能調整它的大小:如果佈局不爲你做的,你需要照顧它。

所以一個簡單的解決方案可以在矩形的尺寸結合這些細胞(減去2個像素單元格邊框):

private final Rectangle rect = new Rectangle(); 

@Override 
protected void updateItem(Priority priority, boolean empty) { 
    super.updateItem(priority, empty); 
    if(priority == null || empty) { 
     setGraphic(null); 
     return; 
    } 
    setGraphic(rect); 
    setStyle("-fx-padding: 0;"); 
    rect.widthProperty().bind(widthProperty().subtract(2)); 
    rect.heightProperty().bind(heightProperty().subtract(2)); 
    ... 
} 

請注意,您將不再需要翻譯的矩形,除非你想給它一個固定的大小,否則不需要修正單元格的大小和列的寬度。

還要注意,setShape()是用來改變單元格的形狀,默認情況下它已經是一個矩形。

這可能會回答你的前兩個問題。對於第三個,有時你希望節點總是可以調整大小......但是如果是這種情況,我們會遇到相反的問題,試圖讓它們受到限制...

+0

對javafx有些新鮮我不熟悉modena.css在你提到它之前有絲毫的意義。這絕對是一個很好的文件來了解。只是做你說的絕對是訣竅!再加上對CSS的理解,Javafx當然非常強大,即使某些API有時看起來有點受限!如果明天某個時候我沒有收到其他答覆,我會將你的答案標記爲答案。 – 2015-02-01 00:12:45