(一)实现功能:
1.基础的成绩排序
2.相同成绩随机排名
3.用户名注册重复
(二)效果视频:
(三)代码实现:
3.1 && 3.2 在FileRead类中新增方法如下:
具体的代码实现:
package DemoProject.fjm0601.FileWandRSystem;import DemoProject.fjm0601.GameWD;import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Locale;public class FileRead {public static File file = new File("src/DemoProject/fjm0601/ScoreFile.txt");static FileReader fr;static BufferedReader bfr;//获取初始的绘制高度:static int y = 10;//最终画板采取的绘制方法:public static void RankOutCurrentBest(File file,Graphics graphics) throws IOException {try {fr= new FileReader(file);bfr = new BufferedReader(fr);} catch (FileNotFoundException e) {throw new RuntimeException(e);}//获取字符串形式的成绩名单ArrayList<String> scoreList = getScoreList(bfr);System.out.println("此时的成绩名单为:"+scoreList);//获取先前查询过的最大值数组ArrayList<Integer> BScoreList = new ArrayList<>();int BScore;String [] NameArr;int CurrentNum = 0;while (true){//获取此时的最大成绩(除去先前已检查过的成绩)BScore = getBestScore(BScoreList,scoreList);System.out.println("此时最好成绩为:"+BScore);//获取成绩最大且相同的用户名数组NameArr = viaScoreToNameArr(BScore,scoreList);System.out.println("成绩相同的有"+NameArr.length+"个人");System.out.println("成绩最好的人为:"+NameArr[0]);//将BScore放入查询过的最大数组中:BScoreList.add(BScore);//将此时成绩最大的用户们成绩写出到面板中readFileOnRow(BScore,NameArr,graphics);System.out.println("此人的成绩已被写入");//将被录入成绩的人计入CurrentNum += getTheScoreNum(BScore,scoreList);System.out.println("此时被写入成绩的人有:"+CurrentNum);//当被录入成绩的人等于scoreList的大小,意味着所有人的成绩皆被录入if(CurrentNum == scoreList.size()){System.out.println("成绩已写入完毕");break;}y += 20;}}//将文件里面的成绩存储进入动态数组public static ArrayList<String> getScoreList(BufferedReader bfr) throws IOException {ArrayList<String> scoreList = new ArrayList<>();String strLine;while (true){if((strLine = bfr.readLine()) != null){scoreList.add(strLine);}else{break;}}return scoreList;}//获取此时最大的成绩public static int getBestScore(ArrayList<Integer> BScoreList,ArrayList<String> scoreList){int BScore = 0 ;System.out.println("scoreList的长度为:"+scoreList.size());LabelName:for(String perScore : scoreList){int score = Integer.valueOf(perScore.split(":")[1]);System.out.println("此时找到的成绩为:"+score);//将"这一个"用户的成绩与先前的成绩做对比,一旦发现相同则跳出内循环,进而检查下一个同学的成绩。for(int previousScore : BScoreList){if(score == previousScore){//一旦发现相同,则跳出内部循环并开启下一次循环System.out.println("找到的该成绩与先前一致,因此退出并进入下一次循环");continue LabelName;}}if(BScore < score){BScore = score;}}System.out.println("最终获取的最好成绩为:"+BScore);return BScore;}//获取具有该成绩的人数public static int getTheScoreNum(int Score, ArrayList<String> scoreList){int EachScore;int i = 0;for(String Each : scoreList){String [] EachArr = Each.split(":");EachScore = Integer.valueOf(EachArr[1]);if(EachScore == Score){i++;}}return i;}//将成绩按行写入面板中public static void readFileOnRow(int BScore, String [] NameArr,Graphics graphics){String condition1;int x = 10;int i = 0;graphics.setColor(Color.BLACK);while (true){System.out.println("进入此循环中");if(i < NameArr.length){condition1 = NameArr[i] + ": " +BScore;graphics.drawString(condition1,x,y);if(NameArr.length > 1 && i != NameArr.length-1){y += 20;}i++;}else{break;}}}//整合获取相同成绩的用户名数组public static String[] viaScoreToNameArr(int BScore, ArrayList<String> scoreList){int EachScore;int len = getLen(BScore,scoreList);String [] NameArr = new String[len];int i = 0;for(String Each : scoreList){String [] EachArr = Each.split(":");EachScore = Integer.valueOf(EachArr[1]);if(EachScore == BScore){NameArr[i] = EachArr[0];i++;}}return NameArr;}//获取用户名数组的长度public static int getLen(int BScore, ArrayList<String> scoreList){int EachScore;int len = 0;for(String Each : scoreList){EachScore = Integer.valueOf(Each.split(":")[1]);if(EachScore == BScore){len++;}}return len;}public static void main(String[] args) {JFrame jFrame = new JFrame();jFrame.setSize(400,400);jFrame.setLocationRelativeTo(null);JPanel jPanel = new JPanel();jFrame.add(jPanel);jFrame.setVisible(true);Graphics graphics = jPanel.getGraphics();// readFileOnRow(file,graphics);}}
由于需要考虑相同成绩的排名,因此不能简单获取成绩再排序,整体的实现思路如下
1.先获得此时的成绩单
2.创建一个死循环,在单次循环中:获取此时的最好成绩->依据最好成绩获取具有相同成绩的用户->按照用户 + 成绩的格式写到面板上。下次排序时,避开此次及先前排好的成绩记录并追加已排好的用户成绩,当排好所有用户后退出循环
3.3 在GameWD中的paint2()方法中修改按钮监听方法
实现提示框的添加
confirmBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String name = jtf.getText();try {if(name == null){JOptionPane.showMessageDialog(null,"用户名不可为空");return;}if(scoreMap.containsKey(name)){JOptionPane.showMessageDialog(null,"该用户名已注册");return;}writeFile(name,Score);JOptionPane.showMessageDialog(null,"写入成功,祝您下次取得更好成绩");} catch (IOException ex) {throw new RuntimeException(ex);}}});
3.4 在RankPanel中替换成RankOutCurrentBest()方法
public class RankJPanel extends JPanel {public void paint(Graphics graphics){super.paint(graphics);File file = FileRead.file;try {FileRead.RankOutCurrentBest(file,graphics);} catch (IOException e) {throw new RuntimeException(e);}}
}
补充:
1.为排行榜加入柱状图:
2.加入用户名规范:
实现代码如下:
1.在readFileOnRow的方法的死循环中修改为下:
while (true){System.out.println("进入此循环中");if(i < NameArr.length){condition1 = NameArr[i] + ": ";double pillarLen = 0;graphics.drawString(condition1,x,y);if(BScore > 1000){pillarLen = BScore / 10.0;}else if( BScore < 100){pillarLen = BScore ;}graphics.fillRect(x + 90,y -7 , (int) pillarLen,5);graphics.drawString(BScore + "",x +(int)pillarLen + 100,y);if(NameArr.length > 1 && i != NameArr.length-1){y += 20;}i++;}else{break;}}
注意:为避免成绩过长画出边界,对超高成绩的柱状图作压缩处理
2.在GameWD的按钮监听方法里面加入:
if(!name.matches("^[\u4e00-\u9fa5]{5}\\d{4}$")){JOptionPane.showMessageDialog(null,"用户名不规范,注册格式为:五个中文字符+四个数字");return;}
括号内的字符就是五个中文字符+四个数字的格式
3.小漏洞修复:反复点击排行榜,整体的成绩图会不断下滑。
原因:单次程序(整个飞机大战)运行后,每次点击排行榜,y值始终在增加
解决方法:在按钮监听器中新增 FileRead.y = 20; :
确保每次点击排行榜的y的初始值都为20
异常/遗漏修复:
(一)数组越界异常:
异常:每次拼接内容:
原因:用户 + 成绩的时候,数组调用在条件判断之前,
解决方法:将数组调用放在条件判断内
(二)排行榜上重复添加相同成绩用户且该用户成绩为0:
原因:使用新方法LabelName的时候,误用break LabelName,导致没有进入下一次的大循环反而直接掠过了整个循环。使得后续的成绩皆为此次成绩0
解决方法:将break LabelName改为continue LabelName
(三) 排行榜显示完后无法结束程序
原因:一开始简单认为退出死循环的条件为:
遍历到成绩为0的时候(实际上最低成绩可能不为0)
解决方法:修改退出循环条件为:排好序的人数达到成绩数组的长度(也就是所有人都排好序)即退出。
(四)排行榜上相同成绩的信息重叠在一块。
原因:相同成绩的人在写入时,没有改变y值
解决方法:当NameArr(也就是相同成绩的玩家)的长度大于1,且没有遍历到最后一人(防止其他成绩的用户在绘制时出现 ”空行“ 的问题),y += 20.
(五) 绘制的时候所有成绩全部画在一块
原因:绘制的y位置仅在方法调用时创建,每一次调用方法后都会创建新的y,致使y没有实时增加
解决方法:添加静态修饰符static,并将其提到全局变量的位置。
感悟:
在进行排序功能时,曾想过:先用哈希表存储各值,比较完值之后,反推回到键(也就是用户名),再进行拼接,失败。
原因: 哈希表没有通过值返回键的方法,也难以通过手动遍历哈希表获取键。
解决方法:通过使用String[]数组预先存储各人的 “用户:成绩” 信息。再通过split()方法获取[用户,成绩]格式的一维数组,最后比对成绩。间接达到二维数组的效果,实现用户与成绩的关系绑定