自定义组合框,JavaFX 中的居中文本

Custom combobox, centered text in JavaFX

我想将组合框按钮单元格中的文本居中对齐并使其成为默认行为。我已经知道如何在创建组合框对象时执行此操作。我如何在组合框的派生 class 中实现该方法?

box.setButtonCell(new ListCell<String>() {
    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (item != null) {
            setText(item);
            setAlignment(Pos.CENTER_RIGHT);
            Insets old = getPadding();
            setPadding(new Insets(old.getTop(), 0, old.getBottom(), 0));
        }
    }
});
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;

public class CustomComboBox<T> extends ComboBox<T> {

    public CustomComboBox() {
        super();
    }

    public CustomComboBox(ObservableList<T> items) {
        super(items);
    }

    public BooleanProperty centeredProperty() { return centered; }
    public final void setCentered(boolean value) { centeredProperty().set(value); }
    public final boolean isCentered() { return centeredProperty().get(); }
    private BooleanProperty centered = new SimpleBooleanProperty(this, "centered", false) {

        private ListCell<T> originalBttonCell =  getButtonCell();

        @Override 
        protected void invalidated() {
            if(get()) {
                setButtonCell(new ListCell<T>() {
                    @Override
                    public void updateItem(T item, boolean empty) {
                        super.updateItem(item, empty);
                        if (item != null) {
                            setText(item.toString());
                            setAlignment(Pos.CENTER_RIGHT);
                            Insets old = getPadding();
                            setPadding(new Insets(old.getTop(), 0, old.getBottom(), 0));
                        }
                    }
                });
            }
            else {
                setButtonCell(originalBttonCell);
            }
        }
    };

}

用法

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class CustomComboBoxTest extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {

        VBox p = new VBox();

        CustomComboBox<String> box = new CustomComboBox<>(FXCollections.observableArrayList("Item 1", "Item 2", "Item 3"));
        box.setValue("Item 2");

        Button b = new Button("Change centered");
        b.setOnAction( e -> {box.setCentered(!box.isCentered());});

        p.getChildren().addAll(box, b);

        Scene scene = new Scene(p);

        primaryStage.setScene(scene);

        primaryStage.setWidth(300);
        primaryStage.setHeight(200);

        primaryStage.show();

    }

}

当然,您可以将代码(box 替换为 this)移动到 class 的构造函数中(或将其放在初始化块中)。

但是为什么要创建一个子class,CSS 很容易让你做到这一点:

添加如下样式sheet到场景

.combo-box>.list-cell {
    -fx-alignment: center;
}

如果您希望能够为单个 ComboBoxes 更改此行为,您只需添加一种方法来区分居中的 ComboBoxes 与其他 css 选择器,例如class

java代码

comboBox.getStyleClass().add("center-aligned");

CSS

.combo-box.center-aligned>.list-cell {
    -fx-alignment: center;
}

使用伪class 可以让您更轻松地打开和关闭行为。以下代码中心在偶数索引处对齐每个项目:

@Override
public void start(Stage primaryStage) {
    ComboBox<String> combo = new ComboBox<>();

    PseudoClass centerAligned = PseudoClass.getPseudoClass("center-aligned");
    combo.pseudoClassStateChanged(centerAligned, true);
    combo.getSelectionModel().selectedIndexProperty().addListener((o, oldValue, newValue)
            -> combo.pseudoClassStateChanged(centerAligned, newValue.intValue() % 2 == 0));

    for (int i = 0; i < 5; i++) {
        combo.getItems().add("item " + i);
    }
    combo.setValue("item 0");
    combo.setMaxWidth(Double.MAX_VALUE);

    VBox root = new VBox(combo);
    root.setFillWidth(true);
    root.setPrefWidth(300);
    Scene scene = new Scene(root);
    scene.getStylesheets().add("style.css");
    primaryStage.setScene(scene);
    primaryStage.show();
}

style.css

.combo-box:center-aligned>.list-cell {
    -fx-alignment: center;
}