【问题标题】:JavaFX, how to freeze the position of some columns in TableViewJavaFX,如何冻结TableView中某些列的位置
【发布时间】:2013-07-15 14:43:21
【问题描述】:

这个想法是:在 N 列的 TableView 上,即使您使用水平滚动条,前 M 列也始终可见。

唯一符合我要求的就是这个Binding two tableviews together such that they scroll in sync。 在我看来,将两张桌子并排放置的想法并不是最好的,因为

1) 列的排序在两个表之间部分独立:如果您使用相同的 observableList 行在两个表中排序,但不可能对至少一列不在的多列进行排序同桌
2)鼠标滚轮或箭头键没有同步滚动

我知道,我可能可以使用 EventHandlers 和 Listeners 来处理此类问题,但我希望可以只使用一张表。

所以,问题是:TableView 或 TableColumns 上是否有任何可配置的属性具有我正在寻找的行为?

【问题讨论】:

    标签: javafx-2 javafx tableview


    【解决方案1】:

    我能够冻结 javafx 表视图中的列。我们需要创建我们的自定义表列类,其中我们将有两个方法setFixedisFixed 将用于使某些列固定。

    除此之外,您还需要创建自己的

    1. TableViewskin

    2. TableHeaderRow - 基本上在这个类中你需要重写getRootHeader() 方法

    3. NestedTableColumnHeader - 在这个类中覆盖 layoutChildren() 方法并添加新方法来布局 fixedColumns
    4. VirtualFlow
    5. TableView - 覆盖 createDefaultSkin() ,添加新的 booleanProperty showColumnHeader 和一个 ObservableArrayListfixedTableColumn
    6. TableRow - 覆盖 createDefaultSkin()
    7. TableRowSkinBase - 覆盖 layoutChildren() 方法来处理固定列。

    【讨论】:

      【解决方案2】:

      目前没有这样的功能。事实上,即使是操纵滚动条和响应滚动条事件也是有问题的。我能想到的两个选择:

      选项 #1 - 多表

      创建一个包含两个表格和两个滚动条的新布局,如下面的 FXML sn-p 所示:

      <BorderPane prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <bottom>
          <ScrollBar fx:id="hScroll" />
        </bottom>
        <center>
          <HBox fx:id="varTable" prefHeight="100.0" prefWidth="200.0">
            <children>
              <TableView fx:id="fixedTable" prefHeight="200.0" prefWidth="200.0">
                <columns>
                  <TableColumn prefWidth="75.0" text="Column X" />
                  <TableColumn prefWidth="75.0" text="Column X" />
                </columns>
              </TableView>
              <TableView prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS">
                <columns>
                  <TableColumn prefWidth="75.0" text="Column X" />
                  <TableColumn prefWidth="75.0" text="Column X" />
                </columns>
              </TableView>
            </children>
          </HBox>
        </center>
        <right>
          <ScrollBar fx:id="vScroll" orientation="VERTICAL" />
        </right>
      </BorderPane>
      

      注意第二个表设置了 HBox.hgrow="ALWAYS"。

      编写一个函数来定位 TableView 中的滚动条:

      private static final ScrollBar getScrollBar(final TableView<?> tableView) {
        for (final VirtualFlow virtualFlow: Nodes.filter(Nodes.descendents(tableView, 2), VirtualFlow.class)) {
           for (final Node subNode: virtualFlow.getChildrenUnmodifiable()) {
              if (subNode instanceof ScrollBar && ((ScrollBar)subNode).getOrientation() == Orientation.VERTICAL) {
                 return (ScrollBar)subNode;
              }
           }
        }
      
        return null;
      

      }

      使用它来定位两个垂直滚动条并将它们的属性(如最小值、最大值、值等)绑定到您自己的垂直滚动条,然后隐藏原始滚动条。您还需要设置 managed=false 以便它们不会占用布局中的空间。

      定位并隐藏水平滚动条并将“移动”表格水平滚动条的属性绑定到您自己的水平滚动条。

      在等待this Jira 被修复时,我们成功地使用了这种技术来链接两个带有单个滚动条的表格。

      选项 #2 - 编写自己的 TableViewSkin 下载 JavaFx 源代码,看看他们对皮肤做了什么,然后你可以编写一个完整的自定义皮肤,或者编写一个包装两个常规皮肤的皮肤,并以与上面的选项 #1 类似的方式实现

      【讨论】:

        【解决方案3】:

        所以,问题是:TableView 或 TableColumns 上是否有任何可配置的属性具有我正在寻找的行为?

        显然没有,但在 JavaFX Jira 上有一个针对此行为的功能请求(您需要注册才能查看):

        https://javafx-jira.kenai.com/browse/RT-19454

        我建议你投票给它。 :^)

        【讨论】:

          【解决方案4】:

          JavaFX 没有这个功能。查看ControlsFX 的电子表格视图。

          【讨论】:

          • ControlsFX 现在在Bitbucket 上有一个不错的实现。他们已将“SpreadsheetView”功能迁移到合法的 TableView 子类。
          【解决方案5】:

          代码使用 javafx 11

          import javafx.application.Platform;
          import javafx.scene.AccessibleAttribute;
          import javafx.scene.control.ScrollBar;
          import javafx.scene.control.TableCell;
          import javafx.scene.layout.Region;
          
          public class LockedTableCell<T, S> extends TableCell<T, S> {
                  
              {
          
                  Platform.runLater(() -> {
          
                      try {
                          ScrollBar scrollBar = (ScrollBar) getTableView()
                                      .queryAccessibleAttribute(AccessibleAttribute.HORIZONTAL_SCROLLBAR);
                          // set fx:id of TableColumn and get region of column header by #id
                          Region headerNode = (Region) getTableView().lookup("#" + getTableColumn().getId());
                          scrollBar.valueProperty().addListener((ob, o, n) -> {
                              double doubleValue = n.doubleValue();
                              
                              // move header and cell with translateX & bring it front
                              headerNode.setTranslateX(doubleValue);
                              headerNode.toFront();
          
                              this.setTranslateX(doubleValue);
                              this.toFront();
          
                          });
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                      
                  });
          
              }
          
          }
          

          使用它(前 M 列)

          tableColumnInstance1.setCellFactory(param -> new LockedTableCell<>() {...});
          tableColumnInstance2.setCellFactory(param -> new LockedTableCell<>() {...});
          ...
          tableColumnInstanceM.setCellFactory(param -> new LockedTableCell<>() {...});
          
          
          
          

          【讨论】:

          • 请不要对多个问题发布相同的答案(既不是直接副本也不是链接)。发布一个好的答案,然后投票/标记以关闭其他问题作为重复问题。如果问题不是重复的,调整您对问题的回答。特别是,这个问题是关于冻结多列的,因此您可以添加一个示例,说明如何将您的解决方案应用于多个列:)
          • 确实如此。在 tableview 的 N 列上使用 setCellFactory(...)。顺便说一句,谢谢你,我会针对多列定制我的答案。
          猜你喜欢
          • 1970-01-01
          • 2020-02-02
          • 2017-03-11
          • 2013-04-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-10
          • 1970-01-01
          相关资源
          最近更新 更多