代码之家  ›  专栏  ›  技术社区  ›  Harkan

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

  •  0
  • Harkan  · 技术社区  · 6 年前

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

    我试了好几个小时的布局,但没有一个简单的设计。

    我的背景是一个黑色的矩形。我希望标题在屏幕顶部居中,我的菜单在标题下方居中。

    这是我的mvce:

    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();
            }
        }
    }
    

    1 回复  |  直到 6 年前
        1
  •  2
  •   fabian    6 年前

    我的背景是黑色的 Rectangle . [...] 另外,我想舞台的大小要固定到 矩形

    简单地为背景分配一个背景会简单得多 StackPane . 这将允许您调整大小 MenuView 并保持背景的大小与背景的大小相同 菜单视图

    防止窗口的大小调整应该为 Stage 使用 setResizable .

    您正在使用“不健康”数量的转换属性(我指的是 translateX translateY

    以下结构将更适合预期结果:

    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 .