2011-10-24 72 views
21

鏈接到問題:JavaFX 2: Save edit in TableCellUITableView - 更好的編輯通過綁定?

似乎有被分配到建立一個可編輯的tableview需要的管道 - 即捕獲的所有事件的每個文本框(獲得/損失的焦點,從文本框Tab鍵程,commiting從編輯將textField添加到基礎數據模型中),並覆蓋TableCell中的幾個方法。

建立編輯的默認行爲 - 單元格中的雙擊 - 對於我或標準表格控件的用戶來說似乎並不熟悉。我只想點擊單元格,然後開始輸入大部分內容。

那裏有沒有完全實施的例子嗎?請添加你的設計或這些生物的評論。

回答

22

而是響應幾個事件在TableCellTableColumn水平發起小區的編輯,併成功地更新單元格的基礎數據 - 而不是我們提供了一個自定義單元格工廠,並覆蓋在細胞中updateItem()方法和「綁定」 textField的textProperty直接映射到TableView(本例中爲StringProperty)的數據模型內的屬性。我添加了其他美學元素,使單元格內的textField看起來無縫並響應懸停和集中狀態。

所有的魔法發生在updateItem()方法。你要跟蹤的文本字段和它必然的 - 在的TableView API「再循環」 TableCells以減少內存消耗:

@Override 
    protected void updateItem(String item, boolean empty) { 
     super.updateItem(item, empty);   
     if(!empty) { 
     // Show the Text Field 
     this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 

     // Retrieve the actual String Property that should be bound to the TextField 
     // If the TextField is currently bound to a different StringProperty 
     // Unbind the old property and rebind to the new one 
     ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex()); 
     SimpleStringProperty sp = (SimpleStringProperty)ov; 

     if(this.boundToCurrently==null) { 
      this.boundToCurrently = sp; 
      this.textField.textProperty().bindBidirectional(sp); 
     } 
     else { 
      if(this.boundToCurrently != sp) { 
       this.textField.textProperty().unbindBidirectional(this.boundToCurrently); 
       this.boundToCurrently = sp; 
       this.textField.textProperty().bindBidirectional(this.boundToCurrently); 
      } 
     } 
     System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue()); 
     //this.textField.setText(item); // No longer need this!!! 
     } 
     else { 
     this.setContentDisplay(ContentDisplay.TEXT_ONLY); 
     } 
    } 

這裏是4列的表的一個完整的例子,所有綁定到底層textField。只要鍵入textField,Observable列表中的基礎數據模型就會更新:

package tablevieweditingwithbinding; 

import javafx.application.Application; 
import javafx.beans.property.ObjectProperty; 
import javafx.beans.property.SimpleObjectProperty; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.geometry.Insets; 
import javafx.geometry.Pos; 
import javafx.scene.Node; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.control.cell.PropertyValueFactory; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.HBox; 
import javafx.stage.Stage; 
import javafx.util.Callback; 

/** 
* 
* @author jKaufmann 
*/ 
public class TableViewEditingWithBinding extends Application { 
    public static class TableData { 
    private SimpleStringProperty firstName, lastName, phone, email; 
    private ObjectProperty<SimpleStringProperty> firstNameObject; 

    public TableData(String firstName, String lastName, String phone, String email) { 
     this.firstName = new SimpleStringProperty(firstName); 
     this.firstNameObject = new SimpleObjectProperty(firstNameObject); 
     this.lastName = new SimpleStringProperty(lastName); 
     this.phone = new SimpleStringProperty(phone); 
     this.email = new SimpleStringProperty(email); 
    } 

    public String getEmail() { 
     return email.get(); 
    } 
    public void setEmail(String email) { 
     this.email.set(email); 
    } 
    public SimpleStringProperty emailProperty() { return email; } 

    public String getFirstName() { 
     return firstName.get(); 
    } 
    public SimpleStringProperty getFirstNameObject() { 
     return firstNameObject.get(); 
    } 
    public void setFirstNameObject(SimpleStringProperty firstNameObject) { 
     this.firstNameObject.set(firstNameObject); 
    } 
    public ObjectProperty<SimpleStringProperty> firstNameObjectProperty() { return firstNameObject; } 

    public void setFirstName(String firstName) { 
     this.firstName.set(firstName); 
    } 
    public SimpleStringProperty firstNameProperty() { 
     return firstName; 
    } 
    public String getLastName() { 
     return lastName.get(); 
    } 
    public void setLastName(String lastName) { 
     this.lastName.set(lastName); 
    } 
    public SimpleStringProperty lastNameProperty() { return lastName; } 

    public String getPhone() { 
     return phone.get(); 
    } 
    public void setPhone(String phone) { 
     this.phone.set(phone); 
    } 
    public SimpleStringProperty phoneProperty() { return phone; } 

    } 
    public static class TextFieldCellFactory 
    implements Callback<TableColumn<TableData,String>,TableCell<TableData,String>> { 

    @Override 
    public TableCell<TableData, String> call(TableColumn<TableData, String> param) { 
     TextFieldCell textFieldCell = new TextFieldCell(); 
     return textFieldCell; 
    } 

    public static class TextFieldCell extends TableCell<TableData,String> { 
     private TextField textField; 
     private StringProperty boundToCurrently = null; 

     public TextFieldCell() { 
      String strCss; 
      // Padding in Text field cell is not wanted - we want the Textfield itself to "be" 
      // The cell. Though, this is aesthetic only. to each his own. comment out 
      // to revert back. 
      strCss = "-fx-padding: 0;"; 


      this.setStyle(strCss); 

      textField = new TextField(); 

      // 
      // Default style pulled from caspian.css. Used to play around with the inset background colors 
      // ---trying to produce a text box without borders 
      strCss = "" + 
        //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" + 
        "-fx-background-color: -fx-control-inner-background;" + 
        //"-fx-background-insets: 0, 1, 2;" + 
        "-fx-background-insets: 0;" + 
        //"-fx-background-radius: 3, 2, 2;" + 
        "-fx-background-radius: 0;" + 
        "-fx-padding: 3 5 3 5;" + /*Play with this value to center the text depending on cell height??*/ 
        //"-fx-padding: 0 0 0 0;" + 
        "-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);" + 
        "-fx-cursor: text;" + 
        ""; 
      // Focused and hover states should be set in the CSS. This is just a test 
      // to see what happens when we set the style in code 
      textField.focusedProperty().addListener(new ChangeListener<Boolean>() { 

      public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 
       TextField tf = (TextField)getGraphic(); 
       String strStyleGotFocus = "-fx-background-color: purple, -fx-text-box-border, -fx-control-inner-background;" + 
          "-fx-background-insets: -0.4, 1, 2;" + 
          "-fx-background-radius: 3.4, 2, 2;"; 
       String strStyleLostFocus = //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" + 
            "-fx-background-color: -fx-control-inner-background;" + 
           //"-fx-background-insets: 0, 1, 2;" + 
            "-fx-background-insets: 0;" + 
           //"-fx-background-radius: 3, 2, 2;" + 
            "-fx-background-radius: 0;" + 
            "-fx-padding: 3 5 3 5;" + /**/ 
            //"-fx-padding: 0 0 0 0;" + 
            "-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);" + 
            "-fx-cursor: text;" + 
            ""; 
       if(newValue.booleanValue()) 
        tf.setStyle(strStyleGotFocus); 
       else 
        tf.setStyle(strStyleLostFocus);    
      } 
      }); 
      textField.hoverProperty().addListener(new ChangeListener<Boolean>() { 

      public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 
       TextField tf = (TextField)getGraphic(); 
       String strStyleGotHover = "-fx-background-color: derive(purple,90%), -fx-text-box-border, derive(-fx-control-inner-background, 10%);" + 
          "-fx-background-insets: 1, 2.8, 3.8;" + 
          "-fx-background-radius: 3.4, 2, 2;"; 
       String strStyleLostHover = //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" + 
            "-fx-background-color: -fx-control-inner-background;" + 
           //"-fx-background-insets: 0, 1, 2;" + 
            "-fx-background-insets: 0;" + 
           //"-fx-background-radius: 3, 2, 2;" + 
            "-fx-background-radius: 0;" + 
            "-fx-padding: 3 5 3 5;" + /**/ 
            //"-fx-padding: 0 0 0 0;" + 
            "-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);" + 
            "-fx-cursor: text;" + 
            ""; 
       String strStyleHasFocus = "-fx-background-color: purple, -fx-text-box-border, -fx-control-inner-background;" + 
          "-fx-background-insets: -0.4, 1, 2;" + 
          "-fx-background-radius: 3.4, 2, 2;"; 
       if(newValue.booleanValue()) { 
        tf.setStyle(strStyleGotHover); 
       } 
       else { 
        if(!tf.focusedProperty().get()) { 
        tf.setStyle(strStyleLostHover); 
        } 
        else { 
        tf.setStyle(strStyleHasFocus); 
        } 
       } 

      } 
      }); 
      textField.setStyle(strCss); 
      this.setGraphic(textField); 
     } 

     @Override 
     protected void updateItem(String item, boolean empty) { 
      super.updateItem(item, empty);   
      if(!empty) { 
      // Show the Text Field 
      this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 

      // Retrieve the actual String Property that should be bound to the TextField 
      // If the TextField is currently bound to a different StringProperty 
      // Unbind the old property and rebind to the new one 
      ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex()); 
      SimpleStringProperty sp = (SimpleStringProperty)ov; 

      if(this.boundToCurrently==null) { 
       this.boundToCurrently = sp; 
       this.textField.textProperty().bindBidirectional(sp); 
      } 
      else { 
       if(this.boundToCurrently != sp) { 
        this.textField.textProperty().unbindBidirectional(this.boundToCurrently); 
        this.boundToCurrently = sp; 
        this.textField.textProperty().bindBidirectional(this.boundToCurrently); 
       } 
      } 
      System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue()); 
      //this.textField.setText(item); // No longer need this!!! 
      } 
      else { 
      this.setContentDisplay(ContentDisplay.TEXT_ONLY); 
      } 
     } 

    } 
    } 
    public static void printNodeKidsRecursively(Node n, String tabs) {  
    String toTab = tabs == null ? "" : tabs; 
    String msg1 = toTab + n.getClass().getName(); 
    String msg2 = ":" + n.toString(); 

    // Spit out and text data from Text classes 
    if(javafx.scene.text.Text.class.isAssignableFrom(n.getClass())) { 
     javafx.scene.text.Text t = (javafx.scene.text.Text)n; 
     msg2 += " \"" +t.getText() + "\""; 
    } 

    // if this Node does not extend from Parent, then it can't have kids. 
    if(!Parent.class.isAssignableFrom(n.getClass())) { 
     System.out.println(msg1+msg2); 
     return; 
    } 

    Parent p = (Parent)n; 
    System.out.println(toTab + n.getClass().getName() + 
      "(KIDS=" + 
      Integer.toString(p.getChildrenUnmodifiable().size()) + ")" + 
      msg2); 

    ObservableList<Node> kids = p.getChildrenUnmodifiable(); 
    toTab +=" "; 
    for(Node n2 : kids) { 
     printNodeKidsRecursively(n2, toTab); 
    } 
    } 

    private final TableView<TableData> table = new TableView<TableData>(); 
    final ObservableList<TableData> ol = 
     FXCollections.observableArrayList(
     new TableData("Wilma","Flintstone","555-123-4567","[email protected]"), 
     new TableData("Fred","Flintstone","555-123-4567","[email protected]"), 
     new TableData("Barney","Flintstone","555-123-4567","[email protected]"), 
     new TableData("Bugs","Bunny","555-123-4567","[email protected]"), 
     new TableData("Yo","Sam","555-123-4567","[email protected]"), 
     new TableData("Tom","","555-123-4567","[email protected]"), 
     new TableData("Jerry","","555-123-4567","[email protected]"), 
     new TableData("Peter","Pan","555-123-4567","[email protected]"), 
     new TableData("Daffy","Duck","555-123-4567","[email protected]"), 
     new TableData("Tazmanian","Devil","555-123-4567","[email protected]"), 
     new TableData("Mickey","Mouse","555-123-4567","[email protected]"), 
     new TableData("Mighty","Mouse","555-123-4567","[email protected]") 
     ); 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
    Application.launch(args); 
    } 

    @Override 
    public void start(Stage Stage) {  
    Stage.setTitle("Editable Table"); 
    BorderPane borderPane = new BorderPane(); 
    Scene scene = new Scene(borderPane, 800, 600); 

    // top of border pane 
    Button b1 = new Button("Print Scene Graph for table Node"); 
    Button b2 = new Button("Change value in table list"); 
    HBox hbox = new HBox(10); 
    hbox.setStyle("-fx-background-color: #336699"); 
    hbox.setAlignment(Pos.BOTTOM_CENTER); 
    HBox.setMargin(b2, new Insets(10,0,10,0)); 
    HBox.setMargin(b1, new Insets(10,0,10,0)); 
    hbox.getChildren().addAll(b1,b2); 
    borderPane.setTop(hbox); 
    BorderPane.setAlignment(hbox, Pos.CENTER); 

    // Button Events 
    b1.setOnAction(new EventHandler<ActionEvent>() { 

     public void handle(ActionEvent event) { 
      printNodeKidsRecursively(table,""); 
     } 
    }); 
    b2.setOnAction(new EventHandler<ActionEvent>() { 

     @Override 
     public void handle(ActionEvent event) { 
     String curFirstName = ol.get(0).getFirstName(); 
     if(curFirstName.contentEquals("Jason")) 
      ol.get(0).setFirstName("Paul"); 
     else 
      ol.get(0).setFirstName("Jason"); 
     } 
    }); 

    table.setItems(ol); 
    borderPane.setCenter(table); 
    BorderPane.setAlignment(table, Pos.CENTER); 
    BorderPane.setMargin(table, new Insets(25)); 

    // Add columns 
    TableColumn<TableData,String> c1 = new TableColumn<TableData,String>("FirstName"); 
    c1.setCellValueFactory(new PropertyValueFactory<TableData,String>("firstName")); 
    c1.setCellFactory(new TextFieldCellFactory()); 

    TableColumn<TableData,String> c2 = new TableColumn<TableData,String>("LastName"); 
    c2.setCellValueFactory(new PropertyValueFactory<TableData,String>("lastName")); 
    c2.setCellFactory(new TextFieldCellFactory()); 

    TableColumn<TableData,String> c3 = new TableColumn<TableData,String>("Phone"); 
    c3.setCellValueFactory(new PropertyValueFactory<TableData,String>("phone")); 
    c3.setCellFactory(new TextFieldCellFactory()); 

    TableColumn<TableData,String> c4 = new TableColumn<TableData,String>("Email"); 
    c4.setCellValueFactory(new PropertyValueFactory<TableData,String>("email")); 
    c4.setCellFactory(new TextFieldCellFactory()); 

    table.getColumns().addAll(c1,c2,c3,c4); 

    Stage.setScene(scene); 
    Stage.show(); 

    } 
} 
+0

非常有趣的技術!你對大桌子上的表現有什麼經驗嗎?另外,您是否有恢復單元格/行選擇的解決方案?現在,這個解決方案提供了一些獨特的東西,但不幸的是,它也帶走了表格的一些基本功能...... – haraldK

+0

幹得好,但看起來你不能用這種解決方案處理模型中的空對象。 –