问题描述
正常的SplitMenuButton如下所示:
A normal SplitMenuButton looks like this:Standard SplitMenuButton
我想改变JavaFX SplitMenuButton中箭头的位置但是如果有一个配置会给我这样的东西就无法解决:
I would like to change the location of the arrow in a JavaFX SplitMenuButton but cannot work out if there is even a configuration that will give me something like this:SplitMenuButton with arrow on bottom
是这可能还是我需要创建一个定制版本的分割菜单按钮?
Is this possible or will I need to create a bespoke version of split menu button?
推荐答案
在玩JavaFX SplitMenuButton后我无法做到找到任何方法轻松重现我需要的东西(默认右边的菜单箭头)。我创建了一个按钮类,允许箭头按钮放在主按钮的任何一侧(甚至隐藏)。这个课程没有得到完善,但能胜任。这个示例类的优点是箭头侧可以动态更改。
After playing with the JavaFX SplitMenuButton I could not find any way to easily reproduce what I need (menu arrow on side other than default right). I created a button class that allows the arrow button to be placed on any side of the main button( or even hidden). The class is not polished but does the job. The great thing about this sample class is that the arrow side can be changed dynamically.
课程代码:
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类创建额外的伪类以为每一方启用特殊格式。
MyButton class creates additional pseudo classes to enable special formatting for each side.
示例css:
.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;
}
示例应用程序测试按钮。
Sample Application to test button.
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);
}
}
这篇关于JavaFX SplitMenuButton设置箭头的位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!