JavaFX TableView Column Width\\Contents auto-trunc

2020-06-04 05:03发布

问题:

I'm trying to "smartly" abbreviate/truncate the String contents of a column nested in a TableView. I have tried using UNCONSTRAINED_RESIZE_POLICY but really prefer CONSTRAINED_RESIZE_POLICY since it auto-resizes the columns based on the window size.

What I'm trying to accomplish:

String one = "SomeVeryLongString";

// Displayed in the column where user has shrunk it too narrow

+-------------+
| Some...ring |
+-------------+

// and if the user shrunk it a bit more

+-----------+
| Som...ing |
+-----------+

// and if they stretch it a bit

+---------------+
| SomeV...tring |
+---------------+

// etc...

I have explored the possibility of reading the String length and doing my own truncating, but then it's not possible to have this dynamically update as the user shrinks/stretches the gui. My gut says this must be done with the JavaFX Classes since it would be closely tied with TableView, however I have not found a way.

How can I accomplish this using JavaFX?

回答1:

Solution

The default overrun behavior of a standard TableCell is to truncate it's string and display ... at the end of the string to indicate that the string has been truncated. All Labeled controls in JavaFX work this way.

The examples in your question have ... ellipsises in the middle of the strings. To achieve that, set the overrun style on the cell generated by the appropriate cell factory:

setTextOverrun(OverrunStyle.CENTER_WORD_ELLIPSIS);

The text string displayed for ellipsises can also be modifed by setting a new ellipsis string on the cell generated by the appropriate cell factory:

setEllipsisString(ellipsisString);

A simple table cell which demonstrates this is:

class CenteredOverrunTableCell extends TableCell<Person, String> {
    public CenteredOverrunTableCell() {
        this(null);
    }

    public CenteredOverrunTableCell(String ellipsisString) {
        super();
        setTextOverrun(OverrunStyle.CENTER_WORD_ELLIPSIS);
        if (ellipsisString != null) {
            setEllipsisString(ellipsisString);
        }  
    }

    @Override protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        setText(item == null ? "" : item);
    }
}

Sample Application

The CenteredOverrunTableCell is used in the following sample application.

  1. The First Name column elides the text in the center and uses a custom ellipsis string of <--->
  2. The Last Name column elides the text in the center but provides no custom ellipsis string.
  3. The Email column just uses the default overrun policy and ellipsis string which places the ... ellipsis at the end of the overrun string.

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ElidedTableViewSample extends Application {
    private TableView<Person> table = new TableView<>();
    private final ObservableList<Person> data =
        FXCollections.observableArrayList(
            new Person("Jacob", "Smith", "jacob.smith@example.com"),
            new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
            new Person("Ethangorovichswavlowskikaliantayaprodoralisk", "Llanfairpwllgwyngyllgogerychwyrndrobwyll-llantysiliogogogoch", "ethan@llanfairpwllgwyngyllgogerychwyrndrobwyll-llantysiliogogogoch.com"),
            new Person("Emma", "Jones", "emma.jones@example.com"),
            new Person("Michael", "Brown", "michael.brown@example.com")
        );

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

    @Override public void start(Stage stage) {
        stage.setTitle("Table View Sample");
        stage.setWidth(470);
        stage.setHeight(500);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));
        firstNameCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
            @Override public TableCell<Person, String> call(TableColumn<Person, String> p) {
                return new CenteredOverrunTableCell("<--->");
            }
        });

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("lastName"));
        lastNameCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
            @Override public TableCell<Person, String> call(TableColumn<Person, String> p) {
                return new CenteredOverrunTableCell();
            }
        });

        TableColumn emailCol = new TableColumn("Email");
        emailCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("email"));

        table.setItems(data);
        table.getColumns().addAll(
            firstNameCol, 
            lastNameCol, 
            emailCol
        );

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10));
        vbox.getChildren().addAll(label, table);

        table.setColumnResizePolicy(
            TableView.CONSTRAINED_RESIZE_POLICY
        );

        Scene scene = new Scene(vbox);
        stage.setScene(scene);
        stage.show();
    }

    class CenteredOverrunTableCell extends TableCell<Person, String> {
        public CenteredOverrunTableCell() {
            this(null);
        }

        public CenteredOverrunTableCell(String ellipsisString) {
            super();
            setTextOverrun(OverrunStyle.CENTER_WORD_ELLIPSIS);
            if (ellipsisString != null) {
                setEllipsisString(ellipsisString);
            }  
        }

        @Override protected void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            setText(item == null ? "" : item);
        }
    }

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

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

        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 getEmail() {
            return email.get();
        }

        public void setEmail(String fName) {
            email.set(fName);
        }
    }
}