User interface 如何在JavaFX中创建多列组合框?

User interface 如何在JavaFX中创建多列组合框?,user-interface,javafx,combobox,User Interface,Javafx,Combobox,我正在尝试创建一个组合框,在其下拉菜单中显示多列 下面是一个屏幕截图,显示了我希望它的外观: 有什么建议吗 我脑海中唯一的解决方案是通过扩展ComboBox并使用多列对其进行自定义来创建自定义容器 但是JavaFX是否为我提供了创建自定义UI容器的选项 如何创建自定义UI容器以及如何在FXML中使用它?您不需要扩展组合框来创建类似的布局。相反,您只需要提供自己的CellFactory的实现 通过创建自定义的单元格工厂,您可以通过提供自己的列表单元格(在下拉菜单中实际可选择的项目)来控制组合框中

我正在尝试创建一个组合框,在其下拉菜单中显示多列

下面是一个屏幕截图,显示了我希望它的外观:

有什么建议吗

我脑海中唯一的解决方案是通过扩展
ComboBox
并使用多列对其进行自定义来创建自定义容器

但是JavaFX是否为我提供了创建自定义UI容器的选项


如何创建自定义UI容器以及如何在FXML中使用它?

您不需要扩展组合框来创建类似的布局。相反,您只需要提供自己的
CellFactory的实现

通过创建自定义的
单元格工厂
,您可以通过提供自己的
列表单元格
(在下拉菜单中实际可选择的项目)来控制组合框
中项目的显示方式

我确信有很多方法可以实现这一点,但在本例中,我将使用
GridPane
作为我的
ListCell
的根布局

下面的完整示例也有注释:

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple Interface
        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        // List of sample Persons
        ObservableList<Person> persons = FXCollections.observableArrayList();
        persons.addAll(
                new Person("Maria Anders", "Sales Representative", "Zurich"),
                new Person("Ana Trujillo", "Owner", "Sydney"),
                new Person("Thomas Hardy", "Order Administrator", "Dallas")
        );

        // Create a simple ComboBox of Persons
        ComboBox<Person> cboPersons = new ComboBox<>();
        cboPersons.setItems(persons);

        // We need a StringConverter in order to ensure the selected item is displayed properly
        // For this sample, we only want the Person's name to be displayed
        cboPersons.setConverter(new StringConverter<Person>() {
            @Override
            public String toString(Person person) {
                return person.getName();
            }

            @Override
            public Person fromString(String string) {
                 return null;
            }
        });

        // Provide our own CellFactory to control how items are displayed
        cboPersons.setCellFactory(cell -> new ListCell<Person>() {

            // Create our layout here to be reused for each ListCell
            GridPane gridPane = new GridPane();
            Label lblName = new Label();
            Label lblTitle = new Label();
            Label lblLocation = new Label();

            // Static block to configure our layout
            {
                // Ensure all our column widths are constant
                gridPane.getColumnConstraints().addAll(
                        new ColumnConstraints(100, 100, 100),
                        new ColumnConstraints(100, 100, 100),
                        new ColumnConstraints(100, 100, 100)
                );

                gridPane.add(lblName, 0, 1);
                gridPane.add(lblTitle, 1, 1);
                gridPane.add(lblLocation, 2, 1);

            }

            // We override the updateItem() method in order to provide our own layout for this Cell's graphicProperty
            @Override
            protected void updateItem(Person person, boolean empty) {
                super.updateItem(person, empty);

                if (!empty && person != null) {

                    // Update our Labels
                    lblName.setText(person.getName());
                    lblTitle.setText(person.getTitle());
                    lblLocation.setText(person.getLocation());

                    // Set this ListCell's graphicProperty to display our GridPane
                    setGraphic(gridPane);
                } else {
                    // Nothing to display here
                    setGraphic(null);
                }
            }
        });

        // Add the ComboBox to the scene
        root.getChildren().addAll(
                new Label("Select Person:"),
                cboPersons
        );

        // Show the stage
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Sample");
        primaryStage.show();
    }
}

// Simple Person class to represent our...Persons
class Person {

    private final StringProperty name = new SimpleStringProperty();
    private final StringProperty title = new SimpleStringProperty();
    private final StringProperty location = new SimpleStringProperty();

    Person(String name, String title, String location) {
        this.name.set(name);
        this.title.set(title);
        this.location.set(location);
    }

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

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

    public StringProperty nameProperty() {
        return name;
    }

    public String getTitle() {
        return title.get();
    }

    public void setTitle(String title) {
        this.title.set(title);
    }

    public StringProperty titleProperty() {
        return title;
    }

    public String getLocation() {
        return location.get();
    }

    public void setLocation(String location) {
        this.location.set(location);
    }

    public StringProperty locationProperty() {
        return location;
    }
}
导入javafx.application.application;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.beans.property.StringProperty;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.geometry.Insets;
导入javafx.geometry.Pos;
导入javafx.scene.scene;
导入javafx.scene.control.ComboBox;
导入javafx.scene.control.Label;
导入javafx.scene.control.ListCell;
导入javafx.scene.layout.ColumnConstraints;
导入javafx.scene.layout.GridPane;
导入javafx.scene.layout.VBox;
导入javafx.stage.stage;
公共类主扩展应用程序{
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
公共无效开始(阶段primaryStage){
//简单接口
VBox根=新的VBox(10);
根部设置对齐(位置中心);
根。设置填充(新插图(10));
//抽样人员名单
ObservableList persons=FXCollections.observableArrayList();
人名:addAll(
新人(“Maria Anders”、“销售代表”、“苏黎世”),
新人(“安娜·特鲁希略”、“所有者”、“悉尼”),
新人(“托马斯·哈代”、“订单管理员”、“达拉斯”)
);
//创建一个简单的人员组合框
ComboBox cboPersons=新ComboBox();
c运营商设置项目(人);
//我们需要一个StringConverter,以确保正确显示所选项目
//对于此示例,我们只希望显示此人的姓名
cOperson.setConverter(新的StringConverter(){
@凌驾
公共字符串toString(Person){
返回person.getName();
}
@凌驾
公共人物fromString(String){
返回null;
}
});
//提供我们自己的CellFactory来控制项目的显示方式
cOperson.setCellFactory(单元格->新列表单元格(){
//在这里创建我们的布局,以便为每个ListCell重用
GridPane GridPane=新建GridPane();
Label lblName=新标签();
Label lblTitle=新标签();
Label lblLocation=新标签();
//静态块来配置我们的布局
{
//确保我们所有的列宽都是恒定的
gridPane.getColumnConstraints().addAll(
新列约束(100100100),
新列约束(100100100),
新列约束(100100100)
);
add(lblName,0,1);
添加(lblTitle,1,1);
添加(lballocation,2,1);
}
//我们重写updateItem()方法,以便为此单元格的graphicProperty提供我们自己的布局
@凌驾
受保护的void updateItem(Person,布尔值为空){
super.updateItem(person,空);
如果(!empty&&person!=null){
//更新我们的标签
lblName.setText(person.getName());
lblTitle.setText(person.getTitle());
lblLocation.setText(person.getLocation());
//设置此ListCell的graphicProperty以显示我们的GridPane
设置图形(网格窗格);
}否则{
//这里没什么可展示的
设置图形(空);
}
}
});
//将组合框添加到场景中
root.getChildren().addAll(
新标签(“选择人员:”),
C操作员
);
//上台
primaryStage.setScene(新场景(根));
初级阶段。设置标题(“样本”);
primaryStage.show();
}
}
//简单的Person类来代表我们的…Person
班主任{
私有最终StringProperty名称=新SimpleStringProperty();
private final StringProperty title=新SimpleStringProperty();
私有最终StringProperty位置=新SimpleStringProperty();
人员(字符串名称、字符串标题、字符串位置){
this.name.set(name);
此.title.set(title);
此.location.set(位置);
}
公共字符串getName(){
返回name.get();
}
公共void集合名(字符串名){
this.name.set(name);
}
公共字符串属性nameProperty(){
返回名称;
}
公共字符串getTitle(){
返回title.get();
}
公共无效集合标题(字符串标题){
此.title.set(title);
}
公共财产所有权{
返回标题;
}
公共字符串getLocation(){
返回位置。