- 2025.01 진행
1. 프로젝트 기획
평소 사람의 성격을 분류화할 수 있는 MBTI 심리검사에 대한 관심이 많아 설명과 밈을 찾아보는 편이다. 다양한 MBTI 테스트도 많이 접했다. 기본 MBTI테스트부터 캐릭터, 동물 등 다양한 형식의 MBTI테스트가 등장하는 것을 보며 나도 이런 MBTI테스트를 언젠가 만들어 봐야겠다고 생각했다. 대학교 2학년 JAVA프로그래밍 실습시간 마지막 부분에서 다루었던 GUI가 설계를 통해 눈에 보이는 창을 만들어 냈었다는 점이 기억나 GUI로 독특한 MBTI테스트를 만들어 보기로 했다. GUI 프로젝트 대부분의 주제가 게임인 만큼, 내 주제가 GUI와 적합한가에 대한 고민이 많았으나 최선을 다해 진행해보기로 한다.
이번에 제작할 MBTI테스트의 가장 큰 차별점은 ‘감각’을 직접 활용했다는 것이다. 기존 MBTI테스트는 대부분 줄글로 질의응답을 진행해야 한다. 예를 들어 ‘Q. 당신이 좋아하는 음악 A.시끄러운 음악 B.조용한 음악’ 과 같은 질의응답에서는 음악을 들었던 나의 과거 경험을 떠올려야 한다. 이에 반해 감각 MBTI 테스트는 직접 음악을 실행하여 즉시 그 자리에서 나의 선호를 파악할 수 있는 것이 장점이다. 음악뿐만 아니라 이미지를 직접 볼 수 있게, 또 계획을 직접 입력할 수 있게 하여 사용자의 흥미를 높이고 즉각적 판단으로 인한 더 정확한 테스트 결과를 이끌어낼 수 있다.
E vs I, N vs S, F vs T, P vs J 의 점수를 각각 매겨 각 알파벳의 총점을 계산해 최종 MBTI를 판단한다. 프로그램을 실행하면 자동으로 배경음악이 흘러나온다. 시작화면에서 닉네임을 입력하고, 질문화면에서 질문과 사진, 음악을 통해 둘 중 하나의 선택지를 고르고 클릭한다. 각 선택버튼을 클릭할 때마다 해당 버튼의 score가 +1로 증가한다. 총 12개의 질의응답 후에 결과화면이 나타난다. (E vs I, N vs S, F vs T, P vs J 를 정하는 문제가 각 3개씩이다. 질문이 짝수개일 경우 2:2로 선택될 경우 오류가 발생하기 때문에 홀수개로 정했다.) MBTI 결과화면에서는 처음에 입력했던 닉네임을 이용해 MBTI결과가 ‘00님의 MBTI는 0000입니다.’의 형식으로 나타난다. 그 밑에 각 MBTI별 설명을 .txt파일로 저장해 놓아 MBTI score 결과에 따라 해당 설명을 가져와 보여준다. 또한 프로젝트에 .png파일로 각 MBTI별 캐릭터를 저장해놓아 하단에 MBTI score 결과에 따른 캐릭터가 나타나도록 설계했다.
2.디자인(실행화면)















3. 소스코드(JAVA)
Package 안에 총 6개의 class가 존재한다.
1. MBTItest(메인클래스)
import javax.swing.*;
import javazoom.jl.player.Player;
import java.awt.*;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
// 메인 클래스: 프로그램 시작점
public class MBTItest {
static Player backgroundMusicPlayer = null;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new StartScreen().setVisible(true));
}
// 배경음악 재생
public static void playBackgroundMusic(String musicFile) {
// 배경음악이 이미 재생 중일 경우 멈추기
if (backgroundMusicPlayer != null) {
backgroundMusicPlayer.close();
}
new Thread(() -> {
try {
FileInputStream fis = new FileInputStream(musicFile);
BufferedInputStream bis = new BufferedInputStream(fis);
Player player = new Player(bis); // JLayer Player
player.play(); // 음악 재생
} catch (Exception e) {
System.err.println("배경음악 재생 오류: " + e.getMessage());
}
}).start();
}
}
//기본 질문 클래스 BasicQuestion
class BasicQuestion extends Question {
private final String option1;
private final String option2;
private final String type1;
private final String type2;
public BasicQuestion(String text, String option1, String option2, String type1, String type2) {
super(text);
this.option1 = option1;
this.option2 = option2;
this.type1 = type1;
this.type2 = type2;
}
@Override
public JPanel getPanel(Runnable onNext, Map<String, Integer> scores) {
// 패널 생성 및 레이아웃 설정
JPanel panel = new JPanel(new BorderLayout());
// 질문 라벨 추가
JLabel questionLabel = new JLabel("<html><h2>" + getText() + "</h2></html>", SwingConstants.CENTER);
questionLabel.setFont(new Font("SansSerif", Font.BOLD, 16)); // 글꼴 및 크기 설정
panel.add(questionLabel, BorderLayout.NORTH);
// 시작화면 이미지 추가 부분
JLabel startLabel = new JLabel();
startLabel.setHorizontalAlignment(JLabel.CENTER); // 수평 정렬
startLabel.setVerticalAlignment(JLabel.CENTER); // 수직 정렬
panel.add(startLabel, BorderLayout.CENTER);
// 패널 설정
panel.setVisible(true);
JButton button1 = new JButton(option1);
JButton button2 = new JButton(option2);
// 버튼 크기 및 스타일 설정
Dimension buttonSize = new Dimension(150, 50);
button1.setPreferredSize(buttonSize);
button2.setPreferredSize(buttonSize);
button1.setFont(new Font("SansSerif", Font.BOLD, 15));
button2.setFont(new Font("SansSerif", Font.BOLD, 15));
button1.setBackground(new Color(0,128,0)); // 초록
button2.setBackground(new Color(0,128,0));
button1.setPreferredSize(new Dimension(300,100));
button2.setPreferredSize(new Dimension(300,100));
button1.setForeground(Color.WHITE);
button2.setForeground(Color.WHITE);
button1.setBorder(BorderFactory.createLineBorder(Color.BLACK));
button2.setBorder(BorderFactory.createLineBorder(Color.BLACK));
button1.addActionListener(e -> {
scores.put(type1, scores.get(type1) + 1);
onNext.run();
});
button2.addActionListener(e -> {
scores.put(type2, scores.get(type2) + 1);
onNext.run();
});
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 20));
buttonPanel.setOpaque(false);
buttonPanel.add(button1);
buttonPanel.add(button2);
panel.add(buttonPanel, BorderLayout.CENTER);
return panel;
}
}
// 시작 화면 클래스: 닉네임 입력
class StartScreen extends JFrame {
public StartScreen() {
setTitle("감각 MBTI TEST");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500,300);
setLocationRelativeTo(null);
JPanel panel = new JPanel(new BorderLayout());
JLabel title = new JLabel("감각 MBTI TEST", SwingConstants.CENTER);
title.setFont(new Font("SansSerif", Font.BOLD, 40));
panel.add(title, BorderLayout.NORTH);
panel.setBackground(new Color(70,130,180)); // 진한 하늘색 배경
JPanel inputPanel = new JPanel(new FlowLayout());
JLabel nameLabel = new JLabel("닉네임을 입력하세요: ", SwingConstants.CENTER);
nameLabel.setFont(new Font("SansSerif", Font.BOLD, 15));
inputPanel.add(nameLabel, BorderLayout.SOUTH);
JTextField nameField = new JTextField(20);
JButton startButton = new JButton("시작");
startButton.addActionListener(e -> {
String nickname = nameField.getText().trim();
if (!nickname.isEmpty()) {
dispose(); // 시작 화면 닫기
new MBTITestFrame(nickname).setVisible(true); // 테스트 화면 열기
} else {
JOptionPane.showMessageDialog(this, "닉네임을 입력해주세요!", "오류", JOptionPane.ERROR_MESSAGE);
}
});
inputPanel.add(nameLabel);
inputPanel.add(nameField);
inputPanel.add(startButton);
panel.add(inputPanel, BorderLayout.CENTER);
add(panel);
playBackgroundMusic("배경음악.mp3");
}
private void playBackgroundMusic(String musicFile) {
new Thread(() -> {
try {
FileInputStream fis = new FileInputStream(musicFile);
BufferedInputStream bis = new BufferedInputStream(fis);
Player player = new Player(bis); // JLayer Player
player.play(); // 음악 재생
} catch (Exception e) {
System.err.println("배경음악 재생 오류: " + e.getMessage());
}
}).start();
}
// MBTI 테스트 프레임
class MBTITestFrame extends JFrame {
private final String nickname;
private final CardLayout cardLayout = new CardLayout(); // 카드 레이아웃으로 질문 화면 전환
private final JPanel mainPanel = new JPanel(cardLayout); // 메인 패널
private final Map<String, Integer> scores = new HashMap<>(); // MBTI 점수 저장
private int currentQuestion = 0; // 현재 질문 번호
// 질문 리스트: 기본 질문과 미디어 질문 포함
private final Question[] questions = {
//E vs I
new BasicQuestion("1. 음식점에 가서 음식을 시켜려 하는데, 벨이 없고 웨이터도 나를 보지 않는다.?", "언젠간 보겠지.. 손을 들고 기다린다.", "당장 소리친다. 여기요~!!!", "I", "E"),
//N vs S
new BasicQuestion("2. 내가 원하는 배움은?", "훗날 나에게 도움이 될 것 같은 배움", "현재 나의 부족한 부분을 메꿀 수 있는 배움", "N", "S"),
//F vs T
new BasicQuestion("3. '나 어제 접촉사고 났어!' 라는 친구의 전화를 받고 나는", "많이 놀랐겠다... 다친 데는 없어?", "정말? 보험처리는 했어?", "F", "T"),
//J vs P
new BasicQuestion("4. 과제를 받았다. 나는", "뭐부터 할지 계획을 세워야 틀이 잡힌다.", "자료조사부터 해 봐야 틀이 잡힌다.", "J", "P"),
//E vs I
new ImageQuestion("5. 둘 중 내가 놓이고 싶은 상황은?", new File("파티이미지.jpg"), new File("쉬는이미지.png"), "E", "I"),
//N vs S
new ImageQuestion("6. 둘 중 내 마음에 더 드는 그림은?", new File("상상화.png"), new File("정물화.jpg"), "N", "S"),
//F vs T
new ImageQuestion("7. '나 아파...'라고 말했을 때 '병원에 가'라는 말을 어떻게 받아들이나요?", new File("F짤.jpg"), new File("T짤.jpg"), "F", "T"),
//J vs P
new ImageQuestion("8. 영주 여행을 가기로 했다. 하루 전 나의 모습은?", new File("J짤.png"), new File("P짤.png"), "J", "P"),
//E vs I
new MediaQuestion("9. 두 음악을 각각 듣고 평소 즐겨듣는 스타일의 노래를 고르세요.", new File("E음악.mp3"), new File("I음악.mp3"), "E", "I"),
//N vs S
new MediaQuestion("10. 두 음악을 각각 듣고 가사가 더 마음에 드는 음악을 고르세요.", new File("N음악.mp3"), new File("S음악.mp3"), "N", "S"),
//F vs T
new BasicQuestion("11. '근데 나는 너랑 취향이 진짜 안 맞는 것 같아.' 라는 말을 듣고 나는?", "(상처)원래 반대성향인 사람들이 잘 맞아.. ","그럴 수 있지! 사람은 다 다르니까.", "F", "T"),
//J vs P
new TextQuestion("12. 오늘 하루, 당신의 계획을 작성해보세요.")
};
// 생성자
public MBTITestFrame(String nickname) {
this.nickname = nickname;
setTitle("감각 MBTI TEST");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(700, 600);
setLocationRelativeTo(null);
// 점수 초기화
scores.put("E", 0);
scores.put("I", 0);
scores.put("N", 0);
scores.put("S", 0);
scores.put("F", 0);
scores.put("T", 0);
scores.put("J", 0);
scores.put("P", 0);
// 질문 패널 추가
for (Question question : questions) {
JPanel questionPanel = question.getPanel(() -> {
if (currentQuestion < questions.length - 1) {
currentQuestion++;
cardLayout.next(mainPanel);
} else {
displayResults();
}
}, scores);
// 버튼 크기를 100x100으로 고정 설정
for (Component component : questionPanel.getComponents()) {
if (component instanceof JButton) {
JButton button = (JButton) component;
button.setPreferredSize(new Dimension(100,100)); // 버튼 크기 고정
button.setBorder(BorderFactory.createLineBorder(Color.darkGray));
}
}
mainPanel.add(questionPanel, question.getText());
}
add(mainPanel);
mainPanel.setBackground(new Color(70,130,180)); // 진한 하늘색 배경
setFontAndColor(mainPanel);
}
// 글꼴 및 버튼 스타일 설정
private void setFontAndColor(JComponent component) {
Font font = new Font("SansSerif", Font.BOLD, 15); // 가독성 높은 글씨체 및 크기
for (Component child : component.getComponents()) {
if (child instanceof JButton) {
JButton button = (JButton) child;
button.setFont(font);
button.setBackground(new Color(0,128,128)); //버튼배경색:청록색
button.setForeground(Color.white); // 버튼글씨색: 하얀색
button.setFocusPainted(false);
} else if (child instanceof JLabel) {
((JLabel) child).setFont(font);
} else if (child instanceof JPanel) {
setFontAndColor((JComponent) child);
}
}
}
// 결과를 계산하고 표시
private void displayResults() {
StringBuilder resultText = new StringBuilder("테스트 결과:\n");
// MBTI 유형 계산
StringBuilder mbti = new StringBuilder();
mbti.append(scores.get("E") > scores.get("I") ? "E" : "I");
mbti.append(scores.get("N") > scores.get("S") ? "N" : "S");
mbti.append(scores.get("F") > scores.get("T") ? "F" : "T");
mbti.append(scores.get("J") > scores.get("P") ? "J" : "P");
resultText.append(nickname).append("님의 MBTI는: ").append(mbti).append("\n");
// 결과를 파일에 저장
try (FileWriter writer = new FileWriter("test_results.txt")) {
writer.write(resultText.toString());
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "결과를 파일에 저장하는 중 오류가 발생했습니다.", "오류", JOptionPane.ERROR_MESSAGE);
}
// MBTI별 설명 파일 읽기
String description = "설명 파일을 불러오는 중 오류 발생"; // 기본값
try {
description = Files.readString(Paths.get(mbti + ".txt"));
} catch (IOException e) {
description = "설명을 불러오는 데 실패했습니다.";
}
// 결과 화면 구성
JPanel resultPanel = new JPanel(new BorderLayout());
JLabel resultLabel1 = new JLabel("<html><h2 style='font-size: 25px;'>[ 테스트 결과 ]</h2></html>", SwingConstants.CENTER);
resultPanel.add(resultLabel1, BorderLayout.NORTH);
resultLabel1.setFont(new Font("SansSerif", Font.BOLD, 40));
JLabel resultLabel2 = new JLabel("<html><h2 style='font-size: 20px;'>" + nickname + "님의 MBTI: " + mbti + "</h2 style='font-size: 15px;><p>" + description + "</p></html>");
resultLabel2.setHorizontalAlignment(SwingConstants.CENTER); // 정렬 설정
resultPanel.add(resultLabel2, BorderLayout.CENTER);
// MBTI별 캐릭터 이미지 추가
JLabel characterImageLabel = new JLabel(new ImageIcon(mbti + ".png")); // MBTI에 따른 캐릭터 이미지
resultPanel.add(characterImageLabel, BorderLayout.SOUTH);
// 마무리 화면 전환
mainPanel.add(resultPanel, "결과");
cardLayout.show(mainPanel, "결과");
}
}
}
2. MediaQuestion class
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;
import javazoom.jl.player.Player;
// 미디어 기반 질문을 처리하는 MediaQuestion 클래스 (AbstractMediaQuestion을 상속)
public class MediaQuestion extends AbstractMediaQuestion {
private Player[] players = new Player[2]; // 두 개의 음악을 제어할 Player 객체 배열
// 생성자: 질문 텍스트와 두 개의 미디어 파일, 점수 유형을 초기화
public MediaQuestion(String text, File media1, File media2, String type1, String type2) {
super(text, media1, media2, type1, type2); // 부모 클래스 AbstractMediaQuestion의 생성자 호출
}
// 질문에 해당하는 패널을 반환하는 메서드
@Override
public JPanel getPanel(Runnable onNext, Map<String, Integer> scores) {
// 기본 패널 생성 (질문 텍스트 포함)
JPanel panel = createBasePanel(getText()); // 질문 텍스트를 포함하는 기본 패널 생성
// 미디어 버튼들을 담을 패널 설정 (음악 재생 및 정지 버튼)
JPanel mediaPanel = new JPanel(new GridLayout(1, 4, 10, 10)); // 그리드 레이아웃으로 1행 4열 배치
Dimension buttonSize = new Dimension(100, 80); // 버튼 크기 설정
// 음악 1과 2의 재생 및 정지 버튼 생성
JButton playButton1 = createButton("음악 1 재생", buttonSize, Color.CYAN, () -> playMedia(0, file1));
JButton stopButton1 = createButton("음악 1 정지", buttonSize, Color.RED, () -> stopMedia(0));
JButton playButton2 = createButton("음악 2 재생", buttonSize, Color.CYAN, () -> playMedia(1, file2));
JButton stopButton2 = createButton("음악 2 정지", buttonSize, Color.RED, () -> stopMedia(1));
// 재생/정지 버튼들을 mediaPanel에 추가
mediaPanel.add(playButton1);
mediaPanel.add(stopButton1);
mediaPanel.add(playButton2);
mediaPanel.add(stopButton2);
// 음악을 선택하는 버튼들을 담을 패널 설정
JPanel choicePanel = new JPanel(new GridLayout(1, 2, 10, 10)); // 1행 2열로 선택 버튼 배치
JButton chooseButton1 = createButton("음악 1 선택", buttonSize, Color.GREEN, () -> {
scores.put(type1, scores.get(type1) + 1); // 음악 1을 선택하면 점수 업데이트
endMediaQuestion(onNext); // 질문 종료 후 후속 작업 실행
});
JButton chooseButton2 = createButton("음악 2 선택", buttonSize, Color.GREEN, () -> {
scores.put(type2, scores.get(type2) + 1); // 음악 2를 선택하면 점수 업데이트
endMediaQuestion(onNext); // 질문 종료 후 후속 작업 실행
});
// 선택 버튼들을 choicePanel에 추가
choicePanel.add(chooseButton1);
choicePanel.add(chooseButton2);
// 패널에 미디어 버튼과 선택 버튼 패널을 추가
panel.add(mediaPanel, BorderLayout.CENTER);
panel.add(choicePanel, BorderLayout.SOUTH);
return panel; // 최종 패널 반환
}
// 음악 재생 메서드: 주어진 인덱스와 파일로 음악을 재생
private void playMedia(int index, File mediaFile) {
stopMedia(index); // 현재 재생 중인 음악을 멈추고 새로운 음악 재생
try {
// FileInputStream을 통해 파일을 읽고, Player 객체로 음악을 재생
FileInputStream fis = new FileInputStream(mediaFile);
players[index] = new Player(fis); // 해당 인덱스에 Player 객체 생성
new Thread(() -> {
try {
players[index].play(); // 새로 생성된 Player로 음악 재생
} catch (Exception ex) {
ex.printStackTrace(); // 예외 처리
}
}).start(); // 별도의 스레드에서 음악을 재생
} catch (Exception ex) {
ex.printStackTrace(); // 예외 처리
}
}
// 음악 정지 메서드: 주어진 인덱스에 해당하는 음악을 정지
private void stopMedia(int index) {
if (players[index] != null) {
players[index].close(); // 음악 정지
players[index] = null; // Player 객체를 null로 설정
}
}
// 질문 종료 후 후속 처리를 위한 메서드
private void endMediaQuestion(Runnable onNext) {
// 모든 음악을 정지시키고, 후속 작업 실행
for (int i = 0; i < players.length; i++) {
stopMedia(i);
}
onNext.run(); // 후속 작업 실행 (다음 질문으로 넘어가기)
}
}
3.Question class
import javax.swing.*;
import java.util.Map;
// 질문을 표현하는 추상 클래스 Question
// 이 클래스는 모든 질문의 기본 형태를 정의하며, 각 질문에 맞는 패널을 생성하는 추상 메서드를 포함
abstract class Question {
private final String text; // 질문 텍스트를 저장하는 변수
// 생성자: 질문 텍스트를 초기화
public Question(String text) {
this.text = text;
}
// 질문 텍스트를 반환하는 메서드
public String getText() {
return text;
}
// 각 질문에 맞는 패널을 생성하는 추상 메서드
// onNext는 다음 질문으로 넘어갈 때 실행할 메서드, scores는 점수 맵
public abstract JPanel getPanel(Runnable onNext, Map<String, Integer> scores);
}
4. ImageQuestion class
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.Map;
// 이미지 질문을 처리하는 클래스 ImageQuestion
// 이미지 2개를 선택하는 질문을 생성하는 클래스
public class ImageQuestion extends AbstractMediaQuestion {
// 생성자: 텍스트, 이미지 파일 2개, 선택 항목 2개를 초기화
public ImageQuestion(String text, File image1, File image2, String type1, String type2) {
super(text, image1, image2, type1, type2); // 부모 클래스인 AbstractMediaQuestion의 생성자 호출
}
@Override
// 패널을 생성하고, 이미지를 보여주는 GUI 컴포넌트를 반환하는 메서드
public JPanel getPanel(Runnable onNext, Map<String, Integer> scores) {
// 기본적인 질문 텍스트와 함께 패널 생성
JPanel panel = createBasePanel(getText());
// 이미지를 표시할 패널을 생성하고, 1행 2열의 GridLayout으로 배치
JPanel imagePanel = new JPanel(new GridLayout(1, 2, 10, 10));
imagePanel.setOpaque(false); // 배경을 투명하게 설정
// 이미지를 크기 조정하여 JLabel에 표시
JLabel imageLabel1 = createScaledImageLabel(file1);
JLabel imageLabel2 = createScaledImageLabel(file2);
// 첫 번째 이미지 클릭 시 점수 갱신
imageLabel1.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
scores.put(type1, scores.get(type1) + 1); // 해당 타입의 점수 +1
onNext.run(); // 다음 질문으로 넘어감
}
});
// 두 번째 이미지 클릭 시 점수 갱신
imageLabel2.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
scores.put(type2, scores.get(type2) + 1); // 해당 타입의 점수 +1
onNext.run(); // 다음 질문으로 넘어감
}
});
// 이미지를 패널에 추가
imagePanel.add(imageLabel1);
imagePanel.add(imageLabel2);
// 이미지 패널을 전체 패널에 추가
panel.add(imagePanel, BorderLayout.CENTER);
return panel; // 패널 반환
}
// 이미지를 크기 조정하여 JLabel로 반환하는 메서드
private JLabel createScaledImageLabel(File imageFile) {
ImageIcon originalIcon = new ImageIcon(imageFile.getPath()); // 파일에서 이미지 로드
Image originalImage = originalIcon.getImage(); // 이미지 객체 생성
Image scaledImage = originalImage.getScaledInstance(300, 350, Image.SCALE_SMOOTH); // 크기 조정
return new JLabel(new ImageIcon(scaledImage), SwingConstants.CENTER); // 크기 조정된 이미지를 JLabel로 반환
}
}
5. TextQuestion class
import javax.swing.*;
import java.awt.*;
import java.util.Map;
// 텍스트 입력 질문을 처리하는 클래스 TextQuestion
// 사용자가 텍스트를 입력하고, 100자 이상인지 확인하여 점수를 업데이트하는 질문을 생성하는 클래스
public class TextQuestion extends Question {
private JTextArea inputTextArea; // 텍스트 입력을 위한 JTextArea
private JButton submitButton; // 제출 버튼
// 생성자: 질문 텍스트를 받아서 부모 클래스 Question의 생성자를 호출
public TextQuestion(String questionText) {
super(questionText); // 부모 클래스의 생성자 호출
}
// 텍스트 길이를 체크하고 점수를 업데이트하는 메서드
// 50자 이상 입력하면 'J' 점수 +1, 50자 미만 입력하면 'P' 점수 +1
private void checkTextLength(String inputText, Map<String, Integer> scores) {
if (inputText.length() >= 50) {
scores.put("J", scores.get("J") + 1); // 50자 이상이면 'J' 점수 +1
} else {
scores.put("P", scores.get("P") + 1); // 50자 미만이면 'P' 점수 +1
}
}
@Override
// 텍스트 입력 질문에 대한 패널을 생성하고 반환하는 메서드
public JPanel getPanel(Runnable onNext, Map<String, Integer> scores) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(new Color(245, 245, 255)); // 패널 배경 색 설정
// 질문 텍스트를 JLabel로 표시
JLabel questionLabel = new JLabel("<html><h2>" + getText() + "</h2></html>", SwingConstants.CENTER);
questionLabel.setFont(new Font("SansSerif", Font.BOLD, 18)); // 글꼴 설정
panel.add(questionLabel, BorderLayout.NORTH); // 질문 레이블을 패널의 북쪽에 추가
// 텍스트 입력을 위한 JTextArea 설정
inputTextArea = new JTextArea(10, 30); // 10줄 30열 크기
inputTextArea.setWrapStyleWord(true); // 단어가 끊어지지 않도록 설정
inputTextArea.setLineWrap(true); // 줄 바꿈을 허용
JScrollPane scrollPane = new JScrollPane(inputTextArea); // 스크롤 가능하도록 설정
panel.add(scrollPane, BorderLayout.CENTER); // 텍스트 입력 필드를 패널에 추가
// 제출 버튼 생성
submitButton = new JButton("제출");
submitButton.setFont(new Font("SansSerif", Font.PLAIN, 16)); // 글꼴 설정
submitButton.setBackground(new Color(135, 206, 250)); // 버튼 배경 색
submitButton.setForeground(Color.WHITE); // 버튼 글자 색
// 제출 버튼 클릭 시 텍스트 길이 확인 후 점수 업데이트
submitButton.addActionListener(e -> {
String inputText = inputTextArea.getText().trim(); // 입력된 텍스트 가져오기
if (inputText.isEmpty()) { // 텍스트가 비어있으면 경고창 띄움
JOptionPane.showMessageDialog(
panel,
"글자를 입력해주세요.", // 경고 메시지
"경고",
JOptionPane.WARNING_MESSAGE // 경고 메시지 박스
);
} else {
checkTextLength(inputText, scores); // 텍스트 길이에 따라 점수 업데이트
onNext.run(); // 다음 질문으로 넘어가게 함
}
});
// 제출 버튼을 배치할 패널 생성
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 20));
buttonPanel.setOpaque(false); // 버튼 패널을 투명하게 설정
buttonPanel.add(submitButton); // 제출 버튼을 버튼 패널에 추가
panel.add(buttonPanel, BorderLayout.SOUTH); // 버튼 패널을 패널의 하단에 추가
return panel; // 생성된 패널 반환
}
}
6. AbstractMediaQuestion class
import javax.swing.*;
import java.awt.*;
import java.io.File;
// 미디어 관련 질문을 처리하는 추상 클래스
// 이미지, 음악 등 미디어 파일을 처리할 때 공통적인 요소들을 포함하고 있음
public abstract class AbstractMediaQuestion extends Question {
// 미디어 파일들을 저장할 필드
protected final File file1;
protected final File file2;
protected final String type1;
protected final String type2;
// 생성자: 질문 텍스트와 두 개의 미디어 파일, 타입을 받음
public AbstractMediaQuestion(String text, File file1, File file2, String type1, String type2) {
super(text); // 부모 클래스 Question의 생성자 호출
this.file1 = file1;
this.file2 = file2;
this.type1 = type1;
this.type2 = type2;
// 파일 유효성 검사
validateFiles(file1, file2);
}
// 파일 유효성 검사 메서드: 파일이 존재하고 유효한지 확인
private void validateFiles(File... files) {
for (File file : files) {
if (!file.exists() || !file.isFile()) { // 파일이 존재하지 않거나 파일이 아니면 예외 처리
throw new IllegalArgumentException("유효하지 않은 파일: " + file.getPath());
}
}
}
// 공통 버튼 생성 메서드
// 버튼을 생성하고 설정하는 메서드로, 크기, 배경 색, 동작을 매개변수로 받아 버튼을 반환
protected JButton createButton(String text, Dimension size, Color bgColor, Runnable action) {
JButton button = new JButton(text);
button.setPreferredSize(size); // 버튼 크기 설정
button.setBackground(bgColor); // 버튼 배경 색 설정
button.addActionListener(e -> action.run()); // 버튼 클릭 시 실행할 액션 설정
return button; // 생성된 버튼 반환
}
// 공통 레이아웃 설정 메서드
// 각 미디어 질문에 대한 기본 패널 레이아웃을 설정
protected JPanel createBasePanel(String questionText) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(new Color(245, 245, 255)); // 패널 배경 색 설정
// 질문 텍스트를 JLabel로 표시
JLabel questionLabel = new JLabel("<html><h2>" + questionText + "</h2></html>", SwingConstants.CENTER);
questionLabel.setFont(new Font("SansSerif", Font.BOLD, 18)); // 글꼴 설정
panel.add(questionLabel, BorderLayout.NORTH); // 패널의 상단에 질문 레이블 추가
return panel; // 생성된 패널 반환
}
}
'프로젝트 > 개인프로젝트' 카테고리의 다른 글
| [개인프로젝트] Portfolio webpage 만들기 - 클론코딩, HTML/CSS 이용 (2) | 2025.01.25 |
|---|