java - 初始化后更改 TableView 中文本的字体

标签 java fonts javafx tableview

我想更改 TableView 中文本的字体,但是当我这样尝试时,它没有应用该字体,因为它找不到任何节点:

tableView.lookupAll(".table-row-cell").forEach(cell -> {
    applyFont(cell, MainFont.LIGHT, FontStyle.SUBHEAD);
});

其中 applyFont() 是自定义函数。我认为这与 TableView 未初始化有关,因此我尝试将其包装在 Platform.runLater() 中,但这也不起作用。

但是,唯一有效的是将这段代码放入监听器中(选择、悬停等):

tableView.hoverProperty().addListener(change -> {

    tableView.lookupAll(".table-row-cell").forEach(cell -> {
        applyFont(cell, MainFont.LIGHT, FontStyle.SUBHEAD);
    });

});

当我现在将鼠标悬停在 TableView 上时,字体会发生变化。但我不明白为什么它不能与 Platform.runLater() 一起使用。

此代码位于我的应用程序中每个屏幕的父类(super class)中,并在子屏幕构建后运行,但尚未显示。

我在这里缺少什么?非常感谢任何帮助!

注意:在我的案例中,我无法使用 CSS 解决方案。

最佳答案

我发现 CSS 查找本质上不稳定,我的建议是尽可能避免它们。除非应用了 CSS,否则它们不会找到任何节点,这通常发生在第一帧渲染脉冲上。此外,对于此用例,您需要确保已创建表行和单元格。当然,仅将查找包装在 Platform.runLater(...) 中并不能保证是否满足这些条件。此外,它们不是类型安全的并且依赖于字符串绑定(bind),因此有很多事情可能会失败,而编译器不会检查这些事情。

对于您的用例,只需设置表行的样式就足够了。您可以直接使用 rowFactory 来完成此操作。为了以后能够更新样式,只需创建一个 StringProperty 来保存样式,然后绑定(bind) TableRowstyleProperty对此:

StringProperty style = new SimpleStringProperty();

// ...

table.setRowFatory(tv -> {
    TableRow<MyDataType> row = new TableRow<>();
    row.styleProperty().bind(style);
    return row ;
});

现在,只需使用有效的 CSS 样式调用 style.set(...) 即可更新行的样式(以及行包含的所有单元格)。这完全避免了查找的需要。

SSCCE:

import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

public class ConfigurableFontTable extends Application {

    private StringProperty fontFamily = new SimpleStringProperty(Font.getDefault().getFamily());
    private ObjectProperty<FontWeight> fontWeight = new SimpleObjectProperty<>(FontWeight.NORMAL);
    private BooleanProperty italic = new SimpleBooleanProperty();
    private IntegerProperty fontSize = new SimpleIntegerProperty((int)Font.getDefault().getSize());

    private ObjectProperty<Color> fontFill = new SimpleObjectProperty<>(Color.BLACK);

    private StringProperty style = new SimpleStringProperty();

    @Override
    public void start(Stage primaryStage) {
        style.bind(Bindings.createStringBinding(() -> String.format(
                "-fx-font-family: %s;\n"
                + "-fx-font-weight: %d;\n"
                + "-fx-font-style: %s;\n"
                + "-fx-font-size: %d;\n"
                + "-fx-text-background-color: rgba(%d, %d, %d, %f);\n",
                fontFamily.get(),
                fontWeight.get().getWeight(),
                italic.get()?"italic":"normal",
                fontSize.get(),
                (int)(255 * fontFill.get().getRed()),
                (int)(255 * fontFill.get().getGreen()),
                (int)(255 * fontFill.get().getBlue()),
                fontFill.get().getOpacity()
            ),
            fontFamily,
            fontWeight,
            italic,
            fontSize,
            fontFill
        ));

        TableView<Person> table = new TableView<>();
        table.setRowFactory(tv -> {
            TableRow<Person> row = new TableRow<>();
            row.styleProperty().bind(style);
            return row ;
        });

        table.getColumns().add(column("First Name", Person::firstNameProperty));
        table.getColumns().add(column("Last Name", Person::lastNameProperty));
        table.getColumns().add(column("Email", Person::emailProperty));

        table.getItems().addAll(
                new Person("Jacob", "Smith", "jacob.smith@example.com"),
                new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
                new Person("Ethan", "Williams", "ethan.williams@example.com"),
                new Person("Emma", "Jones", "emma.jones@example.com"),
                new Person("Michael", "Brown", "michael.brown@example.com")
        );

        Button changeStyleButton = new Button("Change style...");
        changeStyleButton.setOnAction(e -> showChangeStyleDialog(primaryStage));

        BorderPane root = new BorderPane(table, null, null, changeStyleButton, null);
        BorderPane.setAlignment(changeStyleButton, Pos.CENTER);
        BorderPane.setMargin(changeStyleButton, new Insets(10));

        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.show();
    }

    private void showChangeStyleDialog(Stage owner) {
        GridPane root = new GridPane();
        root.setHgap(5);
        root.setVgap(5);
        root.setPadding(new Insets(10));

        ColumnConstraints leftCol = new ColumnConstraints();
        leftCol.setHalignment(HPos.RIGHT);
        leftCol.setHgrow(Priority.NEVER);

        ColumnConstraints rightCol = new ColumnConstraints();

        root.getColumnConstraints().addAll(leftCol, rightCol);

        ComboBox<String> fontFamilyChoice = new ComboBox<>();
        fontFamilyChoice.getItems().addAll(Font.getFamilies());
        fontFamilyChoice.setValue(fontFamily.get());

        ComboBox<FontWeight> fontWeightChoice = new ComboBox<>();
        fontWeightChoice.getItems().addAll(FontWeight.values());
        fontWeightChoice.setValue(fontWeight.get());

        CheckBox italicCheckBox = new CheckBox("Italic");
        italicCheckBox.setSelected(italic.get());

        ComboBox<Integer> fontSizeChoice = new ComboBox<>();
        fontSizeChoice.getItems().addAll(4, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48);
        fontSizeChoice.setValue(fontSize.get());

        ColorPicker colorPicker = new ColorPicker();
        colorPicker.setValue(fontFill.get());

        root.addRow(0,  new Label("Font:"), fontFamilyChoice);
        root.addRow(1, new Label("Weight:"), fontWeightChoice);
        root.addRow(2, new Label("Size:"), fontSizeChoice);
        root.add(italicCheckBox, 1, 3);
        root.addRow(4, new Label("Text Color:"), colorPicker);

        Stage stage = new Stage();

        Button okButton = new Button("OK");
        okButton.setOnAction(e -> {
            fontFamily.set(fontFamilyChoice.getValue());
            fontWeight.set(fontWeightChoice.getValue());
            fontSize.set(fontSizeChoice.getValue());
            italic.set(italicCheckBox.isSelected());
            fontFill.set(colorPicker.getValue());
            stage.hide();
        });

        Button cancelButton = new Button("Cancel");
        cancelButton.setOnAction(e -> stage.hide());

        HBox buttons = new HBox(5, okButton, cancelButton);
        buttons.setAlignment(Pos.CENTER);
        root.add(buttons, 0, 5, 2, 1);

        stage.initOwner(owner);
        stage.setScene(new Scene(root));
        stage.show();
    }

    private static <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        return col ;
    }

    public class Person {
        private final StringProperty firstName = new SimpleStringProperty(this,"firstName");
        private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
        private final StringProperty email = new SimpleStringProperty(this, "email");

        public Person(String firstName, String lastName, String email) {
            this.firstName.set(firstName);
            this.lastName.set(lastName);
            this.email.set(email);
        }

        public final StringProperty firstNameProperty() {
            return this.firstName;
        }

        public final String getFirstName() {
            return this.firstNameProperty().get();
        }

        public final void setFirstName(final String firstName) {
            this.firstNameProperty().set(firstName);
        }

        public final StringProperty lastNameProperty() {
            return this.lastName;
        }

        public final String getLastName() {
            return this.lastNameProperty().get();
        }

        public final void setLastName(final String lastName) {
            this.lastNameProperty().set(lastName);
        }

        public final StringProperty emailProperty() {
            return this.email;
        }

        public final String getEmail() {
            return this.emailProperty().get();
        }

        public final void setEmail(final String email) {
            this.emailProperty().set(email);
        }

    }

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

关于java - 初始化后更改 TableView 中文本的字体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32478638/

相关文章:

python - 使用具有通用页眉/页脚和分页的 WeasyPrint 生成 PDF

java - 仅从 JavaFX ObservableList 中获取一些值

JavaFX 线程卡住

java - 我应该如何解决 DI 和测试的上下文对象?

javascript - 如何确保在执行 JS 之前应用自定义字体

java - 如何创建一个可以实例化多个对象并将它们放入列表中的通用方法

docker - 如何在tileserver-gl服务器上使用自定义字体(字形)?

java - 按ESC退出全屏模式后如何在javafx中返回全屏?

java - 使用另一个类的 JavaFX 媒体播放器播放音频文件

java - 如何使用 JPA 调用 firebird 可选择存储过程?