Java GUI swing 工具包使用总结
前言
最近用 swing 写了一个GUI图片接入的工具, 方便用于将图片数据通过接口推送到 web 项目中
做界面有点像写原生的 CSS
技术储备
-
java
基础知识, 面向对象封装, 继承, 接口, 多态
匿名内部类, Lambda, 线程池
单例模式(懒汉, 饿汉)
-
开发工具
IDEA
功能需求
AI智检
-
支持合成图和序列图
-
工具支持填写数据地址,根据数据地址自动将数据快速导入平台
数据为图片,图片信息在图片名称上面,字段信息已特定的分隔符连接,各个现场不同
-
工具支持填写客户违法编码
如果填写了违法编码,则以填写为准,否则以字段映射关系切割为准
-
工具支持填写合成图模式
-
工具支持自动根据图片名称获取图片及相关信息
-
工具支持自动匹配车牌根据设置规则
如果填写车牌匹配规则,以匹配为准,否则以字段映射关系切割为准
-
工具支持默认数据补充
平台非必须字段,程序指定默认值
-
平台支持离线测试数据,快速看到算法识别指标 (保持智检测试看到的效果)
-
数据导入之后自动创建测试任务,操作人员登录平台人工开启任务
-
任务名称:工具接入_2020-07-04_12:22:30
-
-
支持导入进度查看
- 导入总量及百分比
- 导入成功数量及错误数量
-
支持查看工具执行日志
-
工具为windows exe格式
AI预审
-
支持合成图和序列图
-
工具支持填写数据地址,根据数据地址自动将数据快速导入平台
数据为图片,图片信息在图片名称上面,字段信息已特定的分隔符连接,各个现场不同
-
工具支持自动根据图片名称获取图片及相关信息
-
工具支持默认数据补充
平台非必须字段,程序指定默认值
-
平台支持离线测试数据,快速看到算法识别指标(保持智检测试看到的效果)
- 数据导入之后自动创建测试任务,操作人员登录平台人工开启任务
- 任务名称:工具接入_2020-07-04_12:22:30
-
支持导入进度查看
- 导入总量及百分比
- 导入成功数量及错误数量
-
支持查看工具执行日志
-
工具为windows exe格式
页面设计原型
实际效果
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);
}
}
});
需要注意的地方
关于 Logo
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()+"\n"这一句是获取输入面板内容 style这一句是使用的样式
this.getStyledDocument().insertString(contentLenth, logInfo + "\n", style);
} catch (BadLocationException e) {
e.printStackTrace();
}
//实现垂直滚动条自动下滑到最低端
this.setCaretPosition(this.getStyledDocument().getLength());
}
}
}
最后给出项目地址: https://github.com/kaichenkai/DataAccessTools
联系方式: 13018083063(微信同号)