고급자바프로그래밍 실습과제
조건문과 GUI, 저장, 로드 등을 활용한 퍼즐 만들기
- 20X20pixel 크기의 블록 100개와 총 23개의 버튼을 활용한 퍼즐
- 다양한 조건문과 이벤트리스너, 파일 관리 등을 잘 활용할 줄 알 아야 가능
완성된 화면

※구현형태
- 20*20px 흰색 RGB(255,255,255) 이미지 1개와 20*20px 검정색 RGB (0,0,0) 이미지 1개 만들기
- 간격은 2pixel 로 10*10 (총 100개) 하얀색 이미지를 배치하기
- 버튼 10개를 행의 맨 왼쪽에, 버튼 10개를 열의 맨 위쪽에 배치
- 맨 위에 버튼 3개(초기화, 저장, 불러오기)
※구현조건
- 행 버튼 1개와 열 버튼 1개를 누르면 그 두 버튼이 만나는 지점 의 색이 바뀜 (흰색-> 검정색, 검정색-> 흰색)
- 행 버튼 2개 또는 열 버튼 2개를 누르면 에러 메시지 출력
- 초기화를 할 경우 전부 흰 이미지로 초기화
- 저장 버튼을 누르면 현재 검/흰 이미지 상태 저장
- 불러오기 버튼을 누르면 저장해둔 상태가 그대로 보여짐 (파일이 없는 채로 불러오기 할 경우, 파일에 내용이 없는 경우 에러출력)
코드전체
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.*;
public class Puzzle {
private JFrame frame;
private boolean[][] colorState; // true: white, false: black
private JLabel[][] blockLabel;
public static void main(String[] args) {
Puzzle p = new Puzzle();
p.showGUI();
}
private void showGUI() {
frame = new JFrame("10x10 puzzle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(320, 360);
JButton resetButton = new JButton("Reset");
JButton saveButton = new JButton("Save");
JButton loadButton = new JButton("Load");
//resetButton 구현
resetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
if (!(colorState[i][j])) setColor(i, j, Color.WHITE); //Black image->White image
}
});
//saveButton 구현
saveButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
File file = new File("Puzzle.txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
writer.write(colorState[i][j] + " "); //Boolean값을 10x10으로 저장
}
writer.write('\n');
}
writer.close();
JOptionPane.showMessageDialog(frame, "File saved successfully.", "Success", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(frame, "An error occurred while saving the file.", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
//loadButton 구현
loadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
File file = new File("Puzzle.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
for (int i = 0; i < 10; i++) {
String[] blocks = (reader.readLine()).split(" "); //읽어들인 한 줄을 공백단위로 나눔-> blockLine[0]= false, blockLine[1]= true ...
for (int j = 0; j < 10; j++) {
setColor(i, j, Boolean.parseBoolean(blocks[j]) ? Color.WHITE : Color.BLACK);
}
}
reader.close();
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(frame, "File is not found or an error occurred.", "Error", JOptionPane.ERROR_MESSAGE);
} catch (ArrayIndexOutOfBoundsException ex) { //파일 형식이 망가지면 읽어들인 데이터를 index에 알맞게 저장하지 못함 -> ArrayIndexOutOfBoundsException
resetButton.doClick(); //망가진 파일의 데이터가 나타난 것을 reset
ex.printStackTrace();
JOptionPane.showMessageDialog(frame, "Please check the file format.", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
JPanel TopPanel = new JPanel();
TopPanel.add(resetButton);
TopPanel.add(saveButton);
TopPanel.add(loadButton);
//11X11 퍼즐판
JPanel puzzlePanel = new JPanel(new GridLayout(11, 11, 2, 2));
JButton dummyButton = new JButton(); // (0,0) dummy button
dummyButton.setPreferredSize(new Dimension(20, 20));
dummyButton.setEnabled(false);
puzzlePanel.add(dummyButton);
JButton[] colButton = new JButton[10];
JButton[] rowButton = new JButton[10];
blockLabel = new JLabel[10][10];
colorState = new boolean[10][10];
for (int i = 0; i < 10; i++) { //행 버튼
colButton[i] = new JButton();
colButton[i].setPreferredSize(new Dimension(20, 20));
puzzlePanel.add(colButton[i]);
int colIndex = i;
colButton[i].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
switchColor("Col", colIndex);
}
});
}
for (int i = 0; i < 10; i++) {
rowButton[i] = new JButton(); //열 버튼
rowButton[i].setPreferredSize(new Dimension(20, 20));
puzzlePanel.add(rowButton[i]);
int rowIndex = i;
rowButton[i].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
switchColor("Row", rowIndex);
}
});
for (int j = 0; j < 10; j++) { // 10x10 이미지
blockLabel[i][j] = new JLabel(createIcon(Color.white));
colorState[i][j] = true;
puzzlePanel.add(blockLabel[i][j]);
}
}
frame.add(TopPanel, BorderLayout.NORTH);
frame.add(puzzlePanel, BorderLayout.CENTER);
frame.setVisible(true);
}
static int row = -1;
static int col = -1;
private void switchColor(String matrix, int index) { //-> drawPuzzle
try {
if (matrix.equals("Row") && row == -1) row = index;
else if (matrix.equals("Col") && col == -1) col = index;
else throw new Exception(); //행 버튼 2개, 열 버튼 2개를 누른 경우
} catch (Exception e) {
row = -1; col = -1;
JOptionPane.showMessageDialog(frame, "행, 열 버튼을 1번씩 눌러주세요", "Error", JOptionPane.ERROR_MESSAGE);
}
if (row != -1 && col != -1) { //row와 column index가 정상적으로 하나씩 들어왔을때
setColor(row, col, colorState[row][col] ? Color.BLACK : Color.WHITE); // colorState가 true(white)->매개변수: Black || false(black)->매개변수: White
row = -1; col = -1;
}
}
private void setColor(int row, int col, Color color) { // (row, col)의 이미지를 매개변수로 받은 색으로 바꿔주는 함수
blockLabel[row][col].setIcon(createIcon(color));
colorState[row][col] = (color == Color.white); // white-> true, black-> false
}
private ImageIcon createIcon(Color color) { // Graphics를 이용한 image생성
BufferedImage image = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, 20, 20);
return new ImageIcon(image);
}
}
- saveButton구현
saveButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { File file = new File("Puzzle.txt"); BufferedWriter writer = new BufferedWriter(new FileWriter(file)); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { writer.write(colorState[i][j] + " "); //Boolean값을 10x10으로 저장 } writer.write('\n'); } writer.close(); JOptionPane.showMessageDialog(frame, "File saved successfully.", "Success", JOptionPane.INFORMATION_MESSAGE); } catch (IOException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(frame, "An error occurred while saving the file.", "Error", JOptionPane.ERROR_MESSAGE); } } });
colorState배열의 Boolean값 true, false를 파일에 저장한다.
- loadButton 구현
loadButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { File file = new File("Puzzle.txt"); BufferedReader reader = new BufferedReader(new FileReader(file)); for (int i = 0; i < 10; i++) { String[] blocks = (reader.readLine()).split(" "); //읽어들인 한 줄을 공백단위로 나눔-> blockLine[0]= false, blockLine[1]= true ... for (int j = 0; j < 10; j++) { setColor(i, j, Boolean.parseBoolean(blocks[j]) ? Color.WHITE : Color.BLACK); } } reader.close(); } catch (IOException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(frame, "File is not found or an error occurred.", "Error", JOptionPane.ERROR_MESSAGE); } catch (ArrayIndexOutOfBoundsException ex) { //파일 형식이 망가지면 읽어들인 데이터를 index에 알맞게 저장하지 못함 -> ArrayIndexOutOfBoundsException resetButton.doClick(); //망가진 파일의 데이터가 나타난 것을 reset ex.printStackTrace(); JOptionPane.showMessageDialog(frame, "Please check the file format.", "Error", JOptionPane.ERROR_MESSAGE); } } });
BufferReader로 읽어들인 String 을 Boolean으로 바꿔서 그에 맞게 컬러를 설정한다.
만약 파일이 손상된 상태라면 반복문안에서 배열에 저장하는 과정이 제대로 실행되지 못해서 ArrayIndexOutofBoundsException 예외가 발생하게 된다. 그 부분도 예외처리 해주었다.
- buttonEvent 처리
static int row = -1; static int col = -1; private void switchColor(String matrix, int index) { //-> drawPuzzle try { if (matrix.equals("Row") && row == -1) row = index; else if (matrix.equals("Col") && col == -1) col = index; else throw new Exception(); //행 버튼 2개, 열 버튼 2개를 누른 경우 } catch (Exception e) { row = -1; col = -1; JOptionPane.showMessageDialog(frame, "행, 열 버튼을 1번씩 눌러주세요", "Error", JOptionPane.ERROR_MESSAGE); } if (row != -1 && col != -1) { //row와 column index가 정상적으로 하나씩 들어왔을때 setColor(row, col, colorState[row][col] ? Color.BLACK : Color.WHITE); // colorState가 true(white)->매개변수: Black || false(black)->매개변수: White row = -1; col = -1; } }
n열버튼과 n행버튼을 누르면 (n, n)의 이미지의 색을 바꿔주는 요청을 보낸다.
열버튼과 행버튼을 두번씩 누른 것에 대한 예외를 처리하기 위해 row, col의 변수는 -1로 초기화 된다. 그리고 각각 현재 이벤트 처리를 할려고 하는 열과 행이 비어있을때만 정상적으로 색이 바뀐다.
row가 다른값으로 저장되있는 상태에서 또 값을 저장하지 못한다.
- setColor 함수
private void setColor(int row, int col, Color color) { // (row, col)의 이미지를 매개변수로 받은 색으로 바꿔주는 함수 blockLabel[row][col].setIcon(createIcon(color)); colorState[row][col] = (color == Color.white); // white-> true, black-> false }
block의 색과 colorState를 바꿔주는 함수이다.
처음 퍼즐을 생성할때 말고는 항상 라벨을 바꿔주면서 state배열도 바꿔주어야 하기 때문에 함수로 만들었다.
보완, 추가해야 할 점
- 버튼 크기, 간격설정이 안됨
- 파일 입출력 stream으로 boolean값을 String이 아닌 0, 1의 형태로 저장하도록 해야 자원을 절약할 수 있다.
'학교생활!' 카테고리의 다른 글
[231222] 고급 자바프로그래밍 프로젝트: 캘린더 (0) | 2024.01.01 |
---|---|
[231111] 고급 자료구조 과제3: 그래프 (1) | 2023.11.15 |
[230602] 자료구조 과제6: 연결 리스트와 힙 (0) | 2023.07.11 |
[230520] 고급C++프로그래밍: 과제3 (0) | 2023.05.31 |
[230503] 자료구조 과제5: 순환(recursion) (0) | 2023.05.07 |