JavaFX: как расположить заголовок и меню в центре друг друга [WITH MVCE] - PullRequest
0 голосов
/ 30 сентября 2018

Я новичок в 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();
        }
    }
}

Я уверен, что это очень просто, но я не могу понять это: c

1 Ответ

0 голосов
/ 30 сентября 2018

Мой фон черный Rectangle.[...] Кроме того, я хочу, чтобы размер сцены был установлен в размере Rectangle, чтобы мы не видели белый фон.

Было бы намного проще просто назначитьфон для StackPane.Это позволит вам изменить размер MenuView и сохранить размер фона таким же, как и размер MenuView без дополнительной логики.

Для Stage следует предотвратить изменение размера окна, используя *1012*setResizable.

Я хочу, чтобы заголовок располагался по центру в верхней части экрана, а мое меню располагалось по центру под заголовком.

Вы используете«нездоровое» количество свойств трансформации.(В этом случае я ссылаюсь на translateX и translateY.) Эти свойства не учитываются родительским макетом;во время компоновки узлы располагаются там, где будет располагаться один и тот же узел без какого-либо преобразования, и алгоритм рендеринга учитывает эти преобразования.

Imho, следующая структура будет лучше соответствовать желаемому результату:

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...