有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

javafx TableView:调整可见行数

我正在使用此表在表视图中显示数据:

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Pagination;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class MainApp extends Application
{
    IntegerProperty intP = new SimpleIntegerProperty(5);
    AnchorPane anchor = new AnchorPane();
    Scene scene;
    ObservableList<Integer> options
        = FXCollections.observableArrayList(
            5,
            10,
            15,
            20);
    final ComboBox comboBox = new ComboBox(options);
    final ObservableList<Person> data = FXCollections.observableArrayList(
        new Person("1", "Joe", "Pesci"),
        new Person("2", "Audrey", "Hepburn"),
        new Person("3", "Gregory", "Peck"),
        new Person("4", "Cary", "Grant"),
        new Person("5", "De", "Niro"),
        new Person("6", "Katharine", "Hepburn"),
        new Person("7", "Jack", "Nicholson"),
        new Person("8", "Morgan", "Freeman"),
        new Person("9", "Elizabeth", "Taylor"),
        new Person("10", "Marcello", "Mastroianni"),
        new Person("11", "Innokenty", "Smoktunovsky"),
        new Person("12", "Sophia", "Loren"),
        new Person("13", "Alexander", "Kalyagin"),
        new Person("14", "Peter", "OToole"),
        new Person("15", "Gene", "Wilder"),
        new Person("16", "Evgeny", "Evstegneev"),
        new Person("17", "Michael", "Caine"),
        new Person("18", "Jean-Paul", "Belmondo"),
        new Person("19", " Julia", "Roberts"),
        new Person("20", "James", "Stewart"),
        new Person("21", "Sandra", "Bullock"),
        new Person("22", "Paul", "Newman"),
        new Person("23", "Oleg", "Tabakov"),
        new Person("24", "Mary", "Steenburgen"),
        new Person("25", "Jackie", "Chan"),
        new Person("26", "Rodney", "Dangerfield"),
        new Person("27", "Betty", "White"),
        new Person("28", "Eddie", "Murphy"),
        new Person("29", "Amitabh", "Bachchan"),
        new Person("30", "Nicole", "Kidman"),
        new Person("31", "Adriano", "Celentano"),
        new Person("32", "Rhonda", " Fleming's"),
        new Person("32", "Humphrey", "Bogart"));
    private Pagination pagination;

    public static void main(String[] args) throws Exception
    {
        launch(args);
    }

    public int itemsPerPage()
    {
        return 1;
    }

    public int rowsPerPage()
    {
        return intP.get();
    }

    public VBox createPage(int pageIndex)
    {
        int lastIndex = 0;
        int displace = data.size() % rowsPerPage();
        if (displace > 0)
        {
            lastIndex = data.size() / rowsPerPage();
        }
        else
        {
            lastIndex = data.size() / rowsPerPage() - 1;
        }
        VBox box = new VBox();
        int page = pageIndex * itemsPerPage();
        for (int i = page; i < page + itemsPerPage(); i++)
        {
            TableView<Person> table = new TableView<>();

            TableColumn numCol = new TableColumn("ID");
            numCol.setCellValueFactory(new PropertyValueFactory<>("num"));
            numCol.setMinWidth(20);
            TableColumn firstNameCol = new TableColumn("First Name");
            firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));

            firstNameCol.setMinWidth(160);
            TableColumn lastNameCol = new TableColumn("Last Name");
            lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));
            lastNameCol.setMinWidth(160);
            table.getColumns().addAll(numCol, firstNameCol, lastNameCol);
            if (lastIndex == pageIndex)
            {
                table.setItems(FXCollections.observableArrayList(data.subList(pageIndex * rowsPerPage(), pageIndex * rowsPerPage() + displace)));
            }
            else
            {
                table.setItems(FXCollections.observableArrayList(data.subList(pageIndex * rowsPerPage(), pageIndex * rowsPerPage() + rowsPerPage())));
            }

            box.getChildren().addAll(table);

        }
        return box;
    }

    @Override
    public void start(final Stage stage) throws Exception
    {
        scene = new Scene(anchor, 450, 450);
        comboBox.valueProperty().addListener(new ChangeListener<Number>()
        {
            @Override
            public void changed(ObservableValue o, Number oldVal, Number newVal)
            {
                //System.out.println(newVal.intValue());
                intP.set(newVal.intValue());
                paginate();
            }
        });
        paginate();
        stage.setScene(scene);
        stage.setTitle("Table pager");
        stage.show();
    }

    public void paginate()
    {
        pagination = new Pagination((data.size() / rowsPerPage() + 1), 0);
        //   pagination = new Pagination(20 , 0);
//        pagination.setStyle("-fx-border-color:red;");
        pagination.setPageFactory(new Callback<Integer, Node>()
        {
            @Override
            public Node call(Integer pageIndex)
            {
                if (pageIndex > data.size() / rowsPerPage() + 1)
                {
                    return null;
                }
                else
                {
                    return createPage(pageIndex);
                }
            }
        });

        AnchorPane.setTopAnchor(pagination, 10.0);
        AnchorPane.setRightAnchor(pagination, 10.0);
        AnchorPane.setBottomAnchor(pagination, 10.0);
        AnchorPane.setLeftAnchor(pagination, 10.0);

        AnchorPane.setBottomAnchor(comboBox, 40.0);
        AnchorPane.setLeftAnchor(comboBox, 12.0);
        anchor.getChildren().clear();
        anchor.getChildren().addAll(pagination, comboBox);
    }

    public static class Person
    {
        private final SimpleStringProperty num;
        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;

        private Person(String id, String fName, String lName)
        {
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.num = new SimpleStringProperty(id);
        }

        public String getFirstName()
        {
            return firstName.get();
        }

        public void setFirstName(String fName)
        {
            firstName.set(fName);
        }

        public String getLastName()
        {
            return lastName.get();
        }

        public void setLastName(String fName)
        {
            lastName.set(fName);
        }

        public String getNum()
        {
            return num.get();
        }

        public void setNum(String id)
        {
            num.set(id);
        }
    }
}

当我使用组合框更改行数时,仅更改表行中的数据。桌子高度不变

enter image description here

有没有办法删除空行


共 (6) 个答案

  1. # 1 楼答案

    不幸的是,TabelVIEW中不支持VisualBreWORT的配置(您可以考虑在fx'jra-无需,already done years ago)中提交特征请求。让视图根据这样的偏好返回一个prefHeight并不完全简单:我们需要测量“真实”细胞的大小要求,而这些细胞不知何故埋在肠道内

    只是为了好玩,我们尝试了扩展整个协作者堆栈:

    • 具有visibleRowCount属性的自定义tableView
    • 侦听属性的自定义蒙皮将根据属性计算其高度
    • 访问“真实”单元高度的某种方法是VirtualFlow,它是唯一一个拥有所有信息的类。由于相关的方法受到保护,这需要一个公开该方法的自定义VirtualFlow或反射访问

    守则:

    /**
     * TableView with visibleRowCountProperty.
     * 
     * @author Jeanette Winzenburg, Berlin
     */
    public class TableViewWithVisibleRowCount<T> extends TableView<T> {
    
        private IntegerProperty visibleRowCount = new SimpleIntegerProperty(this, "visibleRowCount", 10);
        
        
        public IntegerProperty visibleRowCountProperty() {
            return visibleRowCount;
        }
        
        @Override
        protected Skin<?> createDefaultSkin() {
            return new TableViewSkinX<T>(this);
        }
        
        /**
         * Skin that respects table's visibleRowCount property.
         */
        public static class TableViewSkinX<T> extends TableViewSkin<T> {
    
            public TableViewSkinX(TableViewWithVisibleRowCount<T> tableView) {
                super(tableView);
                registerChangeListener(tableView.visibleRowCountProperty(), "VISIBLE_ROW_COUNT");
                handleControlPropertyChanged("VISIBLE_ROW_COUNT");
            }
            
            @Override
            protected void handleControlPropertyChanged(String p) {
                super.handleControlPropertyChanged(p);
                if ("VISIBLE_ROW_COUNT".equals(p)) {
                    needCellsReconfigured = true;
                    getSkinnable().requestFocus();
                }
            }
    
            /**
             * Returns the visibleRowCount value of the table.
             */
            private int getVisibleRowCount() {
                return ((TableViewWithVisibleRowCount<T>) getSkinnable()).visibleRowCountProperty().get();
            }
            
            /**
             * Calculates and returns the pref height of the 
             * for the given number of rows.
             * 
             * If flow is of type MyFlow, queries the flow directly
             * otherwise invokes the method.
             */
            protected double getFlowPrefHeight(int rows) {
                double height = 0;
                if (flow instanceof MyFlow) {
                    height = ((MyFlow) flow).getPrefLength(rows);
                }
                else {
                    for (int i = 0; i < rows && i < getItemCount(); i++) {
                        height += invokeFlowCellLength(i);
                    }
                }    
                return height + snappedTopInset() + snappedBottomInset();
    
            }
            
            /**
             * Overridden to compute the sum of the flow height and header prefHeight.
             */
            @Override
            protected double computePrefHeight(double width, double topInset,
                    double rightInset, double bottomInset, double leftInset) {
                // super hard-codes to 400 .. doooh
                double prefHeight = getFlowPrefHeight(getVisibleRowCount());
                return prefHeight + getTableHeaderRow().prefHeight(width);
            }
            
            /**
             * Reflectively invokes protected getCellLength(i) of flow.
             * @param index the index of the cell.
             * @return the cell height of the cell at index.
             */
            protected double invokeFlowCellLength(int index) {
                double height = 1.0;
                Class<?> clazz = VirtualFlow.class;
                try {
                    Method method = clazz.getDeclaredMethod("getCellLength", Integer.TYPE);
                    method.setAccessible(true);
                    return ((double) method.invoke(flow, index));
                } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    e.printStackTrace();
                }
                return height;
            }
    
            /**
             * Overridden to return custom flow.
             */
            @Override
            protected VirtualFlow createVirtualFlow() {
                return new MyFlow();
            }
            
            /**
             * Extended to expose length calculation per a given # of rows.
             */
            public static class MyFlow extends VirtualFlow {
    
                protected double getPrefLength(int rowsPerPage) {
                    double sum = 0.0;
                    int rows = rowsPerPage; //Math.min(rowsPerPage, getCellCount());
                    for (int i = 0; i < rows; i++) {
                        sum += getCellLength(i);
                    }
                    return sum;
                }
    
            }
            
        }
    
        @SuppressWarnings("unused")
        private static final Logger LOG = Logger.getLogger(TableViewWithVisibleRowCount.class
                .getName());
    }
    

    请注意,当有固定的单元格大小时,您可能会通过简单地重写表的prefHeight来解决问题,不要尝试这样做-没有风险没有乐趣:-)


    更新:javafx15中的自定义皮肤-基本相同,只是一些访问细节发生了更改(在两个方向上;)

    /**
     * Skin that respects table's visibleRowCount property.
     */
    public class TableViewSkinX<T> extends TableViewSkin<T> {
    
        public TableViewSkinX(TableViewWithVisibleRowCount<T> tableView) {
            super(tableView);
            registerChangeListener(tableView.visibleRowCountProperty(), e -> visibleRowCountChanged());
        }
        
        private void visibleRowCountChanged() {
            getSkinnable().requestLayout();
        }
        
        /**
         * Returns the visibleRowCount value of the table.
         */
        private int getVisibleRowCount() {
            return ((TableViewWithVisibleRowCount<T>) getSkinnable()).visibleRowCountProperty().get();
        }
        
        /**
         * Calculates and returns the pref height of the for the given number of
         * rows.
         */
        protected double getFlowPrefHeight(int rows) {
            double height = 0;
            for (int i = 0; i < rows && i < getItemCount(); i++) {
                height += invokeFlowCellLength(i);
            }
            return height + snappedTopInset() + snappedBottomInset();
        }
        
        /**
         * Overridden to compute the sum of the flow height and header prefHeight.
         */
        @Override
        protected double computePrefHeight(double width, double topInset,
                double rightInset, double bottomInset, double leftInset) {
            // super hard-codes to 400 .. doooh
            double prefHeight = getFlowPrefHeight(getVisibleRowCount());
            return prefHeight + getTableHeaderRow().prefHeight(width);
        }
        
        /**
         * Reflectively invokes protected getCellLength(i) of flow.
         * @param index the index of the cell.
         * @return the cell height of the cell at index.
         */
        protected double invokeFlowCellLength(int index) {
            // note: use your own utility method to reflectively access internal fields/methods
            return (double) FXUtils.invokeGetMethodValue(VirtualFlow.class, getVirtualFlow(), 
                    "getCellLength", Integer.TYPE, index);
        }
    
    }
    
  2. # 2 楼答案

    更改tableview的高度和删除“空”行是两件不同的事情。具体点

    有关删除行的信息,请参见this tutorial

    要更改高度,请首先设置表视图的fixedCellSizeProperty,然后在绑定中使用它:

    table.setFixedCellSize(25);
    table.prefHeightProperty().bind(Bindings.size(table.getItems()).multiply(table.getFixedCellSize()).add(30));
    

    添加30px用于tableview的标题

  3. # 3 楼答案

    如果您不热衷于绑定,一种简单的方法是基于固定的单元格大小(cf.Fred Danna's answer)计算所需的高度,并在表数据上使用侦听器进行更新

    static void setTableHeightByRowCount(TableView table, ObservableList data) {
      int rowCount = data.size();
      TableHeaderRow headerRow = (TableHeaderRow) table.lookup("TableHeaderRow");
      double tableHeight = (rowCount * table.getFixedCellSize())
        // add the insets or we'll be short by a few pixels
        + table.getInsets().getTop() + table.getInsets().getBottom()
        // header row has its own (different) height
        + (headerRow == null ? 0 : headerRow.getHeight())
        ;
    
      table.setMinHeight(tableHeight);
      table.setMaxHeight(tableHeight);
      table.setPrefHeight(tableHeight);
    }
    

    start(Stage)中,我们创建表并添加ListChangeListener

    TableView<String> table = new TableView<>();
    table.setFixedCellSize(24);
    table.getItems().addListener((ListChangeListener<String>) c ->
      setTableHeightByRowCount(table, c.getList()));
    
    // init scene etc...
    
    stage.show();
    table.getItems().addAll("Stacey", "Kristy", "Mary Anne", "Claudia");
    

    请注意,表头行在stage.show()之后才存在,因此最简单的方法是等待设置表数据。 或者,我们可以在表构造时设置数据,然后显式调用setTableHeightByRowCount()

    TableView<String> table = new TableView<>(
      FXCollections.observableArrayList("Stacey", "Kristy", "Mary Anne", "Claudia")
    );
    
    // add listener, init scene etc...
    
    stage.show();
    setTableHeightByRowCount(table, table.getItems());
    
  4. # 4 楼答案

    这里是我的解决方案,为了不深深依赖于table.getFixedCellSize(我们在FX初始化期间仍然依赖于它,而CSS还没有计算/应用)

    注意,我们还需要添加一些像素(不明白为什么)

    public static <S> void ensureDisplayingRows(@NotNull TableView<S> table, @Null Integer rowCount) {
        DoubleProperty headerRowHeightProperty = new SimpleDoubleProperty();
        table.skinProperty().addListener((observable, oldValue, newValue) -> {
            if (!Objects.equals(oldValue, newValue)) {
                TableHeaderRow headerRow = headerRow(table);
                // TableHeaderRow not defined until CSS is applied.
                if (headerRow == null) {
                    assert table.getFixedCellSize() > 0.0 : "TableView '" + table.getId() + "' is not 'fixedCellSize'."; // TODO Find a better way to control.
                    headerRowHeightProperty.setValue(table.getFixedCellSize()); // Approximation. // TODO Find a better approximation.
                } else {
                    headerRowHeightProperty.bind(headerRow.heightProperty());
                }
            }
        });
    
        IntegerBinding itemsCountBinding = Bindings.size(table.getItems()); // NB: table.getItems() may not (yet) contains all/"new" items, may contain the "old" items.
        IntegerBinding maxRowsCountBinding = (rowCount == null) ? itemsCountBinding :
                (IntegerBinding) Bindings.min(
                        rowCount,
                        itemsCountBinding
                );
        IntegerBinding rowCountBinding = (IntegerBinding) Bindings.max(
                1, // Ensure to display at least 1 row, for JavaFX "No contents" message when table.items.isEmpty.
                maxRowsCountBinding
        );
    
        DoubleBinding tableHeightBinding = headerRowHeightProperty
                .add(rowCountBinding.multiply(table.getFixedCellSize()))
                .add(10); // TODO Understand why we need to add a dozen of pixels.
    
        table.minHeightProperty().bind(tableHeightBinding);
        table.prefHeightProperty().bind(tableHeightBinding);
        table.maxHeightProperty().bind(tableHeightBinding);
    }
    
    @Null
    public static TableHeaderRow headerRow(@NotNull TableView<?> table) {
        TableHeaderRow tableHeaderRow = (TableHeaderRow) table.lookup("TableHeaderRow");
        return tableHeaderRow;
    }
    
  5. # 5 楼答案

    有没有办法做到这一点。。。是的,您需要做的是在创建表时(因为每次选择一个新数字时都会重新创建表),您需要计算表的高度和当前条目数,然后使用TableView的setPrefHeight()属性使表变小,以便只考虑这些行

    我稍微玩弄了一下,没有找到任何快速的解决方案来正确计算表的大小,所以我没有任何代码给您,但这是您需要做的。您还可以“设置”表格的样式,使其不具有交替颜色方案,这将使包含数据的行下面的行看起来“空”,即使其中会有一些空白

    祝你好运

  6. # 6 楼答案

    只需使用css更改空行的背景颜色

    .table-row-cell:empty {
    -fx-background-color: white;
    -fx-border-color: white;
    } 
    

    并在combobox的基础上修改行数