(一)实现功能:
1.基础的成绩排序
2.相同成绩随机排名
3.用户名注册重复
(二)效果视频:
(三)代码实现:
3.1 && 3.2 在FileRead类中新增方法如下:

具体的代码实现:
java
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()方法中修改按钮监听方法
实现提示框的添加

java
confirmBtn.addActionListener(new ActionListener() {
@Override
public 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()方法
java
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的方法的死循环中修改为下:
java
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的按钮监听方法里面加入:
java
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()方法获取 [用户,成绩]格式的一维数组,最后比对成绩。间接达到二维数组 的效果,实现用户与成绩的关系绑定