JavaFX:将TableView的列适合内容以避免剪切内容

时间:2018-05-18 14:33:50

标签: javafx tableview column-width

我遇到以下问题:当我创建TableView并将数据放入列中时,列会自动适合内容。但是如果有很多行(超过30个),JavaFX会将列宽优化为所有内容的平均长度。

在第一个例子中,我把长字符串放在表中,一切都很好。

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Example extends Application {

    @Override
    public void start(Stage stage) {

        // Init data
        ObservableList<Person> persons = FXCollections.observableArrayList();
        persons.add(new Person("Maximus-Superman", "Power", "Maximus-Superman.Power@test.com"));
        persons.add(new Person("Max", "Powerstrongsupercool", "Max.Powerstrongsupercool@test.com"));
        persons.add(new Person("Maximus-Superman", "Powerstrongsupercool", "Maximus-Superman.Powerstrongsupercool@test.com"));
        for (int i = 0; i < 100; i++) {
            persons.add(new Person("Max", "Power", "Max.Power@test.com"));
        }
        // Init table
        TableView<Person> table = new TableView<Person>();
        table.setMaxWidth(Double.MAX_VALUE);
        table.setMaxHeight(Double.MAX_VALUE);
        table.setItems(persons);

        // Init columns
        TableColumn<Person, String> firstname = new TableColumn<Person, String>("Firstname");
        firstname.setCellValueFactory(new PropertyValueFactory<Person, String>("firstname"));
        table.getColumns().add(firstname);

        TableColumn<Person, String> lastname = new TableColumn<Person, String>("Lastname");
        lastname.setCellValueFactory(new PropertyValueFactory<Person, String>("lastname"));
        table.getColumns().add(lastname);

        TableColumn<Person, String> email = new TableColumn<Person, String>("E-Mail");
        email.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
        table.getColumns().add(email);

        // Init Stage
        Scene scene = new Scene(table, 400, 150);
        stage.setScene(scene);
        stage.show();
    }

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

    public class Person {

        private String firstname;
        private String lastname;
        private String email;

        public Person(String firstname, String lastname, String email) {
            this.firstname = firstname;
            this.lastname = lastname;
            this.email = email;
        }

        // Getters and Setters

    }
}

看起来不错......

enter image description here

在第二个例子中,我首先将短字符串放在表格中,至少是长字符串。在此示例中,JavaFX选择columnwidth为small,内容将被删除。

public class Example extends Application {

    @Override
    public void start(Stage stage) {

        // Init data
        ObservableList<Person> persons = FXCollections.observableArrayList();
        for (int i = 0; i < 100; i++) {
            persons.add(new Person("Max", "Power", "Max.Power@test.com"));
        }
        persons.add(new Person("Maximus-Superman", "Power", "Maximus-Superman.Power@test.com"));
        persons.add(new Person("Max", "Powerstrongsupercool", "Max.Powerstrongsupercool@test.com"));
        persons.add(new Person("Maximus-Superman", "Powerstrongsupercool", "Maximus-Superman.Powerstrongsupercool@test.com"));
        [...]

看起来很糟糕......

enter image description here

我该如何避免这种情况?

编辑19.05.2018

评论中的链接不起作用。

所以,我在JavaFx的源代码中发现了这个问题: 在updateScene()方法中,maxRows设置为30.无法更改值,因为涉及的每个方法都是受保护的或私有的。

评论是对的。如果表中有很多行,则创建表可能需要很长时间。但有时开发人员知道表格中可能存在的行数,或者可能会冒更高的加载时间。

一种解决方案是联系Oracle,为max值创建一个setter()。行。所以开发者可以选择最大值。每列的行数。

public class TableColumnHeader extends Region {

    [...]

    private void updateScene() {
        // RT-17684: If the TableColumn widths are all currently the default,
        // we attempt to 'auto-size' based on the preferred width of the first
        // n rows (we can't do all rows, as that could conceivably be an unlimited
        // number of rows retrieved from a very slow (e.g. remote) data source.
        // Obviously, the bigger the value of n, the more likely the default
        // width will be suitable for most values in the column
        final int n = 30; // ------------------------------------------> This is the problem!
        if (! autoSizeComplete) {
            if (getTableColumn() == null || getTableColumn().getWidth() != DEFAULT_COLUMN_WIDTH || getScene() == null) {
                return;
            }
            doColumnAutoSize(getTableColumn(), n);
            autoSizeComplete = true;
        }
    }

    [...]

    private void doColumnAutoSize(TableColumnBase<?,?> column, int cellsToMeasure) {
        double prefWidth = column.getPrefWidth();

        // if the prefWidth has been set, we do _not_ autosize columns
        if (prefWidth == DEFAULT_COLUMN_WIDTH) {
            getTableViewSkin().resizeColumnToFitContent(column, cellsToMeasure);
        }
    }

    [...]

}

public class TableViewSkin<T> extends TableViewSkinBase<T, T, TableView<T>, TableViewBehavior<T>, TableRow<T>, TableColumn<T, ?>> {

    [...]

    @Override protected void resizeColumnToFitContent(TableColumn<T, ?> tc, int maxRows) {
        if (!tc.isResizable()) return;

        // final TableColumn<T, ?> col = tc;
        List<?> items = itemsProperty().get();
        if (items == null || items.isEmpty()) return;

        Callback/*<TableColumn<T, ?>, TableCell<T,?>>*/ cellFactory = tc.getCellFactory();
        if (cellFactory == null) return;

        TableCell<T,?> cell = (TableCell<T, ?>) cellFactory.call(tc);
        if (cell == null) return;

        // set this property to tell the TableCell we want to know its actual
        // preferred width, not the width of the associated TableColumnBase
        cell.getProperties().put(TableCellSkin.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE);

        // determine cell padding
        double padding = 10;
        Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
        if (n instanceof Region) {
            Region r = (Region) n;
            padding = r.snappedLeftInset() + r.snappedRightInset();
        }

        int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows); // ------------------> if maxRows equals -1 every item will be checked
        double maxWidth = 0;
        for (int row = 0; row < rows; row++) {
            cell.updateTableColumn(tc);
            cell.updateTableView(tableView);
            cell.updateIndex(row);

            if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
                getChildren().add(cell);
                cell.applyCss();
                maxWidth = Math.max(maxWidth, cell.prefWidth(-1));
                getChildren().remove(cell);
            }
        }

        // dispose of the cell to prevent it retaining listeners (see RT-31015)
        cell.updateIndex(-1);

        // RT-36855 - take into account the column header text / graphic widths.
        // Magic 10 is to allow for sort arrow to appear without text truncation.
        TableColumnHeader header = getTableHeaderRow().getColumnHeaderFor(tc);
        double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
        Node graphic = header.label.getGraphic();
        double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
        double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
        maxWidth = Math.max(maxWidth, headerWidth);

        // RT-23486
        maxWidth += padding;
        if(tableView.getColumnResizePolicy() == TableView.CONSTRAINED_RESIZE_POLICY) {
            maxWidth = Math.max(maxWidth, tc.getWidth());
        }

        tc.impl_setWidth(maxWidth);
    }

    [...]

}

另一个解决方案是在TableColumn的rect的标题上触发MouseEvent。

enter image description here

如果MouseEvent的ClickCount等于2且PrimaryButton为down,则调用resizeColumnToFitContent()方法,其maxRows值为-1。

int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);

-1表示TableView中的所有行。

public class NestedTableColumnHeader extends TableColumnHeader {

    [...]

    private static final EventHandler<MouseEvent> rectMousePressed = new EventHandler<MouseEvent>() {
        @Override public void handle(MouseEvent me) {
            Rectangle rect = (Rectangle) me.getSource();
            TableColumnBase column = (TableColumnBase) rect.getProperties().get(TABLE_COLUMN_KEY);
            NestedTableColumnHeader header = (NestedTableColumnHeader) rect.getProperties().get(TABLE_COLUMN_HEADER_KEY);

            if (! header.isColumnResizingEnabled()) return;

            if (me.getClickCount() == 2 && me.isPrimaryButtonDown()) {
                // the user wants to resize the column such that its
                // width is equal to the widest element in the column
                header.getTableViewSkin().resizeColumnToFitContent(column, -1); // -----------------------> this method should be call and everything is fine
            } else {
                // rather than refer to the rect variable, we just grab
                // it from the source to prevent a small memory leak.
                Rectangle innerRect = (Rectangle) me.getSource();
                double startX = header.getTableHeaderRow().sceneToLocal(innerRect.localToScene(innerRect.getBoundsInLocal())).getMinX() + 2;
                header.dragAnchorX = me.getSceneX();
                header.columnResizingStarted(startX);
            }
            me.consume();
        }
    };

    [...]

}

那么,是否可以创建一个ClickEount为2且PrimaryButtonDown-boolean为true的新MouseEvent并将其激活到TableColumn?

并且:我如何联系Oracle以取悦他们在下一个版本中为maxRows创建一个setter()?

0 个答案:

没有答案