JavaFX:'禁用'TableView行和列

问题描述:

我有一个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

正如@杰克斯指出的那样,似乎并没有一个捷径。所以这里是我解决问题的方法。

首先,我们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布尔值并不理想:它看起来有点冒险,但我无法找到任何其他注册事件处理程序的方法,只能确保其实际工作。

欢迎提出意见和改进建议。如果有更好的想法,我会在接受我自己的答案前几天离开它。