You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
math-learing/src/main/java/com/personalproject/ui/views/ExamView.java

268 lines
8.3 KiB

package com.personalproject.ui.views;
import com.personalproject.controller.MathLearningController;
import com.personalproject.model.ExamSession;
import com.personalproject.model.QuizQuestion;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
/**
* 提供答题界面以及题目与选项的呈现.
*/
public class ExamView extends BorderPane {
private final Stage primaryStage;
private final MathLearningController controller;
private final ExamSession examSession;
private Label questionNumberLabel;
private Label questionTextLabel;
private ToggleGroup answerToggleGroup;
private VBox optionsBox;
private Button nextButton;
private Button previousButton;
private Button finishButton;
private HBox buttonBox;
/**
* ExamView 的构造函数.
*
* @param primaryStage 应用程序的主舞台
* @param controller 数学学习控制器
* @param examSession 当前的考试会话
*/
public ExamView(Stage primaryStage, MathLearningController controller, ExamSession examSession) {
this.primaryStage = primaryStage;
this.controller = controller;
this.examSession = examSession;
initializeUi();
}
/**
* 初始化界面组件.
*/
private void initializeUi() {
// 创建主布局
VBox mainLayout = new VBox(20);
mainLayout.setAlignment(Pos.CENTER);
mainLayout.setPadding(new Insets(20));
// 题号
questionNumberLabel = new Label();
questionNumberLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
// 题目文本
questionTextLabel = new Label();
questionTextLabel.setWrapText(true);
questionTextLabel.setFont(Font.font("System", FontWeight.NORMAL, 14));
questionTextLabel.setMaxWidth(500);
// 选项容器
optionsBox = new VBox(10);
optionsBox.setPadding(new Insets(10));
answerToggleGroup = new ToggleGroup();
// 按钮区域
buttonBox = new HBox(15);
buttonBox.setAlignment(Pos.CENTER);
previousButton = new Button("上一题");
nextButton = new Button("下一题");
finishButton = new Button("完成考试");
// 设置按钮尺寸
previousButton.setPrefSize(100, 35);
nextButton.setPrefSize(100, 35);
finishButton.setPrefSize(120, 35);
buttonBox.getChildren().addAll(previousButton, nextButton, finishButton);
// 将组件添加到主布局
mainLayout.getChildren().addAll(questionNumberLabel, questionTextLabel, optionsBox, buttonBox);
// 将主布局置于边界面板中央
setCenter(mainLayout);
// 加载第一题
loadCurrentQuestion();
// 添加事件处理器
addEventHandlers();
}
/**
* 将当前题目加载到界面.
*/
private void loadCurrentQuestion() {
try {
// 在加载下一题之前检查考试是否已完成
if (examSession.isComplete()) {
// 如果考试已完成,则启用“完成考试”按钮
updateButtonStates();
return;
}
QuizQuestion currentQuestion = examSession.getCurrentQuestion();
int currentIndex = examSession.getCurrentQuestionIndex();
if (currentQuestion == null) {
showAlert(Alert.AlertType.ERROR, "错误", "当前题目为空,请重新开始考试");
return;
}
// 更新题号与题目文本
questionNumberLabel.setText("第 " + (currentIndex + 1) + " 题");
questionTextLabel.setText(currentQuestion.getQuestionText());
// 清空上一题的选项
answerToggleGroup.selectToggle(null);
answerToggleGroup.getToggles().clear();
optionsBox.getChildren().clear();
// 创建新的选项组件
for (int i = 0; i < currentQuestion.getOptions().size(); i++) {
String option = currentQuestion.getOptions().get(i);
RadioButton optionButton = new RadioButton((i + 1) + ". " + option);
optionButton.setToggleGroup(answerToggleGroup);
optionButton.setUserData(i); // 存储选项索引
// 如果该题已有答案则自动选中
if (examSession.hasAnswered(currentIndex)
&& examSession.getUserAnswer(currentIndex) == i) {
optionButton.setSelected(true);
}
optionsBox.getChildren().add(optionButton);
}
// 更新按钮状态
updateButtonStates();
} catch (Exception e) {
showAlert(Alert.AlertType.ERROR, "错误", "加载题目时发生错误: " + e.getMessage());
}
}
/**
* 根据当前位置更新导航按钮的状态.
*/
private void updateButtonStates() {
try {
int currentIndex = examSession.getCurrentQuestionIndex();
int totalQuestions = examSession.getTotalQuestions();
// 处理潜在极端情况
if (totalQuestions <= 0) {
// 如果没有题目,则禁用所有导航按钮
previousButton.setDisable(true);
nextButton.setDisable(true);
finishButton.setDisable(false); // 仍允许完成考试
return;
}
// “上一题”按钮状态
previousButton.setDisable(currentIndex < 0 || currentIndex == 0);
// “下一题”按钮状态
nextButton.setDisable(currentIndex < 0 || currentIndex >= totalQuestions - 1);
// “完成考试”按钮状态——在考试完成或到达最后一题时启用
boolean isExamComplete = examSession.isComplete();
boolean isAtLastQuestion = (currentIndex >= totalQuestions - 1);
finishButton.setDisable(!(isExamComplete || isAtLastQuestion));
} catch (Exception e) {
// 若出现异常,禁用导航按钮以避免进一步问题
previousButton.setDisable(true);
nextButton.setDisable(true);
finishButton.setDisable(false); // 仍允许完成考试
showAlert(Alert.AlertType.ERROR, "错误", "更新按钮状态时发生错误: " + e.getMessage());
}
}
/**
* 为界面组件添加事件处理器.
*/
private void addEventHandlers() {
nextButton.setOnAction(e -> handleNextQuestion());
previousButton.setOnAction(e -> handlePreviousQuestion());
finishButton.setOnAction(e -> handleFinishExam());
// 添加变更监听器,在选项被选择时保存答案
answerToggleGroup.selectedToggleProperty().addListener((obs, oldSelection, newSelection) -> {
if (newSelection != null) {
int selectedIndex = (Integer) newSelection.getUserData();
examSession.setAnswer(selectedIndex);
}
});
}
/**
* 处理“下一题”按钮的操作.
*/
private void handleNextQuestion() {
try {
if (examSession.goToNextQuestion()) {
loadCurrentQuestion();
} else {
// 若无法跳转到下一题,可能已经到达末尾
// 检查考试是否完成并据此更新按钮状态
updateButtonStates();
}
} catch (Exception e) {
showAlert(Alert.AlertType.ERROR, "错误", "导航到下一题时发生错误: " + e.getMessage());
}
}
/**
* 处理“上一题”按钮的操作.
*/
private void handlePreviousQuestion() {
try {
if (examSession.goToPreviousQuestion()) {
loadCurrentQuestion();
} else {
// 若无法返回上一题,可能已经位于开头
updateButtonStates();
}
} catch (Exception e) {
showAlert(Alert.AlertType.ERROR, "错误", "导航到上一题时发生错误: " + e.getMessage());
}
}
/**
* 显示提示对话框.
*
* @param alertType 提示类型
* @param title 对话框标题
* @param message 显示的消息
*/
private void showAlert(Alert.AlertType alertType, String title, String message) {
Alert alert = new Alert(alertType);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
/**
* 处理“完成考试”按钮的操作.
*/
private void handleFinishExam() {
// 保存考试结果
controller.saveExamResults(examSession);
// 展示考试结果
ExamResultsView resultsView = new ExamResultsView(primaryStage, controller, examSession);
primaryStage.getScene().setRoot(resultsView);
}
}