JAVA Swing开发单机版项目
一、序
最近公司做的项目里出现了一个新的需求,项目大部分是为金融业定制开发的数据集成平台,包括数据的采集,处理,使用。
数据的采集方式不固定,有机构化数据,有非结构话数据,还有附件等其它文件形式。
对于采集端,大部分要求具备硬件服务器架设能力,这时就出现了一个问题,有些采集端是不具备硬件服务器架设能力的,或者主观上不愿意架设,
要求公司拿出一套可以不假设服务器,而是和中心服务器交互。
功能精简为:只保留数据采集,数据结果由中心服务器来提供,客户端安装模式。
二、解决:
我们是JAVA,要开发客户端,那就是AWT、SWING,业内也一直都说是C#更适合开发。
老板一句话,两周之内要产品,没办法,还是用熟悉的SWING吧。遇到了很多问题,中间很坎坷,在这里记录一下:
(1)页面的设计嵌套时,要整个包裹好再放入另一个容器里,这样就可以保证样式
(2)SWING时间控件的选择很少,第三方的时间控件又很难满足具体项目的定制需求
(3)表格的分页,表格单元格显示复选框的感觉很别扭
三、重点:
(1)框架居中:
//第一种居中方式(为空时默认居中) this.setLocationRelativeTo(null); //第二种居中方式(获取屏幕来居中) int width = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth(); int height = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight(); this.setBounds((int)(width/2-500/2), (int)(height/2-350/2), 500, 350); //this.setUndecorated(true); this.setResizable(false);(2)分割框的分割比重
//第一种设置方式,不具有强制性,有可能设置失败 panel_split.setResizeWeight(0.6); //第二种设置方式,可以指定分割框所占的比重 panel_split.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { panel_split.setDividerLocation(1.0 / 4.0); } }); panel_split.setOrientation(JSplitPane.VERTICAL_SPLIT);(3)表格插入图片的时候显示字符串
//其中6、7均为格式需要转换为图片的列位置 table = new JTable(model){ @SuppressWarnings({ "unchecked", "rawtypes" }) public Class getColumnClass(int column) { if (column==6 && model.getDataVector().size()>0) { return getValueAt(0, 6).getClass(); } else if(column==7 && model.getDataVector().size()>0) { return getValueAt(0, 7).getClass(); } else { return getValueAt(0, 0).getClass(); } } }; //图片数据列的插入 row.addElement(new ImageIcon(this.getClass().getClassLoader().getResource("reload.png"))); //普通字串数据列的插入 row.add(data.get("FILE_ID")==null?new String(""):data.get("FILE_ID"));(4)分页对象
//分页对象 package com.dis.view; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; @SuppressWarnings("serial") public abstract class SubPageBar extends JPanel implements ItemListener, ActionListener { private int allCount, fromRec, endRec, pageSize, pageIndex, pageCount; private JLabel allCountLab, curCountLab, pageIndexLab; @SuppressWarnings("rawtypes") private JComboBox eachCom; // 首页 private JButton firstPageButton; // 前一页 private JButton latePageButton; // 下一页 private JButton nextPageButton; // 末页 private JButton lastPageButton; public SubPageBar(int recNums) { this.allCount = recNums; initUI(); updateData(); } @SuppressWarnings({ "unchecked", "rawtypes" }) private void initUI() { // comboBox eachCom = new JComboBox(new String[] { "10", "20", "50" }); // label curCountLab = new JLabel(); allCountLab = new JLabel(); pageIndexLab = new JLabel(); // button firstPageButton = new JButton("首页"); latePageButton = new JButton("上一页"); nextPageButton = new JButton("下一页"); lastPageButton = new JButton("末页"); // listener eachCom.addItemListener(this); firstPageButton.addActionListener(this); nextPageButton.addActionListener(this); latePageButton.addActionListener(this); lastPageButton.addActionListener(this); this.setLayout(new FlowLayout(FlowLayout.RIGHT)); this.add(curCountLab); this.add(allCountLab); this.add(new JLabel("每页")); this.add(eachCom); this.add(new JLabel("条")); this.add(firstPageButton); this.add(latePageButton); this.add(pageIndexLab); this.add(nextPageButton); this.add(lastPageButton); } /** * @Description: (更新分页栏的值) */ private void updateData() { // 必须知道的参数值:allCount pageIndex pageSize pageSize = Integer.valueOf(eachCom.getSelectedItem().toString()); fromRec = pageIndex * pageSize + 1; if (0 == allCount) { fromRec = 0; } endRec = (pageIndex + 1) * pageSize; if (endRec > allCount) { endRec = allCount; } pageSize = (0 == pageSize) ? 1 : pageSize; pageCount = allCount / pageSize - ((0 == allCount % pageSize && 0 != allCount) ? 1 : 0); // curCountLab.setText("第 " + fromRec + "~" + endRec + " 条"); allCountLab.setText("【共有 " + allCount + " 条】 "); pageIndexLab.setText(" 【第 " + (pageIndex) + "/" + (pageCount + 1) + " 页】 "); firstPageButton.setEnabled(pageIndex > 0); latePageButton.setEnabled(pageCount > 0 && pageIndex > 0); nextPageButton.setEnabled(pageIndex < pageCount); lastPageButton.setEnabled(pageCount > 0 && pageIndex < pageCount); } /** * @Description: 有新数据载入时,需要重载 */ public void fresh(int recNums) { this.allCount = recNums; updateData(); } @Override public void itemStateChanged(ItemEvent e) { // 这边之所以要加上这个判断,是因为“选中”和“取消选中”都会触发ItemEvent. if (e.getStateChange() == ItemEvent.SELECTED) { pageIndex = 0; pageSize = Integer.valueOf(eachCom.getSelectedItem().toString()); updateData(); onPageSizeChange(pageSize); } } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == firstPageButton) { pageIndex = 1; onClickPreButton(pageIndex); } else if (e.getSource() == latePageButton) { pageIndex--; onClickPreButton(pageIndex); } else if (e.getSource() == nextPageButton) { pageIndex++; onClickNextButton(pageIndex); } else if (e.getSource() == lastPageButton) { pageIndex = pageCount; onClickNextButton(pageIndex); } updateData(); } public int getPageIndex() { return (pageIndex>0) ? pageIndex : 1; } public void updatePageIndex(int pageIndex) { this.pageIndex = pageIndex; updateData(); } public int getPageSize() { return pageSize; } public abstract void onPageSizeChange(int newPageSize); public abstract void onClickPreButton(int pageIndex); public abstract void onClickNextButton(int pageIndex); } //分页对象集成 table_col_name = new Vector<>(); table_col_name.add("编号"); table_col_name.add("文件名"); table_col_name.add("机构名"); table_col_name.add("流程名"); table_col_name.add("当前状态"); table_col_name.add("变更时间"); table_col_name.add("操作"); table_col_name.add("重置"); Vector<Vector<Object>> rowData = new Vector<Vector<Object>>(); DefaultTableModel model = new DefaultTableModel(rowData, table_col_name); table = new JTable(model){ @SuppressWarnings({ "unchecked", "rawtypes" }) public Class getColumnClass(int column) { if (column==6 && model.getDataVector().size()>0) { return getValueAt(0, 6).getClass(); } else if(column==7 && model.getDataVector().size()>0) { return getValueAt(0, 7).getClass(); } else { return getValueAt(0, 0).getClass(); } } }; table.getTableHeader().setReorderingAllowed(false);// 限制整列拖动 table.setEnabled(false); table.addMouseListener(tableListener); table_bar = new SubPageBar(0) { @Override public void onPageSizeChange(int newPageSize) { initTable(); } @Override public void onClickPreButton(int pageIndex) { this.updatePageIndex(pageIndex--); initTable(); } @Override public void onClickNextButton(int pageIndex) { this.updatePageIndex(pageIndex++); initTable(); } }; JPanel panel_table = new JPanel(new BorderLayout()); JScrollPane scrollPanel_table = new JScrollPane(); scrollPanel_table.setBorder(border); scrollPanel_table.setViewportView(table); panel_table.add(scrollPanel_table, BorderLayout.CENTER); panel_table.add(table_bar, BorderLayout.SOUTH); scrollPanel_bottom.setViewportView(panel_table); //表格数据加载 //初始化表格 @SuppressWarnings("unchecked") public void initTable() { HashMap<String,String> params = this.getParams(); params.put("page", table_bar.getPageIndex()+""); params.put("rows", table_bar.getPageSize()+""); if (params.get("cycleType")=="-1") { params.remove("cycleType"); } try { //请求数据 // String resultStr = HttpRequest.sendGetRequest(PropertyUtil.readKeyValue(PropertyUtil.CONFIG_FILE_PATH, PropertyUtil.API_GETALLFILE), params,"UTF-8"); String resultStr = HttpRequest.sendGetRequest(Sysconfig.getSysInstance().getProperty(PropertyUtil.API_GETALLFILE), params,"UTF-8"); Page resultPage = GsonUtil.GsonToBean(resultStr, Page.class); //解析数据 int totalCount = resultPage.getTotal(); List<Map<String,String>> fileList = (List<Map<String, String>>) resultPage.getRows(); //渲染表格 table_bar.fresh(totalCount); DefaultTableModel model = (DefaultTableModel) table.getModel(); model.getDataVector().clear(); Vector<Vector<Object>> vData = model.getDataVector(); if(null != fileList && fileList.size() > 0 && totalCount > 0) { Page page = null; page = new Page(totalCount); page.setPageSize(table_bar.getPageSize()); page.setPageNow(pageNow); for (Map<String, String> data : fileList) { Vector<Object> row = new Vector<Object>(); row.add(data.get("FILE_ID")==null?new String(""):data.get("FILE_ID")); row.add(data.get("FILE_NAME")==null?new String(""):data.get("FILE_NAME")); row.add(data.get("BANK_NAME")==null?new String(""):data.get("BANK_NAME")); row.add(data.get("PROCESS_NAME")==null?new String(""):data.get("PROCESS_NAME")); row.add(data.get("NODE_DESC")==null?new String(""):data.get("NODE_DESC")); row.add(data.get("UPDATE_TIME")==null?new String(""): this.parseDate(data.get("UPDATE_TIME"))); row.addElement(new ImageIcon(this.getClass().getClassLoader().getResource("look.png"))); row.addElement(new ImageIcon(this.getClass().getClassLoader().getResource("reload.png"))); row.add(data.get("F_PNODE_STATE")==null?new String(""):data.get("F_PNODE_STATE")); vData.add(row); } } table.getColumnModel().getColumn(7).setCellEditor(new DefaultCellEditor(new JCheckBox())); model.fireTableCellUpdated(0, 7); model.fireTableDataChanged(); } catch (Exception e) { JOptionPane.showMessageDialog(this, "服务地址解析错误!"); e.printStackTrace(); log.error("服务地址解析错误!"); } }
我从不相信什么懒洋洋的自由,
我向往的自由是通过勤奋和努力实现更广阔的人生,那样的自由才是珍贵的、有价值的。
我相信一万小时定律,我从来不相信天上掉馅饼的灵感和坐等的成就。
做一个自由又自律的人,靠势必实现的决心认真地活着。
我向往的自由是通过勤奋和努力实现更广阔的人生,那样的自由才是珍贵的、有价值的。
我相信一万小时定律,我从来不相信天上掉馅饼的灵感和坐等的成就。
做一个自由又自律的人,靠势必实现的决心认真地活着。
[山本耀司]
本文转载请注明出处
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具