JavaFX SplitMenuButton设置箭头的位置

时间:2016-05-30 10:00:25

标签: java button javafx

正常的SplitMenuButton如下所示: Standard SplitMenuButton

我想在JavaFX SplitMenuButton中更改箭头的位置,但是如果有一个配置会给我这样的东西,就无法解决: SplitMenuButton with arrow on bottom

这是可能的还是我需要创建一个定制版本的分割菜单按钮?

1 个答案:

答案 0 :(得分:0)

使用JavaFX SplitMenuButton之后,我找不到任何方法可以轻松地重现我需要的东西(默认右边的菜单箭头)。我创建了一个按钮类,允许箭头按钮放在主按钮的任何一侧(甚至隐藏)。这个课程没有得到完善,但能胜任。这个示例类的优点是箭头侧可以动态更改。

课程代码:

public class MyButton extends Group {

private ButtonBase label;
private ButtonBase arrowButton;
protected ContextMenu popup;
private ObjectProperty<SplitMode> splitMode;
private DoubleProperty sizeBinding;
private DoubleProperty layoutBinding;
private double oldSizeValue;
private double oldLayoutValue;
private PseudoClass layoutClass;

//
private static PseudoClass TOP_PSEUDO_CLASS = PseudoClass.getPseudoClass("top");
private static PseudoClass BOTTOM_PSEUDO_CLASS = PseudoClass.getPseudoClass("bottom");
private static PseudoClass LEFT_PSEUDO_CLASS = PseudoClass.getPseudoClass("left");
private static PseudoClass RIGHT_PSEUDO_CLASS = PseudoClass.getPseudoClass("right");
private static PseudoClass HIDDEN_PSEUDO_CLASS = PseudoClass.getPseudoClass("hidden");

public static enum SplitMode {
    SPLIT_TOP, // put arrow buton on top
    SPLIT_RIGHT, // put arrow button on right
    SPLIT_BOTTOM, // bottom
    SPLIT_LEFT, // left
    HIDDEN  // hides the arrow button regardless of visibility
}

private void changeToPseudoClass(PseudoClass newClass) {
    pseudoClassStateChanged(layoutClass, false);
    pseudoClassStateChanged(newClass, true);
    layoutClass = newClass;
}

private void bindHidden() {

    if (sizeBinding != null) {
        sizeBinding.unbind();
        sizeBinding.set(oldSizeValue);
    }
    if (layoutBinding != null) {
        layoutBinding.unbind();
        layoutBinding.set(oldLayoutValue);
    }
    arrowButton.setVisible(false);
    changeToPseudoClass(HIDDEN_PSEUDO_CLASS);
}

private void bindSizeAndLayout(DoubleProperty sizeFrom, ReadOnlyDoubleProperty sizeTo,
        DoubleProperty layoutFrom, ReadOnlyDoubleProperty layoutTo,
        PseudoClass newPseudoClass) {

    if (sizeBinding != null) {
        sizeBinding.unbind();
        sizeBinding.set(oldSizeValue);
    }
    if (layoutBinding != null) {
        layoutBinding.unbind();
        layoutBinding.set(oldLayoutValue);
    }
    oldSizeValue = sizeFrom.get();
    sizeBinding = sizeFrom;
    oldLayoutValue = layoutFrom.get();
    layoutBinding = layoutFrom;
    sizeFrom.bind(sizeTo);
    layoutFrom.bind(layoutTo);
    changeToPseudoClass(newPseudoClass);
    arrowButton.setVisible(true);
}

public void setSplitMode(SplitMode mode) {
    if (splitMode == null) {
        splitMode = new SimpleObjectProperty();
    }
    if (splitMode.get() == mode) {
        return;
    } // no changes needed   
    splitMode.set(mode);

    // set up new bindings
    switch (mode) {
        case SPLIT_BOTTOM:
            // bind arrowbutton width to label width
            // bind arrowbutton starting position to bottom of label
            bindSizeAndLayout(arrowButton.prefWidthProperty(), label.widthProperty(),
                    arrowButton.layoutYProperty(), label.heightProperty(),
                    BOTTOM_PSEUDO_CLASS);
            break;
        case SPLIT_RIGHT:
            // bind arrowbutton height to label height
            bindSizeAndLayout(arrowButton.prefHeightProperty(), label.heightProperty(),
                    arrowButton.layoutXProperty(), label.widthProperty(),
                    RIGHT_PSEUDO_CLASS);
            break;
        case SPLIT_LEFT:
            // bind arrowbutton height to label height
            bindSizeAndLayout(arrowButton.prefHeightProperty(), label.heightProperty(),
                    label.layoutXProperty(), arrowButton.widthProperty(),
                    LEFT_PSEUDO_CLASS);
            break;
        case SPLIT_TOP:
            // bind arrowbutton width to label height
            bindSizeAndLayout(arrowButton.prefWidthProperty(), label.widthProperty(),
                    label.layoutYProperty(), arrowButton.heightProperty(),
                    TOP_PSEUDO_CLASS);
            break;
        case HIDDEN:
            // unbind all and hide button
            bindHidden();
            break;
    }

}

public SplitMode getSplitMode() {
    return (splitMode == null) ? SplitMode.HIDDEN : splitMode.get();
}

public ObjectProperty splitModeProperty() {
    return splitMode;
}

public ButtonBase getButton() {
    return label;
}

public ButtonBase getArrowButton() {
    return arrowButton;
}

// Test suite
public MyButton() {
    this("");
}

public MyButton(String text) {
    this(text, SplitMode.SPLIT_RIGHT);
}

@SuppressWarnings("OverridableMethodCallInConstructor")
public MyButton(String text, SplitMode mode) {
    label = new Button(text);
    label.getStyleClass().setAll("label");
    arrowButton = new Button();
    // bind the managed property to visibility.
    // we dont want to manage an invisible button.
    arrowButton.managedProperty().bind(arrowButton.visibleProperty());
    arrowButton.getStyleClass().setAll("arrow-button");
    getStyleClass().setAll("split-menu-button");
    getChildren().clear();
    getChildren().addAll(label, arrowButton);
    setSplitMode(mode);

}

}// end of class

MyButton类创建其他伪类,以便为每一方启用特殊格式。

示例css:

&#13;
&#13;
.split-menu-button > .label {
    -fx-background-color:  -fx-outer-border, -fx-inner-border, -fx-body-color;
    -fx-background-insets: 0, 1 0 1 1, 2 1 2 2;
    -fx-background-radius: 5 0 0 5, 4 0 0 4, 3 0 0 3;
    -fx-padding: 0.166667em 0.667em 0.25em 0.833333em; /* 2 8 3 10 */
    /* -fx-graphic:url("./icon_32.png"); */
    -fx-content-display: top;
    -fx-alignment: center;
}
.split-menu-button:top > .label {
    -fx-background-insets: 0, 1 0 1 1, 2 1 2 2;
    -fx-background-radius: 0 0 5 5, 0 0 4 4, 0 0 3 3;
}
.split-menu-button:right > .label {
    -fx-background-insets: 0, 1 0 1 1, 2 1 2 2;
    -fx-background-radius: 5 0 0 5, 4 0 0 4, 3 0 0 3;
}
.split-menu-button:bottom > .label {
    -fx-background-insets: 0, 1 0 1 1, 2 1 2 2;
    -fx-background-radius: 5 5 0 0, 4 4 0 0, 3 3 0 0;
}
.split-menu-button:left > .label {
    -fx-background-insets: 0, 1 0 1 1, 2 1 2 2;
    -fx-background-radius: 0 5 5 0, 0 4 4 0, 0 3 3 0;
}
.split-menu-button:hidden > .label {
    -fx-background-insets: 0, 1 0 1 1, 2 1 2 2;
    -fx-background-radius: 5 5 5 5, 4 4 4 4, 3 3 3 3;
}
.split-menu-button > .arrow-button {
    -fx-background-color: -fx-outer-border, -fx-inner-border, -fx-body-color;
    -fx-background-insets: 0, 1, 2;
    -fx-background-radius: 0 5 5 0, 0 4 4 0, 0 3 3 0;
    -fx-padding: 0; /* 0.5em 0.667em 0.5em 0.667em; /* 6 8 6 8 */
    -fx-graphic:url("./arrow.png");
    -fx-alignment:center;
}
.split-menu-button:top > .arrow-button {
    -fx-background-insets: 0, 1, 2;
    -fx-background-radius: 5 5 0 0, 4 4 0 0, 3 3 0 0;
}
.split-menu-button:right > .arrow-button {
    -fx-background-insets: 0, 1, 2;
    -fx-background-radius: 0 5 5 0, 0 4 4 0, 0 3 3 0;
}
.split-menu-button:bottom > .arrow-button {
    -fx-background-insets: 0, 1, 2;
    -fx-background-radius: 0 0 5 5, 0 0 4 4, 0 0 3 3;
}
.split-menu-button:left > .arrow-button {
    -fx-background-insets: 0, 1, 2;
    -fx-background-radius: 5 0 0 5, 4 0 0 4, 3 0 0 3;
}
&#13;
&#13;
&#13;

示例应用程序以测试按钮。

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import MyButton.SplitMode;

public class SimpleTest
    extends Application {

    Stage primaryStage;
    protected ContextMenu popup;

    @Override
    public void start(final Stage stage) throws Exception {
        primaryStage = stage;
        primaryStage.setTitle("SimpleTest.");

        Label label = new Label();
        Button rotate = new Button("Rotate");
        MyButton b = new MyButton("My Test", SplitMode.SPLIT_TOP);
        label.setText(b.getSplitMode().toString());
        StackPane sp = new StackPane();
        sp.setPrefSize(200, 200);
        sp.getStylesheets().add("test.css");

        rotate.setOnAction((ActionEvent t) -> {
            switch(b.getSplitMode()){
                case SPLIT_TOP:
                    b.setSplitMode(SplitMode.SPLIT_RIGHT);
                    break;
                case SPLIT_RIGHT:
                    b.setSplitMode(SplitMode.SPLIT_BOTTOM);
                    break;
                case SPLIT_BOTTOM:
                    b.setSplitMode(SplitMode.SPLIT_LEFT);
                    break;
                case SPLIT_LEFT:
                    b.setSplitMode(SplitMode.HIDDEN);
                    break;
                case HIDDEN:
                    b.setSplitMode(SplitMode.SPLIT_TOP);
                    break;
            }
            label.setText(b.getSplitMode().toString());
        });
        StackPane.setAlignment(label, Pos.TOP_LEFT);
        StackPane.setAlignment(rotate, Pos.TOP_RIGHT);
        sp.getChildren().addAll(b, label, rotate);
        primaryStage.setScene(new Scene(sp));
        primaryStage.toFront();
        primaryStage.show();
    }


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