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:
.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;
}
サンプルアプリケーションは、ボタンをテストします。
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);
}
}