TableView:调整可见行数

问题描述 投票:18回答:6

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

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);
        }
    }
}

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

有没有办法删除空行?

javafx javafx-2 tableview javafx-8
6个回答
26
投票

更改tableview的高度和删除“空”行是两回事。请明确点。

要删除行,请参阅this tutorial

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

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

添加30px是tableview的标题。


14
投票

不幸的是,TableView不支持visibleRowCount的配置(您可以考虑在fx中提交功能请求'jira - 不需要,already done years ago)。并且根据这样的偏好让视图返回prefHeight并不是完全直截了当的:我们需要测量“真实”细胞的大小要求,并且它以某种方式埋在肠内。

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

  • 具有visibleRowCount属性的自定义tableView
  • 一个侦听属性的自定义皮肤会根据它来计算其prefHeight
  • 一些方法来访问“真实”单元格的高度 - 唯一具有测量它的所有信息的类是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的简单覆盖,没有尝试 - 没有风险没有乐趣:-)


2
投票

这是我的解决方案,为了不依赖于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;
}

0
投票

有没有办法做到这一点...是的,你需要做的是当你创建表时(因为你每次选择一个新的数字时重新创建它)你需要计算表的高度是什么当前条目数,然后使用TableView的setPrefHeight()属性使表更小,只考虑那些行。

我玩了一下它,我没有找到任何快速的解决方案来正确计算表的大小,所以我没有任何代码给你,但这就是你需要做的。您还可以“设置”表格以使其不具有交替的颜色方案,即使存在一些空白区域,也会使具有数据的行下方的行看起来为“空”。

祝好运!


0
投票

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

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

并根据组合框修改行数。


0
投票

如果您没有结合绑定,一种简单的方法是根据固定的单元格大小计算所需的高度(参见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());
© www.soinside.com 2019 - 2024. All rights reserved.