😉😉引言:此篇实现的是一个可视化的通讯录界面,能够实现简单的增删改查等功能,详细分析请看正文,总结为作者亲笔所写,花费的时间比较多,源码来源为OneWan
,感谢UU
的学习分享,代码非常规范,非常值得学习,正文未贴所有代码,贴了核心的一些代码【已征求OneWan
的同意】,如有读者想要复现此程序,可根据本文分析再自行再进行码代码~~
😊😊Tips:未学习过JAVA GUI 的读者,可先把基础知识过一遍再进行学习,可在哔站找找视频,作者学习的是以下链接中的知识【JAVA图形界面学习网站】,读者可根据自己的喜好进行选择和学习!此文用的是JAVA GUI SWing
,JavaFx
作者还有待去学习!
🤣🤣:原创不易,请勿抄袭,如果错误或者侵权,请联系作者,谢谢!
整体概述
本文是基于JAVA GUI 实现的一个可视化的通讯录界面,核心分为以下几个部分:
User
类的构建:单个联系人的信息GUI
的构建:界面的实现UserTableModel
类的构建:对信息表的各种操作(初始化,插入,删除…)UserTable
类的构造:信息表的可视化界面实现panel
功能模块的实现:此篇只详细分析前三个部分- 插入模块
- 删除模块
- 查询模块
- 修改模块
- 。。。。
Buttons
类的构建:8个按钮的实现和事件监听器layout
模块:适配大小【将在第二版总结概述中详细分析】
主要知识
-
Java
的一些基础知识,面向对象思想,各种API
的调用等等 -
Java GUI Swing JFrame
框架 -
涉及到正则表达式的部分知识
设计目的
- 加强对
Java
基础语法的学习 - 学习和深入了解
Java
的可视化界面 - 完成一个可视化的增删改查的通讯录界面
- 从实践中了解更多的知识,提升自己的代码能力
详细设计
声明:以下代码的关于界面定位和大小的问题,读者需要自行更改,由于OneWan
重构多次,故意折磨作者,所以作者不进行更改以下设计到这些问题的地方,第二版中会整体再进行重新具体介绍,以下只提供逻辑思想!
😝1 .User
类的构建
- 此处主要定义了表格的列属性,包括编号,姓名,性别,手机号,邮箱,住址,关系和备注,共7个部分
private int id;
private String name;
private String sex;
private String phoneNumber;
private String email;
private String address;
private String relationship;
private String notes;
- 然后是构造方法的实现
public User(int id, String name, String sex, String phoneNumber, String email, String address, String relationship, String notes) {
this.id = id;
this.name = name;
this.sex = sex;
this.phoneNumber = phoneNumber;
this.email = email;
this.address = address;
this.relationship = relationship;
this.notes = notes;
}
- 然后是getter和setter方法的实现
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
小结:
- 基本的类的实现
- 成员属性,构造方法,getter和setter方法,都属于基础部分,需要详细掌握
- 成员属性可根据实际情况进行调整
😝2. GUI
的构建
- 构造
JFrame、UserTable、Buttons
对象,并设置成私有静态常量
private static final JFrame GUI = new JFrame("婉君被通讯录吓晕");
private static final UserTable userTable = new UserTable();
private static final Buttons buttons = new Buttons();
- 设置
GUI
的基本样式
GUI.setSize(width, height); // 读者可根据自己的实际情况进行设置
GUI.setLocationRelativeTo(null);
GUI.setLayout(null);
GUI.setResizable(false);
GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GUI.setVisible(true);
// 样式读者也可自行定义
- 定义了俩个方法,主要为了其他文件来获取到此处的对象
public static JFrame getGUI() {
return GUI;
}
public static UserTable getUserTable() {
return userTable;
}
小结:
- 最基础的界面构建和样式设置,对此知识不了解的可看
Tips
说明中的推荐 - 此处比较值得学习的地方是模块化的处理,比如三个静态对象的设置,这样可以让我们的项目结构更加清楚,每一块都具体分开,可读性高,同时做到了“隐私保护”
😝3. UserTableModel
类的构建
- 设置了三个对象
// columnNames--存放列属性 viewUsers -- 要显示的数据 users -- 初始化的数据
private String[] columnNames = {"编号", "姓名", "性别", "手机号", "邮箱", "住址", "关系", "备注"};
private List<User> viewUsers = new ArrayList<>();
private List<User> users = new ArrayList<>();
// 同上,列属性根据实际自定义即可
- 无参构造,初始化数据
public UserTableModel() {
users.add(new User(3, "阿萨德", "女", "22222222222", "7777777777@qq.com", "湖南科技大学", "其他", "无"));
users.add(new User(4, "投影仪", "男", "11111111111", "1123485778@qq.com", "湖南科技大学", "同事", "公司"));
users.add(new User(5, "机器猫", "男", "11111111111", "2222222222@qq.com", "湖南科技大学", "同事", "公司"));
viewUsers.addAll(users);
}
- 有参构造,传入初始化的数据,并赋值给要显示的数据对象
public UserTableModel(List<User> users) {
if (users != null) {
this.users = users;
viewUsers.addAll(users);
}
}
-
数据操作模块:主要分为7个小部分:
- 更新数据
// 更新数据 public void updateTable() { for (int i = 0, size = viewUsers.size() ; i < size ; ++i) { viewUsers.get(i).setId(i); } }
- 插入数据
// 插入数据 public void insertTable(User user) { viewUsers.add(user); updateTable(); }
- 查询数据 --> 匹配数据模块:准确得到要查询的内容
// 查询数据 public void checkTable(User checkUser) { List<User> users = new ArrayList<>(this.users); // 迭代器 Iterator<User> it = users.iterator(); while (it.hasNext()) { User user = it.next(); if (matchUser(user, checkUser)) { continue; } it.remove(); } viewUsers = users; }
// bool 匹配数据 [一小部分代码] public static boolean matchUser(User user, User checkUser) { // 非空 -- contains: 模糊匹配 equals: 必须一致 if (!checkUser.getName().isEmpty()) { if (!user.getName().contains(checkUser.getName())) { return false; } } // PhoneNumber、Email、Address、Notes和Relationship 属性雷同 if (!checkUser.getRelationship().equals("全部")) { return user.getRelationship().equals(checkUser.getRelationship()); } // Sex 属性雷同 return true; }
- 删除数据
- 重置数据
- 清空数据
-
获取
users
-
获取表格大小
-
重写方法(继承了
AbstractTableModel
类)- 获取行列大小,列名字
- 获取具体数据
// 获取数据 @Override public Object getValueAt(int rowIndex, int columnIndex) { User user = viewUsers.get(rowIndex); if (columnIndex == 0) return user.getId(); if (columnIndex == 1) return user.getName(); if (columnIndex == 2) return user.getSex(); if (columnIndex == 3) return user.getPhoneNumber(); if (columnIndex == 4) return user.getEmail(); if (columnIndex == 5) return user.getAddress(); if (columnIndex == 6) return user.getRelationship(); if (columnIndex == 7) return user.getNotes(); return null; } // 与初试设定保持一致性
小结:
- 较难的地方在于整体逻辑的实现,我们可以通过以上给出的代码看出,他的实现其实没有那么难,关键在于如何能够这么清晰的表达出我们想要表达的东西,这一点也是作者需要努力学习的地方,
OneWan
的构思确实让作者感到佩服,值得深入学习。 - 要理解每一个方法的具体功能是什么,我们需要的功能是什么,然后再进行码代码,可参考作者所列出的大纲进行思考
😝4. UserTable
类的构造
- 定义了四个成员属性
private final UserTableModel utm;
private final JTable table;
private final TableColumnModel tcm;
private final JScrollPane sp;
- 无参构造,并在中间利用
this()
调用了有参构造
public UserTable() {
this(OneWanAddressList.getGUI(), 10, 10, 765, 300);
}
- 设置列的宽度
// 设置列的宽度
public void setColumnWidth(int columnIndex, int width) {
tcm.getColumn(columnIndex).setPreferredWidth(width);
}
- 俩个有参构造
public UserTable(Container container, int x, int y, int w, int h) {
utm = new UserTableModel();
table = new JTable(utm);
tcm = table.getColumnModel();
sp = new JScrollPane(table);
container.add(sp);
// 显示单个单元格的标准类 -- 居中
DefaultTableCellRenderer dc = new DefaultTableCellRenderer();
dc.setHorizontalAlignment(SwingConstants.CENTER);
table.setDefaultRenderer(Object.class, dc);
setColumnWidth(0, width); // 根据实际情况进行调整 【略具体代码】
setColumnWidth(1, width);
sp.setBounds(x, y, w, h);
}
public UserTable(Container container, int x, int y, int w, int h, List<User> users) {
utm = new UserTableModel(users);
}
getter
方法- 刷新数据
public void updateTable() {
table.updateUI();
}
小结:
- 基础类的构造,同时在无参构造里面使用了
this
来调用有参构造,getter的实现 - 值得学习之处:当在代码中需要重复写某一类的代码行,可构建一个方法,进行统一处理,这样代码可读性更高,也更加的清晰明了,比如上文中
setColumnWidth
方法的构建 - 此程序最开始采用的是绝对定位的方式,后来发现如果电脑的分辨率不同,显示的界面大小有时候实在是太小了,所以
OneWan
又进行了适配处理,所以略微跟上文有一点的差别,不过整体思路是不变的,读者也可根据自己的喜好和美感进行改进~~
😝5. panel
功能模块的实现
✨(1):插入数据
- 定义成员变量
// 新建一个画板,并重写方法,定义画板大小【做了适配之后无重写】
protected final JPanel panel = new JPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(width, 170);
}
};
// 五个信息输入框
private final JTextField[] textFields = new JTextField[5];
// 下拉框内容
protected final JComboBox<String> cbSex = new JComboBox<>(new String[]{"男", "女"});
protected final JComboBox<String> cbRelation = new JComboBox<>(new String[]{"家人", "亲戚", "朋友", "同学", "同事", "本人", "其他"});
- 无参构造,初始化界面
public InfoInputPanel() {
panel.setLayout(null);
panel.setBounds(x, y, width, height);
// 姓名
JLabel[] labels = new JLabel[7];
labels[0] = new JLabel("姓名:");
labels[0].setBounds(x, y, width, height);
textFields[0] = new JTextField();
textFields[0].setBounds(x, y, width, height);
panel.add(labels[0]);
panel.add(textFields[0]);
}
// 其他列属性雷同,不再一一给出,定位根据实际情况进行调整即可
-
get
方法getPanel()
getUser()
:创建User
对象
public User getUser(int id) { return new User(id, textFields[0].getText(), (String) cbSex.getSelectedItem(), textFields[1].getText(), textFields[2].getText(), textFields[3].getText(), (String) cbRelation.getSelectedItem(), textFields[4].getText()); }
getText()
:用一个String
对象数组存储输入的内容
-
检查函数 --> 判断插入的信息是否满足数据要求,大体有以下几方面的要求:
- 姓名不能为空,必须全是中文,长度不能超过5个字符
if (!infos[0].matches("[\u4E00-\u9FA5]*")) { JOptionPane.showMessageDialog(panel, "姓名必须全为中文!" , "信息错误", JOptionPane.ERROR_MESSAGE); return -1; }
- 手机号留空填“无”,必须全是11位的数字
if (!infos[1].equals("无")) { if (infos[1].isEmpty()) { JOptionPane.showMessageDialog(panel, "手机号留空需填\"无\"!" , "信息错误", JOptionPane.ERROR_MESSAGE); return -1; } if (!infos[1].matches("[0-9]*")) { JOptionPane.showMessageDialog(panel, "手机号必须全为数字!" , "信息错误", JOptionPane.ERROR_MESSAGE); return -1; } if (infos[1].length() != 11) { JOptionPane.showMessageDialog(panel, "手机号必须全为11位数字!" , "信息错误", JOptionPane.ERROR_MESSAGE); return -1; } }
- 邮箱,住址和备注,留空填“无”
-
执行操作
public void execute() { UserTable userTable = OneWanAddressList.getUserTable(); userTable.getUtm().insertTable(getUser(0)); userTable.updateTable(); }
小结:
- 此小篇中主要有五个模块:变量定义,无参构造,
get
方法,检查函数,执行函数 - 在检查函数中,用到了正则表达式的知识,正则在日常中用的比较多,读者不熟悉此知识点的可以查资料自行学习,作者是在用
Python
写课表解析的时候,接触到此知识点,要多练习,不过也不用全记住语法,用到的时候再查也行的 JPanel、JLabel、JTextField
都是一些界面的基础知识【不清楚的可看开头的Tips
】- 我们还可以发现,有时候我们定义的是
private
,有时候定义的是protected
等,权限修饰符一定要正确使用,规范使用,展示什么叫做计算机人,切不可让别人看笑话
✨(2):查询数据
-
继承了“插入数据”类,重写了检查函数和执行函数
-
初始化,增加了下拉框的内容,并设置成了默认
public InfoCheckPanel() {
cbSex.addItem("全部");
cbRelation.addItem("全部");
cbSex.setSelectedIndex(2);
cbRelation.setSelectedIndex(7);
}
-
检查函数 --> 要求如下:
- 姓名必须全为中文且不超过五个字
- 手机号必须是数字
-
执行操作:与“插入数据”雷同
✨(3):删除数据
- 基础构建与“插入数据”模块一致: 成员变量 + 画板构建 +
getter
方法
private final List<User> users;
- 有参构造,构建界面
public InfoDeletePanel(List<User> users) {
this.users = users;
panel.setLayout(null);
new UserTable(panel, x, y, w, h, users);
}
- 执行操作
public void execute() {
UserTable userTable = OneWanAddressList.getUserTable();
// forEach() 方法用于遍历动态数组中每一个元素并执行特定操作
users.forEach(user -> userTable.getUtm().deleteTable(user));
userTable.updateTable();
}
😝6. Buttons
类的构建
✨(1) 插入
insertButton.addActionListener(e -> {
InfoInputPanel iip = new InfoInputPanel();
int t = -1;
while (t != 0) {
// op=0-->表示用户点击了第一个按键【确认】,op=1-->表示用户点击了第二个按键【取消】
int op = JOptionPane.showConfirmDialog(GUI, iip.getPanel(), "新增数据", JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, new ImageIcon());
if (op == 0) t = iip.checkInfo();
else break;
}
if (t == 0) iip.execute();
});
✨(2) 查询
- 与“插入“操作逻辑上基本一致
checkButton.addActionListener(e -> {
int t = -1;
while (t != 0) {
int op = JOptionPane.showConfirmDialog(GUI, icp.getPanel(), "查询数据", JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, new ImageIcon());
if (op == 0) t = icp.checkInfo();
else break;
}
if (t == 0) icp.execute();
});
✨(3) 删除
- 先要获取到查询的数据,
userTable.getTable().getSelectedRows();
,然后进行遍历,得到users
数据对象,再进行删除确定判断
deleteButton.addActionListener(e -> {
int[] rows = userTable.getTable().getSelectedRows();
if (rows.length == 0) {
JOptionPane.showMessageDialog(GUI, "请选择数据!" , "选择错误", JOptionPane.ERROR_MESSAGE);
return;
}
List<User> users = new ArrayList<>();
for (int row : rows) {
users.add(userTable.getUtm().getUsers().get(row));
}
InfoDeletePanel idp = new InfoDeletePanel(users);
int op = JOptionPane.showConfirmDialog(GUI, idp.getPanel(), "删除数据", JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, new ImageIcon());
if (op == 0) idp.execute();
});
页面展示
- 首页
- 添加数据
- 删除数据
- 查询数据
Tips
以上只给出部分效果图截屏
心得体会
- 我把此篇命名为“婉君被通讯录吓晕”,原因是他真的真的很折磨!!!与
OneWan
一起交流此程序更好的展示和实现,一起喊“救命”,也从中掌握了更多的技术知识,在提出问题-发现问题-讨论交流-解决问题等过程中,作者都感受颇深,也不断提高着自己的代码能力,折磨并快乐着~✨✨ - 此程序的完成需要花费较多的精力,首先得掌握最基础的知识模块,接着是对程序整体架构的思考和逻辑问题的解决,在此特别感谢
OneWan
的耐心指导和教学!!😍😍 - 通过此次学习,我发现自身还存在很大的不足,接下来还会继续学习,此篇为“婉君被通讯录吓晕”学习总结的第一篇,没有详细的介绍完整个程序,整篇将近4000字,之后会写第二版总结再详细介绍,预估会超过一万字,小编会继续加油的!期待第二版总结的推送~~😊😊
NODE: 第二版由于作者时间原因,emm,没有继续跟进,核心为适配的部分,在之后的博客文中会介绍~~😝😝