基于第二次数独游戏,添加GUI界面
高级软件工程第三次作业:基于第二次数独游戏,添加GUI界面。GUI界面代码如下:
1 package firstGui; 2 3 import java.awt.*; 4 import java.awt.event.ActionEvent; 5 import java.awt.event.ActionListener; 6 import java.awt.event.ItemEvent; 7 import java.awt.event.ItemListener; 8 9 import javax.swing.*; 10 11 import org.eclipse.swt.widgets.ExpandItem; 12 13 public class sudokuGui2 { 14 15 private JFrame frame; 16 static int[][] cellsAnswer = new int[9][9]; 17 static int[][] cells = new int[9][9]; 18 static int level = 0; //表示游戏等级 19 20 public static void main(String[] args) { 21 EventQueue.invokeLater(new Runnable() { 22 public void run() { 23 try { 24 sudokuGui2 window = new sudokuGui2(); 25 window.frame.setVisible(true); 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 } 30 }); 31 } 32 33 /** 34 * 构造方法 35 */ 36 public sudokuGui2() { 37 initialize(); 38 } 39 40 /** 41 * 初始化窗体 42 */ 43 private void initialize() { 44 45 frame = new JFrame("数独游戏"); 46 // setBounds(x,y,width,height); 47 // x:组件在容器X轴上的起点 y:组件在容器Y轴上的起点 width:组件的长度 height:组件的高度 48 frame.setBounds(100, 100, 900, 680); 49 frame.setResizable(false); //设置窗口是否可变大小 50 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 51 frame.getContentPane().setLayout(null); //要设置Jpanel的位置与大小,要先关闭frame的布局管理器 52 JPanel panel = new JPanel(); 53 panel.setBounds(80, 60, 500, 500); 54 frame.getContentPane().add(panel); 55 56 Color c1 = new Color(253, 237, 237); 57 Color c2 = new Color(254, 254, 254); 58 59 JTextField[][] chessBoard = new JTextField[9][9]; 60 sudokuV2_modify shudu = new sudokuV2_modify(level); 61 cellsAnswer = shudu.getAnswer(); // 从sudokuV2_modify获取数独的答案 62 cells = shudu.getChess(); 63 64 // shudu.show(cellsAnswer); 65 // System.out.println(); 66 // shudu.show(cells); 67 68 /** 69 * 设置棋盘的初始状态 70 */ 71 panel.setLayout(new GridLayout(9, 9)); 72 for (int i = 0; i < 9; i++) { 73 for (int j = 0; j < 9; j++) { 74 // 设置JTextField的内容 75 chessBoard[i][j] = new JTextField(); 76 chessBoard[i][j].setFont(new Font("TimesRoman", Font.PLAIN, 26)); 77 chessBoard[i][j].setHorizontalAlignment(JTextField.CENTER); 78 if (cells[i][j] == 0) { 79 chessBoard[i][j].setForeground(Color.gray); 80 } else { 81 chessBoard[i][j].setText(Integer.toString(cells[i][j])); 82 chessBoard[i][j].setFocusable(false); // 设置是否可获得焦点 83 // chessBoard[i][j].setEnabled(false); //设置是否处于激活状态 84 } 85 86 /** 设置3*3九宫格的背景颜色 */ 87 if ((i / 3 == 0 && j / 3 == 0) || (i / 3 == 1 && j / 3 == 1) || (i / 3 == 2 && j / 3 == 2) 88 || (i / 3 == 0 && j / 3 == 2) || (i / 3 == 2 && j / 3 == 0)) { 89 chessBoard[i][j].setBackground(c1); 90 } else { 91 chessBoard[i][j].setBackground(c2); 92 } 93 94 chessBoard[i][j].setVisible(true); 95 panel.add(chessBoard[i][j]); 96 panel.setVisible(true); 97 } 98 } 99 100 JButton commit = new JButton("提交答案"); 101 commit.setBounds(610, 440, 120, 30); 102 commit.setBorder(BorderFactory.createRaisedBevelBorder()); 103 commit.setBackground(c1); 104 commit.setFont(new Font("TimesRoman", Font.PLAIN, 20)); 105 frame.getContentPane().add(commit); 106 107 JButton another = new JButton("再来一局"); 108 another.setBorder(BorderFactory.createRaisedSoftBevelBorder()); // 设置凸出的效果 109 another.setBounds(610, 500, 120, 30); 110 another.setBackground(c2); 111 another.setFont(new Font("TimesRoman", Font.PLAIN, 20)); 112 frame.getContentPane().add(another); 113 114 String hintInform = new String(); 115 hintInform = "游戏规则:<br>玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足" 116 + "每一行、每一列、每一个颜色不同的(3*3)的小九宫格中分别填入1~9之间的数字,均不能重复。"; 117 JLabel labelRule = new JLabel("<html>" + hintInform + "</html>"); 118 labelRule.setBounds(610, 60, 220, 240); 119 labelRule.setFont(new Font("TimesRoman", Font.PLAIN, 20)); 120 frame.getContentPane().add(labelRule); 121 122 JLabel labelLevel = new JLabel(); 123 labelLevel.setText("选择难易程度:"); 124 labelLevel.setBounds(610, 327, 180, 30); 125 labelLevel.setFont(new Font("TimesRoman", Font.PLAIN, 20)); 126 frame.getContentPane().add(labelLevel); 127 128 JComboBox comboBox = new JComboBox(); 129 comboBox.setFont(new Font("TimesRoman", Font.PLAIN, 20)); 130 comboBox.addItem("难易适中"); 131 comboBox.addItem("高手过招"); 132 comboBox.addItem("新手上路"); 133 134 comboBox.setSelectedIndex(level); 135 comboBox.setBounds(610, 370, 200,30 ); 136 frame.getContentPane().add(comboBox); 137 138 /** 139 * 添加下拉框监听事件 140 */ 141 comboBox.addItemListener(new ItemListener() { 142 @Override 143 public void itemStateChanged(ItemEvent e) { 144 int choice = comboBox.getSelectedIndex();; 145 if (level != choice) { 146 level = choice; 147 main(null); 148 } 149 } 150 }); 151 152 153 /** 154 * 添加按钮commit监听事件 155 */ 156 commit.addActionListener(new ActionListener() { 157 158 @Override 159 public void actionPerformed(ActionEvent e) { 160 int errorCount = 0; 161 for (int i = 0; i < 9; i++) { 162 for (int j = 0; j < 9; j++) { 163 if (!(chessBoard[i][j].getText().equals(Integer.toString(cellsAnswer[i][j])))) { 164 // 如果chessBoard内的文本与答案不相同 165 chessBoard[i][j].setForeground(Color.red); 166 chessBoard[i][j].setText(Integer.toString(cellsAnswer[i][j])); 167 errorCount++; 168 } 169 } 170 } 171 if (errorCount == 0) { 172 // 全部填写正确,弹出very good对话框 173 String successInfo = new String(); 174 successInfo = "<html><font face = " + "宋体 " + "size = " + "5" + ">" + "太厉害咯,送你一颗小心心!" 175 + "</font></html>"; 176 ImageIcon iconSucc = new ImageIcon("小心心.jpg"); 177 JOptionPane.showMessageDialog(null, successInfo, "恭喜闯关成功!", JOptionPane.INFORMATION_MESSAGE, 178 iconSucc); 179 } else { 180 String failureInfo = new String(); 181 failureInfo = "<html><font face = " + "宋体 " + "size = " + "5" + ">" + "很遗憾,你填错了" + errorCount 182 + "个格子!" + "<br>红色字体的格子显示的是正确答案哦!" + "<br>要不再来一局?!" + "</font></html>"; 183 ImageIcon iconFail = new ImageIcon("哭泣的小熊猫.jpg"); 184 JOptionPane.showMessageDialog(null, failureInfo, "挑战失败", JOptionPane.INFORMATION_MESSAGE, 185 iconFail); 186 } 187 } 188 }); 189 190 /** 191 * 添加another再来一局的监听事件 192 */ 193 another.addActionListener(new ActionListener() { 194 195 @Override 196 public void actionPerformed(ActionEvent e) { 197 main(null); 198 } 199 }); 200 } 201 }
数独带挖空的棋盘生成代码如下:
1 /** 2 * 用来设置棋盘中的空白格子 要求:棋盘上总空格数大于30,小于60,每3*3小棋盘中挖空不少于2个 3 * 实现:为了配合GUI界面的生成,将游戏等级设置为level, 4 * level的值0表示难度适中,挖空27~36个格子,1表示较难,挖空36~54个格子,2表示简单,挖空18个格子 5 * 本类中直接生成的棋局难度为1,挖空36~54个格子 6 */ 7 private void setBlank(int level) { 8 // int blankSum = 0; //用来计算总的挖空数 9 int temp = 0; // temp得到每个3*3格子需要挖空的数量 10 11 for (int i = 0; i < 3; i++) { 12 for (int j = 0; j < 3; j++) { 13 if (level == 0) { // level表示游戏难度,0表示难度适中 14 temp = (int) (Math.random() * 10) % 2 + 3; // temp得到每个3*3格子需要挖空的数量3~4个 15 } else if (level == 1) { // level为1时表示较难 16 temp = (int) (Math.random() * 10) % 3 + 4; // temp得到每个3*3格子需要挖空的数量4~6个 17 } else { // level为2时设置为新手上路 18 temp = 2; // temp得到每个3*3格子需要挖空的数量2个 19 } 20 // blankSum += temp; 21 while (temp > 0) { 22 int blankRandom = (int) (Math.random() * 10) % 9; 23 // 将3*3九宫格从左到右,从上到下当做标号为0~8的格子,取随机数表示当前3*3的格子内需要挖空的格子位置 24 // 以下三句计算要挖空的格子在整个棋盘上的位置 25 int rowTemp = blankRandom / 3; 26 int row = rowTemp + i * 3; 27 int col = blankRandom - 3 * rowTemp + j * 3; 28 if (chess[row][col] != 0) { 29 chess[row][col] = 0; 30 } else { 31 continue; 32 } 33 temp--; 34 } 35 } 36 } 37 // System.out.println("blankSum = " + blankSum); 38 // show(chess); 39 }
GUI界面截图如下:
功能介绍:图一游戏开始默认为难度适中,玩家可以在“下拉框”选择游戏难度,空着的格子可以填入数字,玩家填写完成之后点击“提交答案”按钮,如果全部正确会出现图二的提示框,如果有错误会出现图三所示的提示框,玩家填错了的位置会在棋局内以红色字体显示,此格子内显示的是正确答案。点击“再玩一局”按钮可以重新开始游戏。
(下图为图一)
(下图为图二)
(下图为图三)
作业要求的生成数独棋盘后将其写入文件“sudotiku.txt”中,结果如下:(两个程序的完整代码已上传至coding,地址:https://coding.net/u/dhlg_201810812008/p/sudotiku/git/tree/master/firstGui/src/firstGui)
请输入要生成的数独的个数:(1~1000000之间)3
9 8 0 0 4 3 0 0 2
0 3 4 0 0 7 6 8 0
5 0 0 0 8 9 0 3 0
0 0 3 4 0 0 0 0 0
4 5 0 7 0 8 0 0 3
0 0 0 3 0 2 4 6 5
0 0 2 5 7 0 0 4 8
0 0 9 8 3 0 0 2 0
8 0 5 9 0 0 3 7 0
0 3 9 7 0 0 0 1 0
1 0 0 3 0 8 6 0 9
0 0 0 1 0 9 0 0 8
0 0 3 4 0 0 0 9 0
0 5 0 8 0 7 1 0 3
7 0 0 0 1 3 0 4 0
3 4 0 6 0 1 0 0 0
0 0 1 0 0 2 3 0 4
0 8 2 0 3 0 7 6 1
0 2 0 9 0 0 0 4 5
1 0 0 2 0 6 7 0 9
0 8 9 0 0 0 0 3 0
0 1 0 4 0 0 0 9 8
0 0 7 6 0 0 3 0 0
0 9 0 3 1 0 4 0 0
0 0 0 7 9 0 0 0 0
8 6 1 0 0 4 9 0 0
9 0 4 8 0 3 5 0 1
程序运行的正确性以及性能分析:GUI游戏界面运行正常,填入答案后玩家可以得到正确性反馈,也可以根据需要调整游戏难度,性能较好。
本次学习总结: 由于之前没有做过GUI界面,个人感觉这次的作业对我的挑战很大,总共用了3~4天的时间。但是通过本次实践项目也学习到了很多Java的知识点,以下做个简单的总结。
1、java中两个二维数组answer和chess,要将answer的值赋给chess,如果使用chess = answer结果是引用传递,使用arraycopy方式只能实现一维数组的赋值。要给二维数组赋值必须要将其当做数组的数组,一个数组一个数组地赋值。代码如下:(CSDN上关于数组的赋值问题博客 https://blog.csdn.net/u010000515/article/details/72845085)
for (int i = 0; i < answer.length; i++) {
System.arraycopy(answer[i], 0, chess[i], 0, answer.length);
}
2、关于GUI的一些知识点罗列如下:
frame.setResizable(false); //设置窗口是否可变大小
frame.getContentPane().setLayout(null); //要设置Jpanel的位置与大小,要先关闭frame的布局管理器
chessBoard[i][j].setFocusable(false); // 设置是否可获得焦点
JLabel labelRule = new JLabel("<html>" + hintInform + "</html>"); //JLabel中要实现超出宽度自动换行可以使用html来实现,还可以加<br>实现换行
/** 以下代码添加某个按钮的监听事件*/
comboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
//添加监听事件
}
});
3、学习中遇到的问题可以说是非常多了,对GUI界面的控件和方法不熟悉等等,主要是查阅API文档和网上百度得到解答,觉得对于我比较难的应该是对控件方法的调用和监听事件,以后还需多多学习。