|
|
|
|
@ -16,6 +16,10 @@ import javafx.scene.text.Font;
|
|
|
|
|
import javafx.scene.text.FontWeight;
|
|
|
|
|
import javafx.scene.text.Text;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 级别选择视图类,提供用户选择题目难度级别的界面.
|
|
|
|
|
* 包含小学、初中、高中三个难度级别的选择按钮.
|
|
|
|
|
*/
|
|
|
|
|
public class LevelSelectionView {
|
|
|
|
|
private Scene scene;
|
|
|
|
|
private final SceneManager sceneManager;
|
|
|
|
|
@ -25,12 +29,20 @@ public class LevelSelectionView {
|
|
|
|
|
private Label usernameLabel;
|
|
|
|
|
private Text avatarText;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构造函数,初始化级别选择视图.
|
|
|
|
|
*
|
|
|
|
|
* @param sceneManager 场景管理器,用于界面导航.
|
|
|
|
|
*/
|
|
|
|
|
public LevelSelectionView(SceneManager sceneManager) {
|
|
|
|
|
this.sceneManager = sceneManager;
|
|
|
|
|
this.currentUsername = sceneManager.getCurrentUserName();
|
|
|
|
|
createScene();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建主场景,包含用户信息栏和级别选择内容.
|
|
|
|
|
*/
|
|
|
|
|
private void createScene() {
|
|
|
|
|
// 创建主容器
|
|
|
|
|
VBox mainContainer = new VBox();
|
|
|
|
|
@ -47,31 +59,45 @@ public class LevelSelectionView {
|
|
|
|
|
scene = new Scene(mainContainer, 450, 550);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建用户信息栏,包含用户头像和用户名显示.
|
|
|
|
|
*
|
|
|
|
|
* @return HBox 用户信息栏布局容器.
|
|
|
|
|
*/
|
|
|
|
|
private HBox createUserInfoBar() {
|
|
|
|
|
HBox userInfoBar = new HBox(15);
|
|
|
|
|
userInfoBar.setAlignment(Pos.CENTER_LEFT);
|
|
|
|
|
userInfoBar.setPadding(new Insets(0, 0, 30, 0));
|
|
|
|
|
userInfoBar.setStyle("-fx-border-color: #e0e0e0; -fx-border-width: 0 0 1 0; -fx-padding: 0 0 15 0;");
|
|
|
|
|
userInfoBar.setStyle("-fx-border-color: #e0e0e0; "
|
|
|
|
|
+ "-fx-border-width: 0 0 1 0; -fx-padding: 0 0 15 0;");
|
|
|
|
|
// 用户名标签
|
|
|
|
|
usernameLabel = new Label(currentUsername != null ? currentUsername : "用户");
|
|
|
|
|
usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
|
|
|
|
|
usernameLabel.setStyle("-fx-text-fill: #2c3e50;");
|
|
|
|
|
// 间隔
|
|
|
|
|
Region spacer = new Region();
|
|
|
|
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
|
|
|
|
// 创建头像容器
|
|
|
|
|
VBox avatarContainer = createAvatarContainer();
|
|
|
|
|
userInfoBar.getChildren().addAll(avatarContainer, usernameLabel, spacer);
|
|
|
|
|
return userInfoBar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建圆形头像容器
|
|
|
|
|
/**
|
|
|
|
|
* 创建头像容器,包含圆形头像背景和用户首字母.
|
|
|
|
|
*
|
|
|
|
|
* @return VBox 头像容器布局.
|
|
|
|
|
*/
|
|
|
|
|
private VBox createAvatarContainer() {
|
|
|
|
|
VBox avatarContainer = new VBox();
|
|
|
|
|
avatarContainer.setAlignment(Pos.CENTER);
|
|
|
|
|
avatarContainer.setPrefSize(50, 50);
|
|
|
|
|
|
|
|
|
|
// 创建圆形头像背景
|
|
|
|
|
Circle avatarCircle = new Circle(22);
|
|
|
|
|
avatarCircle.setFill(Color.web("#4CAF50")); // 统一的绿色背景
|
|
|
|
|
avatarCircle.setStroke(Color.WHITE);
|
|
|
|
|
avatarCircle.setStrokeWidth(2);
|
|
|
|
|
|
|
|
|
|
// 添加阴影效果
|
|
|
|
|
avatarCircle.setStyle("-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 5, 0.3, 2, 2);");
|
|
|
|
|
|
|
|
|
|
// 添加首字母文本
|
|
|
|
|
avatarText = new Text(getFirstLetter());
|
|
|
|
|
avatarText.setFill(Color.WHITE);
|
|
|
|
|
avatarText.setFont(Font.font("Arial", FontWeight.BOLD, 16));
|
|
|
|
|
|
|
|
|
|
// 创建圆形头像背景
|
|
|
|
|
Circle avatarCircle = createAvatarCircle();
|
|
|
|
|
// 使用StackPane将文本放在圆形中心
|
|
|
|
|
javafx.scene.layout.StackPane avatarStack = new javafx.scene.layout.StackPane();
|
|
|
|
|
avatarStack.getChildren().addAll(avatarCircle, avatarText);
|
|
|
|
|
@ -79,101 +105,190 @@ public class LevelSelectionView {
|
|
|
|
|
avatarStack.setAlignment(Pos.CENTER);
|
|
|
|
|
|
|
|
|
|
avatarContainer.getChildren().add(avatarStack);
|
|
|
|
|
return avatarContainer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用户名标签
|
|
|
|
|
usernameLabel = new Label(currentUsername != null ? currentUsername : "用户");
|
|
|
|
|
usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
|
|
|
|
|
usernameLabel.setStyle("-fx-text-fill: #2c3e50;");
|
|
|
|
|
|
|
|
|
|
// 间隔
|
|
|
|
|
Region spacer = new Region();
|
|
|
|
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
|
|
|
|
|
|
|
|
|
userInfoBar.getChildren().addAll(avatarContainer, usernameLabel, spacer);
|
|
|
|
|
/**
|
|
|
|
|
* 创建圆形头像背景.
|
|
|
|
|
*
|
|
|
|
|
* @return Circle 圆形头像背景对象.
|
|
|
|
|
*/
|
|
|
|
|
private Circle createAvatarCircle() {
|
|
|
|
|
Circle avatarCircle = new Circle(22);
|
|
|
|
|
avatarCircle.setFill(Color.web("#4CAF50")); // 统一的绿色背景
|
|
|
|
|
avatarCircle.setStroke(Color.WHITE);
|
|
|
|
|
avatarCircle.setStrokeWidth(2);
|
|
|
|
|
|
|
|
|
|
return userInfoBar;
|
|
|
|
|
// 添加阴影效果
|
|
|
|
|
avatarCircle.setStyle("-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 5, 0.3, 2, 2);");
|
|
|
|
|
return avatarCircle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建级别选择内容区域,包含标题和级别选择按钮.
|
|
|
|
|
*
|
|
|
|
|
* @return VBox 级别选择内容区域布局容器.
|
|
|
|
|
*/
|
|
|
|
|
private VBox createSelectionContent() {
|
|
|
|
|
VBox selectionContent = new VBox(25);
|
|
|
|
|
selectionContent.setPadding(new Insets(30, 20, 20, 20));
|
|
|
|
|
selectionContent.setAlignment(Pos.CENTER);
|
|
|
|
|
|
|
|
|
|
// 创建标题区域
|
|
|
|
|
VBox titleSection = createTitleSection();
|
|
|
|
|
|
|
|
|
|
// 创建级别按钮区域
|
|
|
|
|
VBox levelButtonsSection = createLevelButtonsSection();
|
|
|
|
|
|
|
|
|
|
// 创建返回按钮
|
|
|
|
|
Button backButton = createBackButton();
|
|
|
|
|
|
|
|
|
|
selectionContent.getChildren().addAll(titleSection, levelButtonsSection, backButton);
|
|
|
|
|
|
|
|
|
|
return selectionContent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建标题区域,包含主标题和副标题.
|
|
|
|
|
*
|
|
|
|
|
* @return VBox 标题区域布局容器.
|
|
|
|
|
*/
|
|
|
|
|
private VBox createTitleSection() {
|
|
|
|
|
VBox titleSection = new VBox(5);
|
|
|
|
|
titleSection.setAlignment(Pos.CENTER);
|
|
|
|
|
|
|
|
|
|
Label titleLabel = new Label("选择题目级别");
|
|
|
|
|
titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 26));
|
|
|
|
|
titleLabel.setStyle("-fx-text-fill: #2c3e50;");
|
|
|
|
|
|
|
|
|
|
// 添加副标题
|
|
|
|
|
Label subtitleLabel = new Label("请选择适合您的学习级别");
|
|
|
|
|
subtitleLabel.setFont(Font.font("Arial", 14));
|
|
|
|
|
subtitleLabel.setStyle("-fx-text-fill: #7f8c8d; -fx-padding: 0 0 10 0;");
|
|
|
|
|
|
|
|
|
|
titleSection.getChildren().addAll(titleLabel, subtitleLabel);
|
|
|
|
|
return titleSection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建级别按钮区域,包含三个难度级别的选择按钮.
|
|
|
|
|
*
|
|
|
|
|
* @return VBox 级别按钮区域布局容器.
|
|
|
|
|
*/
|
|
|
|
|
private VBox createLevelButtonsSection() {
|
|
|
|
|
VBox levelButtonsSection = new VBox(15);
|
|
|
|
|
levelButtonsSection.setAlignment(Pos.CENTER);
|
|
|
|
|
|
|
|
|
|
Button primaryButton = createLevelButton("小学题目", "#4CAF50", "#45a049");
|
|
|
|
|
Button juniorButton = createLevelButton("初中题目", "#2196F3", "#1976D2");
|
|
|
|
|
Button seniorButton = createLevelButton("高中题目", "#9C27B0", "#7B1FA2");
|
|
|
|
|
|
|
|
|
|
Button backButton = new Button("返回主菜单");
|
|
|
|
|
backButton.setStyle("-fx-background-color: #95a5a6; -fx-text-fill: white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;");
|
|
|
|
|
backButton.setPrefSize(180, 45);
|
|
|
|
|
backButton.setOnMouseEntered(e -> backButton.setStyle("-fx-background-color: #7f8c8d; -fx-text-fill: white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;"));
|
|
|
|
|
backButton.setOnMouseExited(e -> backButton.setStyle("-fx-background-color: #95a5a6; -fx-text-fill: white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;"));
|
|
|
|
|
setupLevelButtonAction(primaryButton, "小学");
|
|
|
|
|
setupLevelButtonAction(juniorButton, "初中");
|
|
|
|
|
setupLevelButtonAction(seniorButton, "高中");
|
|
|
|
|
|
|
|
|
|
primaryButton.setOnAction(e -> {
|
|
|
|
|
String selectedLevel = "小学";
|
|
|
|
|
sceneManager.getQuestionCountView().setLevel(selectedLevel);
|
|
|
|
|
sceneManager.showQuestionCountView();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
juniorButton.setOnAction(e -> {
|
|
|
|
|
String selectedLevel = "初中";
|
|
|
|
|
sceneManager.getQuestionCountView().setLevel(selectedLevel);
|
|
|
|
|
sceneManager.showQuestionCountView();
|
|
|
|
|
});
|
|
|
|
|
levelButtonsSection.getChildren().addAll(primaryButton, juniorButton, seniorButton);
|
|
|
|
|
return levelButtonsSection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seniorButton.setOnAction(e -> {
|
|
|
|
|
String selectedLevel = "高中";
|
|
|
|
|
sceneManager.getQuestionCountView().setLevel(selectedLevel);
|
|
|
|
|
/**
|
|
|
|
|
* 设置级别按钮的点击事件.
|
|
|
|
|
*
|
|
|
|
|
* @param button 级别按钮.
|
|
|
|
|
* @param level 对应的难度级别.
|
|
|
|
|
*/
|
|
|
|
|
private void setupLevelButtonAction(Button button, String level) {
|
|
|
|
|
button.setOnAction(e -> {
|
|
|
|
|
sceneManager.getQuestionCountView().setLevel(level);
|
|
|
|
|
sceneManager.showQuestionCountView();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
backButton.setOnAction(e -> {
|
|
|
|
|
sceneManager.showMainMenuView();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
selectionContent.getChildren().addAll(
|
|
|
|
|
titleLabel, subtitleLabel, primaryButton, juniorButton, seniorButton, backButton
|
|
|
|
|
);
|
|
|
|
|
/**
|
|
|
|
|
* 创建返回按钮.
|
|
|
|
|
*
|
|
|
|
|
* @return Button 返回主菜单按钮.
|
|
|
|
|
*/
|
|
|
|
|
private Button createBackButton() {
|
|
|
|
|
Button backButton = new Button("返回主菜单");
|
|
|
|
|
backButton.setStyle("-fx-background-color: #95a5a6; -fx-text-fill: "
|
|
|
|
|
+ "white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;");
|
|
|
|
|
backButton.setPrefSize(180, 45);
|
|
|
|
|
setupButtonHoverEffect(backButton, "#95a5a6", "#7f8c8d");
|
|
|
|
|
backButton.setOnAction(e -> sceneManager.showMainMenuView());
|
|
|
|
|
|
|
|
|
|
return selectionContent;
|
|
|
|
|
return backButton;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建级别选择按钮.
|
|
|
|
|
*
|
|
|
|
|
* @param text 按钮显示文本.
|
|
|
|
|
* @param color 按钮正常状态颜色.
|
|
|
|
|
* @param hoverColor 按钮悬停状态颜色.
|
|
|
|
|
* @return Button 配置好的级别选择按钮.
|
|
|
|
|
*/
|
|
|
|
|
private Button createLevelButton(String text, String color, String hoverColor) {
|
|
|
|
|
Button button = new Button(text);
|
|
|
|
|
button.setStyle(String.format(
|
|
|
|
|
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; -fx-font-weight: bold; " +
|
|
|
|
|
"-fx-background-radius: 12; -fx-padding: 12 30; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);",
|
|
|
|
|
"-fx-background-color: %s; -fx-text-fill: "
|
|
|
|
|
+ "white; -fx-font-size: 16px; -fx-font-weight: bold;-fx-background-radius: 12;"
|
|
|
|
|
+ "-fx-padding: 12 30;-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);",
|
|
|
|
|
color
|
|
|
|
|
));
|
|
|
|
|
button.setPrefSize(220, 60);
|
|
|
|
|
button.setOnMouseEntered(e -> button.setStyle(String.format(
|
|
|
|
|
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; -fx-font-weight: bold; " +
|
|
|
|
|
"-fx-background-radius: 12; -fx-padding: 12 30; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 10, 0.4, 3, 3);",
|
|
|
|
|
hoverColor
|
|
|
|
|
)));
|
|
|
|
|
button.setOnMouseExited(e -> button.setStyle(String.format(
|
|
|
|
|
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; -fx-font-weight: bold; " +
|
|
|
|
|
"-fx-background-radius: 12; -fx-padding: 12 30; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);",
|
|
|
|
|
color
|
|
|
|
|
)));
|
|
|
|
|
|
|
|
|
|
setupButtonHoverEffect(button, color, hoverColor);
|
|
|
|
|
|
|
|
|
|
return button;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置按钮的悬停效果.
|
|
|
|
|
*
|
|
|
|
|
* @param button 需要设置悬停效果的按钮.
|
|
|
|
|
* @param normalColor 正常状态颜色.
|
|
|
|
|
* @param hoverColor 悬停状态颜色.
|
|
|
|
|
*/
|
|
|
|
|
private void setupButtonHoverEffect(Button button, String normalColor, String hoverColor) {
|
|
|
|
|
String normalStyle = String.format(
|
|
|
|
|
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: %s;"
|
|
|
|
|
+ " -fx-background-radius: %s; -fx-padding: %s; -fx-effect: "
|
|
|
|
|
+ "dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);",
|
|
|
|
|
normalColor,
|
|
|
|
|
button.getStyle().contains("16px") ? "16px" : "14px",
|
|
|
|
|
button.getStyle().contains("12") ? "12" : "8",
|
|
|
|
|
button.getStyle().contains("12 30") ? "12 30" : "8 20"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
String hoverStyle = String.format(
|
|
|
|
|
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: %s;"
|
|
|
|
|
+ " -fx-background-radius: %s; -fx-padding: %s; -fx-effect:"
|
|
|
|
|
+ " dropshadow(gaussian, rgba(0,0,0,0.3), 10, 0.4, 3, 3);",
|
|
|
|
|
hoverColor,
|
|
|
|
|
button.getStyle().contains("16px") ? "16px" : "14px",
|
|
|
|
|
button.getStyle().contains("12") ? "12" : "8",
|
|
|
|
|
button.getStyle().contains("12 30") ? "12 30" : "8 20"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
button.setOnMouseEntered(e -> button.setStyle(hoverStyle));
|
|
|
|
|
button.setOnMouseExited(e -> button.setStyle(normalStyle));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取用户名的首字母,用于头像显示.
|
|
|
|
|
*
|
|
|
|
|
* @return String 用户名的首字母大写,如果用户名为空则返回"U".
|
|
|
|
|
*/
|
|
|
|
|
private String getFirstLetter() {
|
|
|
|
|
return currentUsername != null && !currentUsername.isEmpty() ?
|
|
|
|
|
currentUsername.substring(0, 1).toUpperCase() : "U";
|
|
|
|
|
return currentUsername != null && !currentUsername.isEmpty()
|
|
|
|
|
? currentUsername.substring(0, 1).toUpperCase() : "U";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加更新用户名的方法
|
|
|
|
|
/**
|
|
|
|
|
* 更新用户名显示.
|
|
|
|
|
*
|
|
|
|
|
* @param username 新的用户名.
|
|
|
|
|
*/
|
|
|
|
|
public void updateUsername(String username) {
|
|
|
|
|
this.currentUsername = username;
|
|
|
|
|
if (usernameLabel != null) {
|
|
|
|
|
@ -184,6 +299,11 @@ public class LevelSelectionView {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取当前场景.
|
|
|
|
|
*
|
|
|
|
|
* @return Scene 级别选择界面的场景对象.
|
|
|
|
|
*/
|
|
|
|
|
public Scene getScene() {
|
|
|
|
|
return scene;
|
|
|
|
|
}
|
|
|
|
|
|