通讯录总结-JAVA GUI- “婉君被通讯录吓晕”

😉😉引言:此篇实现的是一个可视化的通讯录界面,能够实现简单的增删改查等功能,详细分析请看正文,总结为作者亲笔所写,花费的时间比较多,源码来源为OneWan,感谢UU的学习分享,代码非常规范,非常值得学习,正文未贴所有代码,贴了核心的一些代码【已征求OneWan的同意】,如有读者想要复现此程序,可根据本文分析再自行再进行码代码~~

😊😊Tips:未学习过JAVA GUI 的读者,可先把基础知识过一遍再进行学习,可在哔站找找视频,作者学习的是以下链接中的知识【JAVA图形界面学习网站】,读者可根据自己的喜好进行选择和学习!此文用的是JAVA GUI SWing,JavaFx作者还有待去学习!

🤣🤣:原创不易,请勿抄袭,如果错误或者侵权,请联系作者,谢谢!

整体概述

本文是基于JAVA GUI 实现的一个可视化的通讯录界面,核心分为以下几个部分:

  • User类的构建:单个联系人的信息
  • GUI的构建:界面的实现
  • UserTableModel类的构建:对信息表的各种操作(初始化,插入,删除…)
  • UserTable类的构造:信息表的可视化界面实现
  • panel功能模块的实现:此篇只详细分析前三个部分
    • 插入模块
    • 删除模块
    • 查询模块
    • 修改模块
    • 。。。。
  • Buttons类的构建:8个按钮的实现和事件监听器
  • layout模块:适配大小【将在第二版总结概述中详细分析】

主要知识

  1. Java的一些基础知识,面向对象思想,各种API的调用等等

  2. Java GUI Swing JFrame框架

  3. 涉及到正则表达式的部分知识

设计目的

  1. 加强对Java基础语法的学习
  2. 学习和深入了解Java的可视化界面
  3. 完成一个可视化的增删改查的通讯录界面
  4. 从实践中了解更多的知识,提升自己的代码能力

详细设计

声明:以下代码的关于界面定位和大小的问题,读者需要自行更改,由于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();
});

页面展示

  • 首页

xdYcdJ.png

  • 添加数据

xdYsLF.png

  • 删除数据

xdYgo9.png

  • 查询数据

xdYfRx.png

Tips 以上只给出部分效果图截屏

心得体会

  • 我把此篇命名为“婉君被通讯录吓晕”,原因是他真的真的很折磨!!!与OneWan一起交流此程序更好的展示和实现,一起喊“救命”,也从中掌握了更多的技术知识,在提出问题-发现问题-讨论交流-解决问题等过程中,作者都感受颇深,也不断提高着自己的代码能力,折磨并快乐着~✨✨
  • 此程序的完成需要花费较多的精力,首先得掌握最基础的知识模块,接着是对程序整体架构的思考和逻辑问题的解决,在此特别感谢OneWan的耐心指导和教学!!😍😍
  • 通过此次学习,我发现自身还存在很大的不足,接下来还会继续学习,此篇为“婉君被通讯录吓晕”学习总结的第一篇,没有详细的介绍完整个程序,整篇将近4000字,之后会写第二版总结再详细介绍,预估会超过一万字,小编会继续加油的!期待第二版总结的推送~~😊😊

NODE: 第二版由于作者时间原因,emm,没有继续跟进,核心为适配的部分,在之后的博客文中会介绍~~😝😝

  • 10
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值