Java GUI swing 工具包使用总结

前言

最近用 swing 写了一个GUI图片接入的工具, 方便用于将图片数据通过接口推送到 web 项目中

做界面有点像写原生的 CSS


技术储备

  1. java

    基础知识, 面向对象封装, 继承, 接口, 多态

    匿名内部类, Lambda, 线程池

    单例模式(懒汉, 饿汉)

  2. 开发工具

    IDEA


功能需求

AI智检

  1. 支持合成图和序列图

  2. 工具支持填写数据地址,根据数据地址自动将数据快速导入平台

    数据为图片,图片信息在图片名称上面,字段信息已特定的分隔符连接,各个现场不同

  3. 工具支持填写客户违法编码

    如果填写了违法编码,则以填写为准,否则以字段映射关系切割为准

  4. 工具支持填写合成图模式

  5. 工具支持自动根据图片名称获取图片及相关信息

  6. 工具支持自动匹配车牌根据设置规则

    如果填写车牌匹配规则,以匹配为准,否则以字段映射关系切割为准

  7. 工具支持默认数据补充

    平台非必须字段,程序指定默认值

  8. 平台支持离线测试数据,快速看到算法识别指标 (保持智检测试看到的效果)

    • 数据导入之后自动创建测试任务,操作人员登录平台人工开启任务

    • 任务名称:工具接入_2020-07-04_12:22:30

  9. 支持导入进度查看

    • 导入总量及百分比
    • 导入成功数量及错误数量
  10. 支持查看工具执行日志

  11. 工具为windows exe格式


AI预审

  1. 支持合成图和序列图

  2. 工具支持填写数据地址,根据数据地址自动将数据快速导入平台

    数据为图片,图片信息在图片名称上面,字段信息已特定的分隔符连接,各个现场不同

  3. 工具支持自动根据图片名称获取图片及相关信息

  4. 工具支持默认数据补充

    平台非必须字段,程序指定默认值

  5. 平台支持离线测试数据,快速看到算法识别指标(保持智检测试看到的效果)

    • 数据导入之后自动创建测试任务,操作人员登录平台人工开启任务
    • 任务名称:工具接入_2020-07-04_12:22:30
  6. 支持导入进度查看

    • 导入总量及百分比
    • 导入成功数量及错误数量
  7. 支持查看工具执行日志

  8. 工具为windows exe格式


页面设计原型

img


实际效果


swing 工具包的组件使用

JFrame

public class MainFrame extends JFrame {
    public MainFrame(String title, int width,  int height) {
        this.setLayout(null);
        this.setSize(width, height);//尺寸大小, 单位像素
        this.setTitle(title);//标题
        //修改logo
        Image icon = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("favicon.ico"));
        this.setIconImage(icon);
        //String imagePath = "static/favicon.ico";
        //ImageIcon icon = new ImageIcon(imagePath);
        //this.setIconImage(icon.getImage());
        this.getContentPane().setBackground(Color.WHITE);//背景色
        this.setLocationRelativeTo(null);//取消相对定位
        this.setResizable(true);//尺寸是否可变
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//默认关闭操作
    }
}

JButton

public class AITrafficListener extends JButton {
    //单例
    private AITrafficListener(String text, Color color){
        this.setText(text);
        this.setBounds(ScreenSize.getWidth() / 4 - BaseConstant.CONST210, BaseConstant.CONST0, BaseConstant.CONST200, BaseConstant.CONST50);
        this.setFont(FontClass.boldFont20);
        this.setForeground(Color.WHITE);
        this.setBorderPainted(false);//去掉边框
        this.setFocusPainted(false);//去掉按钮文字周围的焦点框
        this.setBackground(color);
        //事件绑定
        this.addActionListener();
    }
    public static AITrafficListener instance;//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
    //public static 方法,返回实例对象
    public static AITrafficListener getInstance(String text, Color color){
        if(null==instance){//第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
            instance = new AITrafficListener(text, color);
        }
        return instance;//返回 instance指向的对象
    }

    //事件绑定
    public void addActionListener() {
        //按钮点击事件绑定
        this.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (StartButton.instance.isEnabled()) {//开始接入按钮打开时允许切换
                    AITrafficConfigPanel.instance.setVisible(true);
                    AIQualityConfigPanel.instance.setVisible(false);
                    AITrafficConfigPanel.instance.addComponents();//添加组件
                    //切换颜色/字体
                    AITrafficListener.instance.setBackground(ColorClass.color_18a5d6);
                    AIQualityListener.instance.setBackground(ColorClass.color_bbbbbb);
                    AITrafficListener.instance.setFont(FontClass.boldFont20);
                    AIQualityListener.instance.setFont(FontClass.font20);
                    BootStrap.business= BusinessConstant.AI_TRAFFIC_USINESS_MODE;//切换业务模式
                    //合成图模式隐藏
                    CombinedPicRule.instance.combinedPicTypeText.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
                    CombinedPicRule.instance.carNumPicIndexLabel.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
                    CombinedPicRule.instance.carNumPicIndex.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
                    CombinedPicRule.instance.recogPicIndexLabel.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
                    CombinedPicRule.instance.recogPicIndex.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
                    //替换url
                    String url = AccessURL.instance.getUrl();
                    url = url.replace(BusinessConstant.AIQUALITY_ACCESS_URL, BusinessConstant.AITRAFFIC_ACCESS_URL);//替换URL中间部分
                    AccessURL.instance.accessUrlText.setText(url);
                }
            }
        });

        //按钮悬停事件绑定MouseAdapter
        this.addMouseListener(new MouseAdapter() {
            public void mouseEntered(MouseEvent e) {
                AITrafficListener.instance.setFont(FontClass.boldFont20);
            }

            public void mouseExited(MouseEvent e) {
                if (!AITrafficConfigPanel.instance.isVisible()) {
                    AITrafficListener.instance.setFont(FontClass.font20);
                }
            }
        });
    }
}

JPanel

public class ConfigPanel extends JPanel {
    //单例面板类
    private ConfigPanel(Color color){
        this.setLayout(null);
        this.setBackground(color);
        this.setBorder(null);//去掉边框
        this.addComponents();
    }//私有化构造方法使得该类无法在外部通过new 进行实例化
    public static ConfigPanel instance;//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
    //public static 方法,提供给调用者,创建一次
    public static ConfigPanel createInstance(Color color){
        if(null==instance){//第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
            instance = new ConfigPanel(color);
        }
        return instance;
    }

    /**
     * 添加菜单组件
     */
    public void addComponents(){
        //导航菜单
    }
}

JScrollPane(滚动条)

public class LogScrollPanel extends JScrollPane {
    //单例面板类
    private LogScrollPanel(){
        this.setBackground(Color.WHITE);//背景色
        this.setForeground(Color.ORANGE);//设置字体颜色
        this.setBorder(BorderFactory.createTitledBorder(null, " 日志区 ", TitledBorder.LEFT, TitledBorder.ABOVE_TOP, FontClass.boldFont16, ColorClass.color_18a5d6));
        this.setBounds(ScreenSize.getWidth() / 2, BaseConstant.CONST0, ScreenSize.getWidth() / 2, ScreenSize.getHeight() - BaseConstant.CONST45);
        this.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);//水平滚动条
        this.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//垂直滚动条
        //添加第二层 JPane
        this.setViewportView(LogPanel.instance);
    }//私有化构造方法使得该类无法在外部通过new 进行实例化
    public static LogScrollPanel instance = new LogScrollPanel();//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
}

JSplitPane

//分割配置面板 与 日志面板
public class SplitPanel extends JSplitPane {
    //单例面板类
    private SplitPanel(){
        this.setBorder(null);
        this.setOrientation(JSplitPane.HORIZONTAL_SPLIT);//设置分割线方向 纵向分布
        this.setDividerSize(6);//设置分割线的宽度
        this.setDividerLocation(ScreenSize.getWidth()/2);//设置分割线位于中央
        this.setOneTouchExpandable(false);//设置那个杠杠上的两个黑点显示
        //this.setEnabled(true);//设置分割能不能移动,拖动左右面板
        this.setBackground(Color.WHITE);
    }//私有化构造方法使得该类无法在外部通过new 进行实例化
    public static SplitPanel instance;//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
    //public static 方法,提供给调用者,创建一次
    public static SplitPanel createInstance(){
        if(null==instance){//第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
            instance = new SplitPanel();
        }
        return instance;
    }
}

设置windows界面, 下拉框, 复选框样式

public static void setWindowStyle(){
    try {
        //界面风格
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());//设置为当前系统风格
        //UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");//Windows风格
        //UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel") ; //Mac风格
        //UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel") ;//Java默认风格
        //下拉框, 复选框样式
        SwingUtilities.updateComponentTreeUI(Separator.instance.separatorText);//分隔符
        SwingUtilities.updateComponentTreeUI(TimeFormat.instance.timeFormatText);//时间格式
        SwingUtilities.updateComponentTreeUI(ProcessNum.instance.processNumText);//进程数量
        SwingUtilities.updateComponentTreeUI(ImageDataMode.instance.compositeModeText);//合成图
        SwingUtilities.updateComponentTreeUI(CombinedPicRule.instance.combinedPicTypeText);//合成图类型
        SwingUtilities.updateComponentTreeUI(ImageDataMode.instance.sequenceModeText);//序列图
        SwingUtilities.updateComponentTreeUI(RecordId.instance.checkBoxValue);//RecordId
        SwingUtilities.updateComponentTreeUI(IllegalTime.instance.checkBoxValue);//违法时间
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

设置UI默认字体

public static void setUIFont (javax.swing.plaf.FontUIResource font){
    Enumeration keys = UIManager.getDefaults().keys();
    while (keys.hasMoreElements()) {
        Object key = keys.nextElement();
        Object value = UIManager.get(key);
        if (value instanceof javax.swing.plaf.FontUIResource)
            UIManager.put (key, font);
    }
}

鼠标点击事件

//按钮点击事件绑定
this.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        if (StartButton.instance.isEnabled()) {//程序启动后不允许切换配置页
            AIQualityConfigPanel.instance.setVisible(true);
            AITrafficConfigPanel.instance.setVisible(false);
            AIQualityConfigPanel.instance.addComponents();//添加组件
            //切换颜色/字体
            AIQualityListener.instance.setBackground(ColorClass.color_18a5d6);
            AITrafficListener.instance.setBackground(ColorClass.color_bbbbbb);
            AIQualityListener.instance.setFont(FontClass.boldFont20);
            AITrafficListener.instance.setFont(FontClass.font20);
            //切换业务模式
            BootStrap.business = BusinessConstant.AI_QUALITY_USINESS_MODE;
            //合成图模式显示
            CombinedPicRule.instance.combinedPicTypeText.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
            CombinedPicRule.instance.carNumPicIndexLabel.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
            CombinedPicRule.instance.carNumPicIndex.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
            CombinedPicRule.instance.recogPicIndexLabel.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
            CombinedPicRule.instance.recogPicIndex.setVisible(AIQualityConfigPanel.instance.isVisible() && ImageDataMode.instance.compositeModeText.isSelected());
            //更换推送地址URL
            String url = AccessURL.instance.getUrl();
            url = url.replace(BusinessConstant.AITRAFFIC_ACCESS_URL, BusinessConstant.AIQUALITY_ACCESS_URL);//替换URL中间部分
            AccessURL.instance.accessUrlText.setText(url);
        }
    }
});

鼠标悬停离开事件

//按钮悬停事件绑定MouseAdapter
this.addMouseListener(new MouseAdapter() {
    public void mouseEntered(MouseEvent e) {
        AIQualityListener.instance.setFont(FontClass.boldFont20);
    }

    public void mouseExited(MouseEvent e) {
        if (!AIQualityConfigPanel.instance.isVisible()) {
            AIQualityListener.instance.setFont(FontClass.font20);
        }
    }
});

需要注意的地方

maven 打包成 jar, 运行 jar 程序包, logo 不能正常显示, 是maven 打包时没有把图片打包在内; 可以把图片放在编译后的目录, 然后在程序中通过 getResource() 去获取, 就可以了.

//修改logo
Image icon = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("favicon.ico"));
this.setIconImage(icon);


滚动条

需要嵌套才能实现, 嵌套关系是:

JScrollPane (第一层) --> JPanel (第二层) --> JTextPane(第三层)

JScrollPane 第一层


public class LogScrollPanel extends JScrollPane {
    //单例面板类
    private LogScrollPanel(){
        this.setBackground(Color.WHITE);//背景色
        this.setForeground(Color.ORANGE);//设置字体颜色
        this.setBorder(BorderFactory.createTitledBorder(null, " 日志区 ", TitledBorder.LEFT, TitledBorder.ABOVE_TOP, FontClass.boldFont16, ColorClass.color_18a5d6));
        this.setBounds(ScreenSize.getWidth() / 2, BaseConstant.CONST0, ScreenSize.getWidth() / 2, ScreenSize.getHeight() - BaseConstant.CONST45);
        this.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);//水平滚动条
        this.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//垂直滚动条
        //添加第二层 JPane
        this.setViewportView(LogPanel.instance);
    }//私有化构造方法使得该类无法在外部通过new 进行实例化
    public static LogScrollPanel instance = new LogScrollPanel();//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
}

JPanel 第二层

//JPanel (第二层)
public class LogPanel extends JPanel {
    //单例面板类
    private LogPanel(){
        this.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));//设置为其内容实际的高度
        this.setBackground(Color.BLACK);
        //this.setBounds(ScreenSize.getWidth() / 2, BaseConstant.CONST0, ScreenSize.getWidth() / 2, ScreenSize.getHeight() - BaseConstant.CONST45);
        //添加第三层 JTextPanel
        this.add(LogTextPane.instance);
    }//私有化构造方法使得该类无法在外部通过new 进行实例化
    public static LogPanel instance = new LogPanel();//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
}

JTextPane 第三层 + 添加内容方法(日志记录)

public class LogTextPane extends JTextPane {
    //单例面板类
    private LogTextPane(){
        this.setLayout(null);
        this.setBackground(Color.BLACK);
        this.setFont(FontClass.font14);
        //this.setBounds(BaseConstant.CONST0, BaseConstant.CONST0, ScreenSize.getWidth() / 2, ScreenSize.getHeight());
        this.setEditable(false);//不可编辑
    }//私有化构造方法使得该类无法在外部通过new 进行实例化
    public static LogTextPane instance = new LogTextPane();//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个

    public void debug(String logInfo) {
        Style style = this.getStyledDocument().addStyle(null, null);// 获取组件空样式,addStyle(null, null)会返回一个空样式
        StyleConstants.setForeground(style, Color.GREEN);// 将style的设置颜色
        this.append(logInfo, style);//追加日志
    }

    public void info(String logInfo) {
        Style style = this.getStyledDocument().addStyle(null, null);// 获取组件空样式,addStyle(null, null)会返回一个空样式
        StyleConstants.setForeground(style, Color.WHITE);// 将style的设置颜色
        this.append(logInfo, style);//追加日志
    }

    public void warning(String logInfo) {
        Style style = this.getStyledDocument().addStyle(null, null);// 获取组件空样式,addStyle(null, null)会返回一个空样式
        StyleConstants.setForeground(style, Color.ORANGE);// 将style的设置颜色
        this.append(logInfo, style);//追加日志
    }

    public void error(String logInfo) {
        Style style = this.getStyledDocument().addStyle(null, null);// 获取组件空样式,addStyle(null, null)会返回一个空样式
        StyleConstants.setForeground(style, Color.RED);// 将style的设置颜色
        this.append(logInfo, style);//追加日志
    }
    
    public void append(String logInfo, Style style){
        if (logInfo != null) {
            int contentLenth = this.getStyledDocument().getLength();// 这一句是获取当前面板内容的总长度,
            try {
                // 作为要插入内容的偏移量 this._new.getText()+"
"这一句是获取输入面板内容 style这一句是使用的样式
                this.getStyledDocument().insertString(contentLenth, logInfo + "
", style);
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
            //实现垂直滚动条自动下滑到最低端
            this.setCaretPosition(this.getStyledDocument().getLength());
        }
    }
}

最后给出项目地址: https://github.com/kaichenkai/DataAccessTools
联系方式: 13018083063(微信同号)




完 !
每天都要遇到更好的自己.
原文地址:https://www.cnblogs.com/kaichenkai/p/13329725.html