JavaFX:如何使我的标题和菜单彼此居中[与MVCE]

时间:2018-09-30 12:35:37

标签: javafx menu

我是JavaFX的新手,我正在尝试制作一个可以任意大小的菜单。

我已经尝试了每种布局几个小时,但是我无法完成简单的设计。

我的背景是黑色矩形。我希望标题居中显示在屏幕上方,而我的菜单居中显示在标题下方。 另外,我希望将舞台大小固定为“矩形”大小,这样我们就不会在背景上看到白色。

这是我的MV:

package mvce_poneymon_menu;

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Mvce_poneymon_menu extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        MenuView menuView = new MenuView(600, 600);

    Group root = new Group();
    Scene scene = new Scene(root);

    stage.setTitle("Poneymon");
    stage.setScene(scene);

    root.getChildren().add(menuView);
    menuView.requestFocus();

    stage.show();
}

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

MenuView.java:

package mvce_poneymon_menu;

import javafx.animation.TranslateTransition;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.util.Duration;


public class MenuView extends StackPane {
    static final Font FONT = Font.font("", FontWeight.BOLD, 50);

    int width;
    int height;

    VBox menuBox;
    int currentItem = 0;

    public MenuView(int w, int h) {
        width = w;
        height = h;

        createContent();
        setOnKeyPressedEvent();
    }

    private void createContent() {
        GridPane grid = new GridPane();

        MenuItem exitItem = new MenuItem("Exit");
        exitItem.setOnActivate(() -> System.exit(0));

        menuBox = new VBox(10,
                    new MenuItem("Start a game"),
                    new MenuItem("Parameters"),
                    exitItem);
        menuBox.setAlignment(Pos.CENTER);
        menuBox.setTranslateX(360);

        getMenuItem(0).setActive(true);

        HBox title = (HBox)createTitle("Poneymon");
        grid.add(title, 0, 0);
        grid.add(menuBox, 0, 1);

        Rectangle bg = new Rectangle(width, height);
        grid.setTranslateY(25);
        this.getChildren().addAll(bg, grid);
    }

    private Node createTitle(String title) {
        HBox letters = new HBox(0);
        letters.setAlignment(Pos.CENTER);

        for (int i = 0; i < title.length(); i++) {
            Text letter = new Text(title.charAt(i) + "");
            letter.setFont(FONT);
            letter.setFill(Color.WHITE);
            letters.getChildren().add(letter);

            TranslateTransition tt = new TranslateTransition(Duration.seconds(2), letter);
            tt.setDelay(Duration.millis(i * 50));
            tt.setToY(-25);
            tt.setAutoReverse(true);
            tt.setCycleCount(TranslateTransition.INDEFINITE);
            tt.play();
        }

        return letters;
    }

    private MenuItem getMenuItem(int index) {
        return (MenuItem)menuBox.getChildren().get(index);
    }

    private void setOnKeyPressedEvent() {
        this.setOnKeyPressed(new EventHandler<KeyEvent>() {
            public void handle(KeyEvent e) {
                if (e.getCode() == KeyCode.UP) {
                    if (currentItem > 0) {
                        getMenuItem(currentItem).setActive(false);
                        getMenuItem(--currentItem).setActive(true);
                    }
                }

                if (e.getCode() == KeyCode.DOWN) {
                    if (currentItem < menuBox.getChildren().size() - 1) {
                        getMenuItem(currentItem).setActive(false);
                        getMenuItem(++currentItem).setActive(true);
                    }
                }

                if (e.getCode() == KeyCode.ENTER) {
                    getMenuItem(currentItem).activate();
                }
            }
        });
    }
}

MenuItem.java:

package mvce_poneymon_menu;

import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;


public class MenuItem extends HBox {
    static final Font FONT = Font.font("", FontWeight.BOLD, 30);

    private TriCircle c1 = new TriCircle();
    private TriCircle c2 = new TriCircle();
    private Text text;
    private Runnable script;

    private static class TriCircle extends Parent {
        public TriCircle() {
            Shape shape1 = Shape.subtract(new Circle(5), new Circle(2));
            shape1.setFill(Color.WHITE);

            Shape shape2 = Shape.subtract(new Circle(5), new Circle(2));
            shape2.setFill(Color.WHITE);
            shape2. setTranslateX(5);

            Shape shape3 = Shape.subtract(new Circle(5), new Circle(2));
            shape3.setFill(Color.WHITE);
            shape3.setTranslateX(2.5);
            shape3.setTranslateY(-5);

            getChildren().addAll(shape1, shape2, shape3);

            setEffect(new GaussianBlur(2));
        }
    }

    public MenuItem(String name) {
        super(15);
        setAlignment(Pos.CENTER);

        text = new Text(name);
        text.setFont(FONT);
        text.setEffect(new GaussianBlur(2));

        getChildren().addAll(c1, text, c2);
        setActive(false);
        setOnActivate(() -> System.out.println(name + " activated"));
    }

    public void setActive(boolean b) {
        c1.setVisible(b);
        c2.setVisible(b);
        text.setFill(b ? Color.WHITE : Color.GREY);
    }

    public void setOnActivate(Runnable r) {
        script = r;
    }

    public void activate() {
        if (script != null) {
            script.run();
        }
    }
}

我敢肯定这很简单,但我想不通:c

1 个答案:

答案 0 :(得分:2)

  

我的背景是黑色的Rectangle。 [...]另外,我希望将舞台大小固定为Rectangle大小,以便我们在背景中看不到白色。

简单地为StackPane分配背景会简单得多。这样一来,您就可以调整MenuView的大小,并使背景的大小与MenuView的大小相同,而无需附加逻辑。

应该为Stage使用setResizable来防止窗口大小的改变。

  

我希望标题居中显示在屏幕上方,而我的菜单居中显示在标题下方。

您使用的转换属性数量“不健康”。 (在这种情况下,我指的是translateXtranslateY。)父级布局不考虑这些属性。在布局过程中,将节点放置在不进行任何转换的同一节点的位置,并且渲染算法会考虑这些转换。

下面的结构更适合所需的结果:

MenuView (root)
  |- VBox (place menu items below title)
      |- HBox (title container) 
          |- ...
      |- MenuItem
      |- MenuItem
      |- MenuItem

要获得标题容器的正确大小,我建议在内容周围使用填充。

我还要更改其他几件事:

  • Shape shape1 = Shape.subtract(new Circle(5), new Circle(2));
    shape1.setFill(Color.WHITE);
    

    我建议将其更改为带有笔触的圆圈,而不是相交的形状。

  • 我建议不要对项目MenuItem中的每个孩子分别进行模糊处理。
  • TriCircle类除设置节点外不包含任何逻辑。可以(并且应该)将其替换为创建包含圆圈的Group的方法。
@Override
public void start(Stage stage) {
    MenuView menuView = new MenuView(600, 600);
    Scene scene = new Scene(menuView);

    stage.setTitle("Poneymon");
    stage.setScene(scene);
    menuView.requestFocus();

    stage.setResizable(false); // prevent resizing of stage
    stage.show();
}
    
public class MenuView extends StackPane {

    static final Font FONT = Font.font("", FontWeight.BOLD, 50);
    int currentItem = 0;

    public MenuView(int w, int h) {
        setPrefSize(w, h);

        createContent();
        setOnKeyPressedEvent();
    }

    private List<MenuItem> menuItems;

    private void createContent() {

        MenuItem exitItem = new MenuItem("Exit");
        exitItem.setOnActivate(() -> Platform.exit());

        menuItems = Arrays.asList(
                new MenuItem("Start a game"),
                new MenuItem("Parameters"),
                exitItem);

        VBox container = new VBox(10, createTitle("Poneymon"));
        container.getChildren().addAll(menuItems);
        container.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);

        getMenuItem(0).setActive(true);

        setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));

        getChildren().add(container);
    }

    private Node createTitle(String title) {
        final double movement = 25;
        HBox letters = new HBox();
        letters.setAlignment(Pos.CENTER);

        // add space on top equla to the upwards movement of the letters
        letters.setPadding(new Insets(movement, 0, 0, 0));

        for (int i = 0; i < title.length(); i++) {
            Text letter = new Text(title.charAt(i) + "");
            letter.setFont(FONT);
            letter.setFill(Color.WHITE);
            letters.getChildren().add(letter);

            TranslateTransition tt = new TranslateTransition(Duration.seconds(2), letter);
            tt.setDelay(Duration.millis(i * 50));
            tt.setToY(-movement);
            tt.setAutoReverse(true);
            tt.setCycleCount(TranslateTransition.INDEFINITE);
            tt.play();
        }

        return letters;
    }

    private MenuItem getMenuItem(int index) {
        return menuItems.get(index);
    }

    private void setOnKeyPressedEvent() {
        this.setOnKeyPressed(new EventHandler<KeyEvent>() {
            public void handle(KeyEvent e) {
                if (e.getCode() == KeyCode.UP) {
                    if (currentItem > 0) {
                        getMenuItem(currentItem).setActive(false);
                        getMenuItem(--currentItem).setActive(true);
                    }
                }

                if (e.getCode() == KeyCode.DOWN) {
                    if (currentItem < menuItems.size() - 1) {
                        getMenuItem(currentItem).setActive(false);
                        getMenuItem(++currentItem).setActive(true);
                    }
                }

                if (e.getCode() == KeyCode.ENTER) {
                    getMenuItem(currentItem).activate();
                }
            }
        });
    }
}
public class MenuItem extends HBox {

    static final Font FONT = Font.font("", FontWeight.BOLD, 30);

    private Group c1 = createTriCircle();
    private Group c2 = createTriCircle();
    private Text text;
    private Runnable script;

    private static Circle createCircle(double centerX, double centerY) {
        final double innerRadius = 2;
        final double outerRadius = 5;
        Circle circle = new Circle(centerX, centerY, (innerRadius + outerRadius) / 2, null);
        circle.setStroke(Color.WHITE);
        circle.setStrokeWidth(outerRadius - innerRadius);
        return circle;
    }

    private static Group createTriCircle() {
        return new Group(
                createCircle(0, 0),
                createCircle(5, 0),
                createCircle(2.5, -5));
    }

    public MenuItem(String name) {
        super(15);
        setAlignment(Pos.CENTER);

        text = new Text(name);
        text.setFont(FONT);

        setEffect(new GaussianBlur(2));

        getChildren().addAll(c1, text, c2);
        setActive(false);
        setOnActivate(() -> System.out.println(name + " activated"));
    }

    public void setActive(boolean b) {
        c1.setVisible(b);
        c2.setVisible(b);
        text.setFill(b ? Color.WHITE : Color.GREY);
    }

    public void setOnActivate(Runnable r) {
        script = r;
    }

    public void activate() {
        if (script != null) {
            script.run();
        }
    }
}

要调整标题和菜单项之间的距离,可以使用VBox.setMargin