有没有办法在 JavaFX 中使 ListView 元素变为粗体?

Is there a way to make ListView element bold in JavaFX?

所以我是 JavaFX 的新手,我正在制作我的第一个应用程序。我希望有一个选项,可以在选择并单击按钮后使我的 ListView 中的元素变为粗体。不幸的是,我找不到解决问题的方法,而且我没有主意。如果您能告诉我如何在 .java 或 .fxml 文件中执行此操作,我将不胜感激,因为我现在不使用 CSS 文件。

实现这样的功能需要一些东西:

  • 适合您的 ListView
  • 的数据模型
  • 为您的 ListView 定制的 CellFactory 将修改其中包含的项目的样式
  • 更新 Person 模型以将个人标记为“重要”的方法

此答案底部有一个完整的示例应用程序(带注释),但让我们对其进行分解并解决上述问题。

数据模型:

如果您使用简单的 String 作为 ListView 的类型(即:ListView<String>),您将需要更改它以提供自定义对象。对于这个例子,我们将创建一个新的 Person class.

class Person {

    private final StringProperty name = new SimpleStringProperty();
    private final BooleanProperty important = new SimpleBooleanProperty(false);

    public Person(String name) {    
        this.name.set(name);
    }

    public String getName() {    
        return name.get();
    }

    public StringProperty nameProperty() {    
        return name;
    }

    public void setName(String name) {    
        this.name.set(name);
    }

    public boolean isImportant() {    
        return important.get();
    }

    public BooleanProperty importantProperty() {    
        return important;
    }

    public void setImportant(boolean important) {    
        this.important.set(important);
    }
}

现在,您需要相应地定义 ListView

ListView<Person> listView = new ListView<>();

自定义细胞工厂:

CellFactoryListView 的一部分,负责呈现 ListView 中每一行的实际内容。为了根据特定 Person 的属性更改单元格的外观,我们必须提供自己的实现(JavaFX 的标准实现将简单地呈现每个单元格以包含项目的 toString() 方法) .

listView.setCellFactory(cell -> new ListCell<Person>(){
    @Override
    protected void updateItem(Person person, boolean empty) {

        super.updateItem(person, empty);
            
        // First, we are only going to update the cell if there is actually an item to display there.
        if (!empty && person != null) {
                
            // Set the text of the cell to the Person's name
            setText(person.getName());

            // If the Person has the "important" flag, we can make the text bold here
            if (person.isImportant()) {
                setStyle("-fx-font-weight: bold");
            } else {
                // Remove any styles from the cell, because this Person isn't important
                setStyle(null);
            }
        } else {
            // If there is no item to display in this cell, set the text to null
            setText(null);
        }
    }
});

更新“重要”属性:

MenuItem 的操作应该会更新所选人员的 important 属性。这相当简单:

mnuSetImportant.setOnAction(event -> {
        Person selectedPerson = listView.getSelectionModel().getSelectedItem();

        if (selectedPerson != null) {
            selectedPerson.setImportant(!selectedPerson.isImportant());
        }
    });

请注意,在这种情况下,我已将操作设置为切换此人的重要状态。您可以轻松地将 属性 设置为 true,但如果不添加单独的菜单选项,您将无法再次将其设置为 false

但是,在此之后,您会注意到单元格不会使用新样式自行更新。这是因为 ListView 不会“监听”每个 Person 的属性变化。因此,在下面的示例中,我们向 Person class 添加了一个静态 extractor() 方法,它允许其属性触发对 ListView.

的更新

就是这样!下面是一个完整的应用程序,您可以亲自试用一下,看看它的实际效果:

import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ListViewItemProperties extends Application {

    public static void main(String[] args) {

        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // **********************************************************************************************
        // Create a basic layout
        // **********************************************************************************************
        BorderPane root = new BorderPane();

        // **********************************************************************************************
        // Create the menu bar and Menu items
        // **********************************************************************************************
        MenuBar menuBar = new MenuBar();
        Menu mnuMore = new Menu("More");
        MenuItem mnuSetImportant = new MenuItem("Set important");

        // **********************************************************************************************
        // Create the ListView
        // **********************************************************************************************
        ListView<Person> listView = new ListView<>();

        // **********************************************************************************************
        // Set the action for the "Set important" menu item to update the property of the selected
        // Person in the ListView, if applicable
        // **********************************************************************************************
        mnuSetImportant.setOnAction(event -> {
            // **********************************************************************************************
            // Get reference to the currently selected Person
            // **********************************************************************************************
            Person selectedPerson = listView.getSelectionModel().getSelectedItem();

            if (selectedPerson != null) {
                // **********************************************************************************************
                // If a person has been selected, toggle their "important" property
                // **********************************************************************************************
                selectedPerson.setImportant(!selectedPerson.isImportant());
            }
        });

        // **********************************************************************************************
        // Assemble the menus
        // **********************************************************************************************
        mnuMore.getItems().add(mnuSetImportant);
        menuBar.getMenus().add(mnuMore);

        // **********************************************************************************************
        // Create some sample Persons and add them to the ListView. This will also configure the list with
        // the extractor we created in the Person class in order to trigger updates to the ListView when any 
        // of the Person's properties change.
        // **********************************************************************************************
        ObservableList<Person> people = FXCollections.observableArrayList(Person.extractor());
        people.addAll(
                new Person("Dad"),
                new Person("Sis"),
                new Person("Babe"));
        listView.setItems(people);

        // **********************************************************************************************
        // Here we will create our own CellFactory so we can set the style of the text based on the
        // important property of each Person
        // **********************************************************************************************
        listView.setCellFactory(cell -> new ListCell<Person>() {
            @Override
            protected void updateItem(Person person, boolean empty) {

                super.updateItem(person, empty);

                // First, we are only going to update the cell if there is actually an item to display there.
                if (!empty && person != null) {

                    // Set the text of the cell to the Person's name
                    setText(person.getName());

                    // If the Person has the "important" flag, we can make the text bold here
                    if (person.isImportant()) {
                        setStyle("-fx-font-weight: bold");
                    } else {
                        // Remove any styles from the cell, because this Person isn't important
                        setStyle(null);
                    }
                } else {
                    // If there is no item to display in this cell, set the text to null
                    setText(null);
                }
            }
        });

        // **********************************************************************************************
        // Assemble the Scene
        // **********************************************************************************************
        root.setTop(menuBar);
        root.setCenter(listView);

        // **********************************************************************************************
        // Set the Scene for the stage
        // **********************************************************************************************
        primaryStage.setScene(new Scene(root));

        // **********************************************************************************************
        // Configure the Stage
        // **********************************************************************************************
        primaryStage.setTitle("Test Application");
        primaryStage.show();
    }
}

class Person {

    // Name of this person
    private final StringProperty name = new SimpleStringProperty();

    // Boolean property to track whether this person has been flagged as "important"
    private final BooleanProperty important = new SimpleBooleanProperty(false);

    public Person(String name) {

        this.name.set(name);
    }

    /**
     * Callback to trigger updates whenever a property of a Person is changed. This allows our ListView
     * to refresh the cell when the "important" property changes.
     */
    public static Callback<Person, Observable[]> extractor() {

        // **********************************************************************************************
        // Return the callback which fires change events whenever either name or important properties
        // are updated. A ListView normally only listens for changes to its Items list but ignores 
        // changes to the item's internal properties.
        // **********************************************************************************************
        return (Person p) -> new Observable[]{
                p.name, p.important
        };

    }

    public String getName() {

        return name.get();
    }

    public void setName(String name) {

        this.name.set(name);
    }

    public StringProperty nameProperty() {

        return name;
    }

    public boolean isImportant() {

        return important.get();
    }

    public void setImportant(boolean important) {

        this.important.set(important);
    }

    public BooleanProperty importantProperty() {

        return important;
    }
}