2016-05-13 129 views
0

我有一個TableView並希望允許用戶禁用單獨的行和列。JavaFX:'禁用'TableView行和列

表格數據是由API提供的,所以行和列的確切數量在運行時間之前是未知的,但通常是4-30行和15-25列。理想情況下,第一列/行將是一個標題,最後一行將是頁腳(即:上面/側面數據的摘要)。一旦用戶執行了一些其他操作,他們應該能夠禁用行或列 - 最好是通過單擊標題 - 並從摘要中刪除這些數據。

行/列仍應可見。排序不是必需的。編輯不是必需的。

我知道JavaFX只支持列標題,而不是行標題,當然也不支持頁腳。這使我認爲最好的選擇是:

  • Remove the TableView headers完全。
  • 將第一個和最後一個列/行設置爲頁眉/頁腳。
  • setCellSelectionEnabled爲true
  • 如果所選單元格是標題單元格,請「禁用」該行或列;可能是通過CSS樣式的組合並從摘要中刪除單元格值。
  • 如果所選單元格不是標題單元格,請執行其他操作。
  • 如果再次單擊標題單元格,請重新啓用行或列。

這是最好的方法嗎?有沒有第三方庫可以讓工作更輕鬆?它甚至值得一起傾銷TableView並使用GridPane?

謝謝。

+1

你看過函數'setRowFactory'(來自TableView)和'setCellFactory'(來自TableColumn)嗎?網上有很多例子,它允許程序員通過設置監聽器來定製單元/列行爲。由此,例如,如果點擊列/單元格,則可以執行操作。 – Jacks

+0

我見過這些。我的問題可能應該包括對他們的引用,因爲如果沒有某種捷徑,那麼這兩種方法可能是必不可少的。 – AJR

+1

據我所知,沒有真正的「捷徑」。只要舉一個這些函數的例子並重寫'call()'函數就可以根據需要創建單元格或列。您還可以在設置工廠時添加監聽器,以便在您的單元格(或單元格內的單元格,或者單元格內的單元)被單擊時觸發某個操作。希望這有助於,如果沒有,我可以給你一個小例子。 – Jacks

回答

1

正如@傑克斯指出的那樣,似乎並沒有一個捷徑。所以這裏是我解決問題的方法。

首先,我們hide the table header並允許單獨選擇單元格。 updateTable()方法每n秒;它模擬了一個ScheduledService API調用。 MyRow對象只是一個ObservableList<TableCell>的包裝器,我們使用它,因爲我們不知道有多少列會在運行時出現。

COLUMN_HEADER, ROW_FOOTER等是枚舉。

private void updateTable() { 
    Pane header = (Pane) myTable.lookup("TableHeaderRow"); 
    header.setVisible(false); 
    myTable.getSelectionModel().setCellSelectionEnabled(true); 
    myTable.setLayoutY(-header.getHeight()); 
    myTable.autosize(); 

    List<MyRow> list = new CopyOnWriteArrayList(); 
    Integer maxRows = 5, maxColumns = 5; // For example 
    MyRow row; 
    for (int rowId = 0; rowId <= maxRows; rowId++) { 
     Boolean isEmpty = myTable.getColumns().isEmpty(); 
     row = new MyRow(); 
     Integer total = 0; 
     for (int colId = 0; colId <= maxColumns; colId++) { 
      if (isEmpty) { 
       myTable.getColumns().add(createReactiveColumn()); 
      } 
      TableCell cell = new MyTableCell(); 
      if (rowId == 0 && colId == 0) { 
       // Top-left cell 
      } else if (rowId == 0) { 
       // Column title 
       cell.setUserData(COLUMN_HEADER); 
       cell.setText("CH" + Integer.toString(colId)); 
      } else if (colId == 0) { 
       // Row title 
       cell.setUserData(ROW_HEADER); 
       cell.setText("RH" + Integer.toString(rowId)); 
      } else if (colId == maxColumns) { 
       // Row 'footer' 
       cell.setUserData(ROW_FOOTER); 
       cell.setText(Integer.toString(total)); 
      } else if (rowId == maxRows) { 
       // Column 'footer' 
       cell.setUserData(COLUMN_FOOTER); 
       cell.setText("CF" + Integer.toString(rowId)); 
      } else { 
       // Data 
       Integer r = new Random().nextInt(9); // Simulate API data 
       if (!this.disabledColumns.contains(myTable.getColumns().get(colId))) { 
        total += r; // Sum of each enabled cell 
       } 
       cell.setUserData(DATA); 
       cell.setText(Integer.toString(r)); 
      } 
      row.add(cell); 
     } 
     list.add(row); 
    } 
    myTable.getItems().setAll(list); 
} 

TableColumn只是採用setCellFactory()方法:

private TableColumn<MyRow, String> createReactiveColumn() { 

    TableColumn<MyRow, String> column = new TableColumn<MyRow, String>(); 
    column.setCellFactory(new Callback<TableColumn<MyRow, String>, TableCell<MyRow, String>>() { 
     @Override 
     public TableCell<MyRow, String> call(TableColumn<MyRow, String> param) { 
      return new MyTableCell(); 
     } 
    }); 
    column.setSortable(FALSE); 
    column.setMinWidth(40d); 

    return column; 
} 

MyTableCell增加了MOUSE_CLICKED事件處理程序,以及複製從更新TableCell文本。

private class MyTableCell extends TableCell<MyRow, String> { 

    Boolean hasEventHandler = FALSE; 

    @Override 
    protected void updateItem(String item, boolean empty) { 
     super.updateItem(item, empty); 
     if (!empty && getTableRow() != null && getTableRow().getItem() != null) { 
      MyRow er = (MyRow) getTableRow().getItem(); 
      TableCell cell = er.get(getTableView().getColumns().indexOf(getTableColumn())); 
      this.setText(cell.getText()); 
      if (cell.getUserData() instanceof MyTableCellEnum) { 
       MyTableCellEnumcellType = (MyTableCellEnum) cell.getUserData(); 
       if (null != cellType && !hasEventHandler) { 
        switch (cellType) { 
         case COLUMN_HEADER: 
          addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { 
           @Override 
           public void handle(MouseEvent event) { 
            toggleColumn(getTableColumn()); 
           } 
          }); 
          hasEventHandler = TRUE; 
          break; 
         case ROW_HEADER: 
          addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { 
           @Override 
           public void handle(MouseEvent event) { 
            toggleRow(getTableRow()); 
           } 
          }); 
          hasEventHandler = TRUE; 
          break; 
         case DATA: 
          addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { 
           @Override 
           public void handle(MouseEvent event) { 
            // Do other action on selection 
           } 
          }); 
          break; 
         default: 
          break; 
        } 
       } 
      } 
     } 
    } 
} 

最後,撥動方法只是換一個集殘疾人TableRow/TableColumn對象之間的行/列,並更新CSS:

private void toggleRow(TableRow tableRow) { 
    if (this.disabledRows.contains(tableRow)) { 
     this.disabledRows.remove(tableRow); 
     tableRow.getStyleClass().remove("cell-disabled"); 
    } else { 
     this.disabledRows.add(tableRow); 
     tableRow.getStyleClass().add("cell-disabled"); 
    } 
} 

private void toggleColumn(TableColumn tableColumn) { 
    System.out.println(tableColumn); 
    if (this.disabledColumns.contains(tableColumn)) { 
     this.disabledColumns.remove(tableColumn); 
     tableColumn.getStyleClass().remove("cell-disabled"); 
    } else { 
     this.disabledColumns.add(tableColumn); 
     tableColumn.getStyleClass().add("cell-disabled"); 
    } 
} 

它的工作原理,我主要是喜歡它。包含hasEventHandler布爾值並不理想:它看起來有點冒險,但我無法找到任何其他註冊事件處理程序的方法,只能確保其實際工作。

歡迎提出意見和改進建議。如果有更好的想法,我會在接受我自己的答案前幾天離開它。