前段时间要求做一个项目,项目比较简单,项目要求是:1、从客户端读取指定目录、格式(.xml)的文件,然后传给服务器;2、做成客户端形式。
client端写好了,server端的代码是从网上借鉴的,后来才做成窗口形式。功能不是很完美,有些bug尚未解决,希望大家多多指正。下面我会把主要代码贴出来给大家看看。
client端的编写过程:
首先是设计界面,界面也比较简洁,一个主窗口、一个功能配置窗口
这个说明一下,这个窗口样式是采用SynthLookAndFeel的方式。用到了开源的beautyeye样式,大家可以在网上搜索下载。
这里有个注意的地方:beautyeye默认的样式看起来相当漂亮了,但是可能不太符合我的要求,所以根据作者的文档修改了下
//隐藏“设置”按钮
UIManager.put("RootPane.setupButtonVisible", false);
//设置本属性将改变窗口边框样式定义
BeautyEyeLNFHelper.frameBorderStyle = BeautyEyeLNFHelper.FrameBorderStyle.translucencySmallShadow;
//BeautyEyeLNFHelper.frameBorderStyle = BeautyEyeLNFHelper.FrameBorderStyle.translucencyAppleLike;//默认样式
//加载Beauty Eye的外观,如果不需要其他设置的话,直接写这句话就OK了,其他都可以不用写
BeautyEyeLNFHelper.launchBeautyEyeLNF();
//关闭窗口在不活动时的半透明效果
BeautyEyeLNFHelper.translucencyAtFrameInactive = false;
上面代码加到布局之前。
还有种方法可以加载,效果跟上面差不多,区别在于上面的判断了系统兼容问题,可以处理更多的系统,如linux。
try {
// 使用配置文件创建窗口皮肤
SynthLookAndFeel synth = new SynthLookAndFeel();
/**
//加载自定义皮肤文件
InputStream is = clazz.getResourceAsStream("window.xml");
if (is == null) {
System.err.println("Unable to find theme configuration");
System.exit(0);
}
synth.load(is, clazz);
*/
//UIManager.put("swing.boldMetal", Boolean.FALSE);//修改皮肤
//UIManager.setLookAndFeel(synth);//自定义风格,需要编写皮肤文件。如上面的window.xml然后加载
//UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");//自带的其他风格
//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默认风格 dowsLookAndFeel");
UIManager.setLookAndFeel("org.jb2011.lnf.beautyeye.BeautyEyeLookAndFeelWin");//BeautyEye风格
this.init();//初始化主窗口
} catch (Exception e) {
e.printStackTrace();
System.exit(0);//报错程序退出
}
自定义皮肤文件window.xml代码,仅供参考,本项目没有使用到,网上的教程比较少,做的比较难看,所以使用现有的beautyeye皮肤样式,如果大家喜欢折腾可以试试:
<?xml version="1.0" encoding="UTF-8"?>
<synth>
<style id="default">
<font name="Aharoni" size="12" />
<!--
<state>
<color value="#FFFFFF" type="BACKGROUND" />
<color value="#FFFFFF" type="TEXT_FOREGROUND" />
</state>
-->
</style>
<bind style="default" type="region" key=".*" />
<style id="border">
<opaque value="true" />
<state>
<color value="#0692fa" type="BACKGROUND" />
<color value="#0692fa" type="TEXT_FOREGROUND" />
<insets top="5" left="5" bottom="5" right="5" />
</state>
</style>
<bind style="border" type="region" key="Border" />
<style id="menubar">
<opaque value="true" />
<state>
<color value="#eeeeee" type="BACKGROUND" />
<color value="#000000" type="TEXT_FOREGROUND" />
</state>
</style>
<bind style="menubar" type="region" key="MenuBar" />
<style id="menuitem">
<opaque value="true" />
<state>
<color value="#eeeeee" type="BACKGROUND" />
<color value="#000000" type="TEXT_FOREGROUND" />
</state>
</style>
<bind style="menuitem" type="region" key="MenuItem" />
<style id="textfield">
<opaque value="true" />
<state>
<color value="#eeeeee" type="BACKGROUND" />
<color value="#000000" type="TEXT_FOREGROUND" />
<insets top="5" left="5" bottom="15" right="5" />
</state>
</style>
<bind style="textfield" type="region" key="Textfield" />
<style id="label">
<opaque value="true" />
<state>
<color value="#eeeeee" type="BACKGROUND" />
<color value="#000000" type="TEXT_FOREGROUND" />
<insets top="5" left="20" bottom="5" right="5" />
</state>
</style>
<bind style="label" type="region" key="JLabel" />
<style id="optionpane">
<opaque value="true" />
<state>
<color value="#eeeeee" type="BACKGROUND" />
<color value="#000000" type="TEXT_FOREGROUND" />
<insets top="5" left="20" bottom="5" right="5" />
</state>
</style>
<bind style="optionpane" type="region" key="JOptionPane" />
<style id="button">
<opaque value="true"></opaque>
<state>
<insets top="5" left="5" bottom="5" right="5" />
<color type="BACKGROUND" value="#0092fe" />
<color type="TEXT_FOREGROUND" value="#FFFFFF" />
</state>
<state value="MOUSE_OVER">
<insets top="5" left="10" bottom="5" right="5" />
<color type="TEXT_FOREGROUND" value="#c0c0c0" />
</state>
<state value="PRESSED">
<insets top="5" left="5" bottom="5" right="5" />
<color type="TEXT_FOREGROUND" value="#c0c0c0" />
</state>
<state value="DISABLED">
<insets top="5" left="5" bottom="5" right="5" />
<color type="TEXT_FOREGROUND" value="#777777" />
</state>
<property key="Button.margin" type="insets" value="5 5 5 5" />
</style>
<bind style="button" type="region" key="Button" />
</synth>
布局类的代码如下:
ImageIcon icon=new ImageIcon(clazz.getResource("icon.jpg"));
mainFrame = new JFrame("设备状态监控配置");
mainFrame.setIconImage(icon.getImage());//设置窗口的icon
mainFrame.setSize(508, 536);
mainFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);//关闭不是直接程序退出,只是界面隐藏
JComponent bar = (JComponent) ((JLayeredPane) mainFrame.getRootPane().getComponents()[1]).getComponent(1);
JButton closeBtn = (JButton) bar.getComponent(1);// 获取关闭按钮
closeBtn.setToolTipText("最小化到托盘");//修改关闭按钮默认的ToolTipText
if (SystemTray.isSupported()) {//判断系统是否支持托盘功能
this.minTray();//最小化到托盘
}
//Toolkit toolkit = Toolkit.getDefaultToolkit();这个我写成了全局变量,这里贴出来给大家看
//屏幕中间显示
Dimension screenSize = toolkit.getScreenSize();
Dimension jfSize = mainFrame.getSize();
int x = screenSize.width / 2 - jfSize.width / 2;
int y = screenSize.height / 2 - jfSize.height / 2;
mainFrame.setLocation(x, y);
mainFrame.setLayout(null);//设置Layout为null
JPanel p1 = new JPanel(new BorderLayout());
Border border = BorderFactory.createTitledBorder(new LineBorder(Color.LIGHT_GRAY), "运行状态", 1, 0, new Font("宋体", Font.PLAIN, 12));//设置边框,可以对比图
JPanel statusPanel = new JPanel();
statusPanel.setLayout(new GridLayout(9, 2, 0, 5));//设置GridLayout布局9横2纵,上下5px的间隔
JLabel jb0 = new JLabel("服务器IP:", JLabel.LEFT);
//...(省略其他)
//初始化label,当显示文本框用
label0 = new JLabel();
//设置边框颜色
label0.setBorder(new LineBorder(Color.LIGHT_GRAY));
//...(省略其他)
//初始化文本框的值
label2.setText("未连接");
label3.setText("未获取");
label4.setText("未启动");
label6.setText("0");
//...(省略其他)
//添加到panelstatusPanel.add(jb0);statusPanel.add(label0);
//...(省略其他)
//初始化按钮
optionsButton = new JButton("配置选项");
runButton = new JButton("启 动");
stopButton = new JButton("暂 停");
aboutButton = new JButton("关 于");
//如果有需要可以修改按钮的默认颜色
//optionsButton.setUI(new BEButtonUI().setNormalColor(BEButtonUI.NormalColor.lightBlue));
//设置按钮的icon
optionsButton.setIcon(new ImageIcon(clazz.getResource("configure.png")));
//...(省略其他)
//设置按钮的大小、鼠标手势
optionsButton.setPreferredSize(new Dimension(90, 30));
optionsButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
//...(省略其他)
//按钮所在的panel
JPanel panel = new JPanel();
//添加按钮
panel.add(optionsButton);
...(省略其他)
//用于显示错误信息的panel
JPanel errerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
errerLabel = new JLabel();//用于显示错误信息的label
//设置字体颜色、长度、高度
//...(省略)
errerPanel.add(errerLabel);//主panel,把之前两个panel加进去
JPanel content=new JPanel(new BorderLayout());
content.add(statusPanel, BorderLayout.CENTER);
content.add(errerPanel, BorderLayout.SOUTH);
content.setBorder(border);
p1.add(content, BorderLayout.CENTER);
p1.add(panel, BorderLayout.SOUTH);
p1.setBounds(0, 0, 498, 500);//设置显示区域
mainFrame.add(p1);
mainFrame.setResizable(false);
mainFrame.setVisible(true);
runButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//...省略其他代码
new Thread() {//new一个线程,这样程序可以继续往下执行,及label2等设置文本能顺利执行
public void run() {
flag = true;sendMessage();// 执行方法,用到socket,会网络阻塞
}
}.start();
label2.setText("正在连接...");
label3.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
label4.setText("运行正常");
}
});
程序设计的时候有一个暂停的功能,思路:通过全局变量来 flag(boolean类型) 控制,当运行的时候给flag赋值为true,暂停的时候为false;看代码:
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//...省略其他代码
label4.setText("暂停运行");
flag = false;
}
});
下面是 sendMessage() 的代码:
try {
String string=null;
while (flag) {
if (socket == null) {
socket=new Socket(LINKIP, LINKPORT);
}
// 输出流,立即刷新
PrintWriter os = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
// 输入流
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
string = this.readSSLXML(path);//从地址为path的地方读取xml文件,使用了XMLConfiguration(commons-configuration-1.9.jar,依赖包commons-lang-2.3.jar以下),可以搜索下载,然后返回string类型的值
Thread.sleep(TIMESPACING);//睡眠指定时间
os.println(string);// 往Server写值
count++;//统计
String msg=null;
try {
if ((msg=is.readLine()) != null) {
successCount++;//统计
//其他处理(省略)
}
} catch (IOException e) {
socket=new Socket(LINKIP, LINKPORT);//异常处理,重新建立连接
}
System.out.println("服务器接收:"+ successCount + "条,未发送:" + errorCount + "条");
}
} catch (Exception e) {
exceptionHandler();//异常处理
}
相当于递归调用,重复20次,设置flag=false 停止运行。并在屏幕右下角弹出提示框,图标闪动,播放警示音提醒用户,跟QQ功能相似。下面是 exceptionHandler() 的代码:
private void exceptionHandler() {
if (trySendMessage >= TRY_COUNT) {// 重试次数
flag = false;
errerLabel.setIcon(new ImageIcon(clazz.getResource("error.png")));
errerLabel.setText("连接失败,请检查服务器状态,网络连接状态,配置文件路径及格式!");
runButton.setEnabled(true);
optionsButton.setEnabled(true);
stopButton.setEnabled(false);
runButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
// 屏幕右下角提示框
TipWindow tw = new TipWindow();
tw.init();
tw.run();
while (!flag) {
try {// 系统托盘闪动
trayIcon.setImage(toolkit.createImage(""));
toolkit.beep();// 警示音
Thread.sleep(400);
Image image = toolkit.createImage(clazz.getResource("icon.jpg"));
trayIcon.setImage(image);
Thread.sleep(400);
} catch (Exception e) {
e.printStackTrace();
}
}
return;
}
trySendMessage++;
System.err.println("连接失败,正在重试第" + trySendMessage + "次...");
sendMessage();//再次调用sendMessage()方法
}
/**
* 屏幕右下角提示框
*
* @author admin
*
*/
class TipWindow {
private Dimension dim;
private int x, y, width=300, height=180;
private Insets screenInsets;
private JDialog jd;
private void init() {
jd = new JDialog(mainFrame, "设备状态读取程序运行异常提示");
jd.setSize(width, height);
dim = toolkit.getScreenSize();
screenInsets = toolkit.getScreenInsets(jd
.getGraphicsConfiguration());
x = (int) (dim.getWidth() - width);
y = (int) (dim.getHeight() - screenInsets.bottom);
// 提示内容
JLabel info=new JLabel("设备状态读取程序运行异常,可能遇到下列问题:",JLabel.LEFT);
info.setFont(new Font("微软雅黑", Font.PLAIN, 12));
JTextArea textArea = new JTextArea("1、网络未连接或中断\n2、服务器未开启或挂起\n3、配置状态文件不存在或格式错误\n4、尝试重新启动程序");
textArea.setEditable(false);
textArea.setLineWrap(true);// 自动换行
textArea.setPreferredSize(new Dimension(250, 120));// 分割组件的宽度
//设置文本颜色、字体
textArea.setForeground(Color.RED);
textArea.setSelectionColor(Color.WHITE);
textArea.setSelectedTextColor(Color.RED);
textArea.setBackground(Color.WHITE);
textArea.setFont(new Font("微软雅黑", Font.PLAIN, 12));
JPanel center=new JPanel();
center.add(info,BorderLayout.NORTH);
center.add(textArea,BorderLayout.CENTER);
JPanel main = new JPanel(new BorderLayout(10,0));
main.add(center, BorderLayout.CENTER);
jd.add(main);
jd.setAlwaysOnTop(true);
jd.setUndecorated(true);
jd.setResizable(false);
jd.setVisible(true);
}
// 显示--渐渐滑入的效果
private void run() {
for (int i = 0; i <= jd.getHeight(); i += 10) {
try {
jd.setLocation(x, y - i);
Thread.sleep(5);
} catch (InterruptedException ex) {
}
}
}
}
程序不希望直接关闭,所以用到了最小化到托盘的功能,这里使用了自定义的JPopupMenu ,但是它不能直接加到TrayIcon(只允许添加PopupMenu)里面,需要处理一下,具体看代码
// 最小到系统托盘
private void minTray() {
try {
systemTray = SystemTray.getSystemTray();//获取系统托盘
// 右击时添加的菜单项
Image openImage = toolkit.createImage(clazz.getResource("house.png"));//图标
openItem = new JMenuItem("显示主窗口", new ImageIcon(openImage));//初始化,使用JMenuItem的好处是可以添加图标,样式更美观
openItem.setCursor(new Cursor(Cursor.HAND_CURSOR));
...(省略其他)
//按钮的功能实现
openItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
mainFrame.setVisible(true);
}
});
...(省略其他)
//退出按钮功能
exitItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
if (confirm())System.exit(0);//程序退出,有个确认功能,也很简单,两个按钮,返回true或者false。true关闭,false忽略
}
});
// 弹出式菜单,即选中右击时弹出的菜单
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.setPreferredSize(new Dimension(120, 165));//设置右键弹出来的菜单大小
popupMenu.add(openItem);
popupMenu.addSeparator();//分割线
popupMenu.add(startItem);
popupMenu.add(stopItem);
popupMenu.addSeparator();
popupMenu.add(aboutItem);
popupMenu.addSeparator();
popupMenu.add(exitItem);
Image image = toolkit.createImage(clazz.getResource("icon.jpg"));
trayIcon = new TrayIcon(image, "设备状态读取程序", null);// 实例化托盘图标
trayIcon.setImageAutoSize(true);
trayIcon.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {//右键
//之前是没有这句话的,第一次点的时候因为popupMenu 还没有初始化,
//因为不知道大小,显示的话就挨到屏幕最底部,而不是从鼠标点击的point开始,所以这样处理了一下
int y = (int) (e.getY() - (popupMenu.getHeight() == 0 ? 165 : popupMenu.getHeight()));
popupMenu.setLocation(e.getX(), y);//设置显示开始的point
popupMenu.setInvoker(popupMenu);//加载菜单
popupMenu.setVisible(true);
}
}
public void mouseClicked(MouseEvent mouse) {
if (mouse.getButton() == MouseEvent.BUTTON1) {//左键
mainFrame.setVisible(true);
}
}
});
systemTray.add(trayIcon);//把图标显示到系统托盘
} catch (Exception ex) {
ex.printStackTrace();
}
}
基本功能就这些,大家可以参考跟指正。
本人的处女作,写的不好望大家见谅!谢谢