我是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
我的背景是一个黑色的矩形
。[…]另外,我希望舞台大小固定为Rectgle
大小,这样我们就不会在背景上看到白色。
简单地为StackPane
分配背景会简单得多。这将允许您调整MenuView
的大小,并保持背景的大小与MenuView
的大小相同,而无需额外的逻辑。
应使用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);
我建议将此更改为带有笔画的圆圈,而不是交叉形状。
@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
。