如何用Java Swing实现车牌管理系统
前言
这是我的课程设计周的大作业。这个系统编写中利用了Java Swing作为图形库。在对于车牌的排序和查找算法中,应用了对半查找,顺序查找,分块索引以及快排算法。
项目中使用的车牌号做了简化处理,不进行复杂的车辆类型区分,统一为6位。数据库中的车牌均为信息均为本人伪造。
项目地址
(PS:虽然造福学弟学妹,但是我确实还是有点私心的,希望各位借鉴了代码的给仓库点个星星,毕竟我也想得到点正反馈嘛)
github仓库
gitee仓库
作业要求
在汽车数据的信息模型中,汽车牌照是具有结构特点的一类关键字。汽车牌照是字母和数字混编的,例如:苏A0B7238。利用查找和排序算法,实现省内汽车牌照的快速查找。
1、需要设置普通用户、超级管理员不同角色,不同角色登录后的权限各不相同,普通用户可以进行查询;超级管理员有对汽车牌照增加、删除和修改的权限。
2、汽车牌照信息应包含号码、车辆、车主等相应信息,信息保存在文本文件中。
3、对汽车牌照进行排序,并实现顺序查找功能。
4、采用对半查找汽车牌照查询出所对应的车辆及车主信息。
5、可以按城市建立索引表,进行分块索引查找。
6、界面美观,交互方便
经过整理的项目结构
需要编写两种界面,由管理员进入的界面查询,修改车号牌信息,由普通用户进入查询车牌号信息。
- 普通用户
- 按照车牌号查找车牌(对半查找算法)
- 按照车主姓名查找车牌(顺序查找算法)
- 按照城市分布查找车牌(分块索引算法)
- 管理员
- 增加车牌
- 删除车牌
- 修改车牌信息
代码实现
对象分类
对于该项目进行分析可以知道,需要分出登录界面和应用内界面两个UI类,车牌号类,登录用户类两个信息类。
HomePage主类
没有太多好说的,最先写出来,只需要利用Java Swing画出一个登录面板即可。
画出样式图如下
这里需要说明的是,为了使登陆界面好看,我借鉴了一些美化方式,将JTextField 以及JPasswordField和JButton做了一些美化。
//自定义文本框
private class Login_textfield extends JTextField{
MatteBorder matteBorder=new MatteBorder(0, 0, 1, 0, Color.white);
public Login_textfield(int n) {
super(n);
setFont(new Font("宋体", 1, 30));
setPreferredSize(new Dimension(255, 30));
setOpaque(false);
setBorder(matteBorder);
}
}
//自定义密码框
private class Login_codefield extends JPasswordField{
MatteBorder matteBorder=new MatteBorder(0, 0, 1, 0, Color.white);
public Login_codefield(int n) {
super(n);
setFont(new Font("宋体", 1, 30));
setPreferredSize(new Dimension(255, 30));
setOpaque(false);
setBorder(matteBorder);
}
}
//自定义按钮
private class Login_Button extends JButton{
public Login_Button(String text) {
super(text);
setBackground(new Color(0,131,255));
setPreferredSize(new Dimension(215, 37));
setForeground(Color.white);
setFocusPainted(false);
setFont(new Font("微软雅黑", 1, 15));
setHorizontalAlignment(SwingConstants.CENTER);
setVisible(true);
}
}
User用户类
用户需要做的是区分管理员和普通用户。由于这个项目简单,就没有加入复杂的注册等功能。以及仅仅需要演示两个界面,故而我的账号都是固定的。
public class User {
public static String userName;
private static String password;
public int isAdmin;//0不是管理员,1是管理员,2为未注册用户
public User (String userName,String password,int i)
{
this.userName = userName;
this.password = password;
this.isAdmin =i;
}
public void FindUser()
{
int flag = 0;//确认用户用flag
FileReader fr = null;
try {
fr = new FileReader(new File("src/database/User.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BufferedReader bf = new BufferedReader(fr);
String b = null;
while (true) {
try {
if (!((b = bf.readLine()) != null)) break;
} catch (IOException e) {
e.printStackTrace();
}
// System.out.println(b);
int num[] = new int[]{0,0};
for(int i=0,j=0;i<b.length();i++){
char ch=b.charAt(i);
if(ch==' '){
num[j++]=i;
}
}
String u = b.substring(0,num[0]);
String p = b.substring(num[0]+1,num[1]);
int i =Integer.parseInt(b.substring(num[1]+1));
if(u.equals(this.userName) &&p.equals(this.password))
{
this.isAdmin=i;//匹配到用户设置权限
flag=1;
return;
}
}
//未匹配到用户返回失败虚拟用户
if(flag==0)
this.isAdmin=2;
}
}
这里为该类加入了两个类继承函数,主要是用于登录界面的判断用户所用。
Registration车牌号类
package code;
public class Registration {
public String province ="苏";
public String cityNumber;
public String number;
public String carNumber;
public String city;
public String owner;
public String car;
public String carType;
public void setCarNumber(String str)
{
this.carNumber = new String (str);
this.cityNumber = this.carNumber.substring(0,1);
this.number = this.carNumber.substring(1);
}
public void setOwner(String str)
{
this.owner = new String(str);
}
public void setCar(String str)
{
this.car = new String(str);
}
public void setCarType(String str)
{
this.carType = new String(str);
}
public String FindCity()//由cityNumber标号确定所属城市
{
switch (this.cityNumber)
{
case "A":
this.city = "南京";
break;
case "B":
this.city = "无锡";
break;
case "C":
this.city = "徐州";
break;
case "D":
this.city = "常州";
break;
case "E":
this.city = "苏州";
break;
case "F":
this.city = "南通";
break;
case "G":
this.city = "连云港";
break;
case "H":
this.city = "淮安";
break;
case "J":
this.city = "盐城";
break;
case "K":
this.city = "扬州";
break;
case "L":
this.city = "镇江";
break;
case "M":
this.city = "泰州";
break;
case "N":
this.city = "宿迁";
break;
}
return this.city;
}
public String NumberCamp(String str)//返回两相比较中较小的字符串
{
char[] S_tem = new char[6];
char[] tem = new char[6];
S_tem=str.toCharArray();
tem = this.carNumber.toCharArray();
for(int i =0;i<6;i++)
{
if((int)tem[i]<(int)S_tem[i])
{
return this.carNumber;
}
if((int)tem[i]>(int)S_tem[i])
{
return str;
}
}
return null;//若相同则返回null
}
}
车牌号的存储和查询由于混杂了英文和数字,使得我将其拆分为了这么多细致的信息。
FuncPage功能页类
功能页是主要承载增删改查和查询函数的功能类。同时也是主要的UI类。这一部分最为复杂。
首先可以来看看页面布局。
1.管理员界面
- 管理员添加车牌界面
这里归属地可以根据输入车牌中第一个城市字母标识确认归属地。并且可以进行简单的依据已有的数据库判断车牌号是否有误或者已经存在
- 管理员删除数据界面
在这里需要注意的是几个数据的更新。例如当前页面数,数据库内已有数据量。在每一次修改数据后都会重新刷新一遍UI页面进行更新。
- 管理员修改数据界面
修改界面仅仅是上面两个界面的拼合。并无其他新东西。
关于管理员界面其余还需要注意的是三个界面的独立性,均为线程开启然后刷新相应的UI区域。关于区域的转换我采取了JFrame里多个JPanel叠加的方式,怎样相应切换不同的Panel和刷新UI各位可详细看看相应部分的代码。
而且对于页面中循环列出数据库我采取的方式是每个数据后都跟上了一个button来响应选中对象并且执行相应的函数,对于删除我曾经想过利用多选择框来实现,但是方式就要变得不同而且略有麻烦。且需要调整相应的UI结构和布局以及UI的数据结构,于是我在局促的时间内放弃了。这也可以作为一个新的小问题留给各位去解决。
2.用户界面
- 按照车牌号查找车牌
- 按照车主姓名查找车牌
- 按照城市分布查找车牌
用户界面的UI机制比之管理员界面就要简单许多。没有太多可以讲的。关于作业要求中使用三种搜索方式来进行排序搜索则在下一部分详尽叙述。
搜索算法
使用的存储结构:
1、用于记录系统中所有车号牌的对象数组:ArrayList Re_Data
2、用于分块索引查找存储信息城市块的索引表:block[] ref = new block[13]
对于数据集的引入和排序
运用到了快排。并且由于这里是String类,所以大小的比较需要重新定义和规范,这部分的定义函数在Registration类里已经给出。
//对读入文件内数据的数组采取快速排序
public void QucikSort(ArrayList<Registration> Data)
{
Qucik_Sort(Data,0,Data.size()-1);
}
//数组快速排序递归函数
public void Qucik_Sort(ArrayList<Registration> Data,int low,int high)
{
int k;
if(low<high)
{
k=Partition(Data,low,high);
Qucik_Sort(Data,low,k-1);
Qucik_Sort(Data,k+1,high);
}
}
//数组快速排序序列划分函数
public int Partition(ArrayList<Registration> Data,int low,int high)
{
int i=low,j=high+1;
String pivot = Data.get(low).carNumber;
do{
do i++;
while (i<=high&&Data.get(i).carNumber.equals(Data.get(i).NumberCamp(pivot)));//i前进
do j--;
while (pivot.equals(Data.get(j).NumberCamp(pivot)));//j前进
if(i<j)
Swap(Data,i,j);
}while (i<j);
Swap(Data,low,j);
return j;
}
//数组交换函数
public void Swap(ArrayList<Registration> Data,int i,int j)
{
Registration a = new Registration();
a = Data.get(i);
Data.set(i,Data.get(j));
Data.set(j,a);
}
车牌对半查找算法
通过对半查找车牌信息,对于混杂了英文和数字的车号牌信息,通过将英文部分定义为ASCLL码大小比较,实现了对车号牌大小概念的确立,从而利用对半查找算法实现了根据输入车牌信息进行对半查找的功能。算法流程图如图所示。
//对半查找车牌号
public ArrayList<Registration> FindByNumber(ArrayList<Registration> Data,String number)
{
ArrayList<Registration> Result = new ArrayList<Registration>();
int result_i = Binarysearch(number,Data,0,Data.size()-1);
if(result_i==-1)
{
;
}
else
{
Result.add(0,Data.get(result_i)) ;
}
return Result;
}
//对半查找函数
int result=0;
public int Binarysearch(String input, ArrayList<Registration> Data, int low, int high)
{
int mid =(high+low)/2;
if(high>=low)
{
if(Data.get(mid).carNumber.equals(Data.get(mid).NumberCamp(input)))
Binarysearch(input,Data,mid+1,high);
else if(input.equals(Data.get(mid).NumberCamp(input)))
Binarysearch(input,Data,low,mid-1);
else
result = mid;
}
else {
result = -1;
}
return result;
}
车主信息顺序查找算法
通过顺序遍历已有对象数组的车主信息,实现车主信息的顺序查找。算法流程图如图示。
public ArrayList<Registration> FindByOwner(ArrayList<Registration> Data,String owner)
{
ArrayList<Registration> Result = new ArrayList<Registration>();
int j=0;
for(Registration ele:Data)
{
if(ele.owner.equals(owner)) {
Result.add(j++, ele);
}
}
return Result;
}
城市分块索引算法
通过先对已有对象数组的快排后可以实现数据的块间有序,由于通过城市分块索引不需要知道最大车牌号,实际中也没有最大车牌号这一概念,所以在本算法中没有记录块间最大数据,仅记录了本块对应的城市和所在块在数组中的起始下标。通过遍历数组,判断数据中所代表城市项的变化来确定所在块起始的数组下标。算法流程图如图示。
public ArrayList<Registration> FindByCity(ArrayList<Registration> Data,String city)
{
int i=0,j=1;
ArrayList<Registration> Result = new ArrayList<Registration>();
ref[0] = new block();
ref[0].City = Data.get(0).city;
String nowCity =Data.get(0).city;
ref[0].start = 0;
for(Registration ele:Data)
{
if(!ele.city.equals(nowCity))
{
ref[j] = new block();
ref[j].City = ele.city;
ref[j++].start = i;
nowCity = ele.city;
}
i++;
}
int start=0;
int end=0;
int flag =0;
for(block ele: ref)
{
if(ele.City.equals(city))
{
start=ele.start;
flag=1;
}
if(flag==1&&(!ele.City.equals(city)))
{
end = ele.start-1;
flag=0;
break;
}
}
if(flag ==1)
end=Data.size()-1;
for(i =start,j=0;i<=end;i++)
{
Result.add(j++,Data.get(i));
}
return Result;
}
public class block{
public String City;//该块部分代表的城市
public int start;//该块部分起始下标
}
一些问题的解释
实际上我在完成这个项目时已经有了不少经验了。对于Java Swing 的一些特性也较为熟悉了。所以在复述项目过程中,一些比较困难的多线程部分和单线程刷新机制就可能解释的不是那么清楚。如果你感兴趣的话,可以参考我这篇博文里Java swing实现跳一跳小游戏-记录我的JAVA大作业
的一些问题和参考文章。
对于那篇文章里我有一个未解的问题,即图片的加载貌似降低了像素,使得分辨率下降。后来我在写这个项目的时候无意间试出来,并不是Java Swing 降低了分辨率,而是我的显示器设置为125%的缩放设置使得界面里图片被扩大了,看起来就很模糊,像是降低了分辨率。
结语
实际上我认为这是我花费时间认真写的代码里,最不为老师重视的一个。我遇到了一个苛刻的老师,因为我的最后提交报告不够丰富,挑了重点按照要求只写了算法设计部分的报告被她称作未良好完成的报告。我甚至怀疑她猜测我的代码是网上抄来的而不相信是我自己独立写完的,这使得出成绩后我去诘问她的时候她甚至哑口无言只会道歉。这份努力了两周的工作没有得到其应有的结果,仅仅被给了一个一般分,没有拿到优秀。我把它列在这里,不作为一种耻辱,而作为一个警告,时时警醒我不公平的事情常常发生,仅靠自己的认真完成而希冀获得应得的回报有时实在被动。
同时我也在这里提醒看到了这篇文章的各位,如果你有益于借鉴我的一些工作,我不推荐直接搬走我的代码,一字不改,看也不看。我真诚地希望你可以认真对待,将其作为一个参考和借鉴。