前言
最近用 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(微信同號)