利用Vaadin,我们可以轻松的开发出丰富的web界面,像写Swing一样写GUI,感觉什么extjs之类的弱爆了!下面是我做的一个网络聊天室的例子,
浏览器要求支持WebSocket。我这里用的Chrome 41,IE11,服务器使用jetty7
Vaadin的运行库,请到官网下载。https://vaadin.com/home
不说了,我直接贴代码。附件在群里面,QQ群号:185441009
运行效果图。
项目结构,不会建项目的请看我之前的教程。
package com.chat.main;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.data.validator.StringLengthValidator;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.StreamResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.*;
/**登陆类
* Created by Juyan on 2015/4/28.
*/
@Theme("chat")
@Push
public class Login extends UI {
TextField usernameField, checkCodeField;
Window loginWindow;
Button button;
Image image;
StreamResource.StreamSource imageSource;
StreamResource resource;
FieldGroup fieldGroup;
public void init(VaadinRequest request) {
loginWindow = new Window(" 聊天室登陆");
loginWindow.setResizable(false);
loginWindow.center();
loginWindow.setModal(true);
loginWindow.setIcon(FontAwesome.USER);
loginWindow.addCloseListener(closeEvent -> {
JavaScript.eval("close()");
getUI().close();
});
FormLayout formLayout = new FormLayout();
formLayout.setSizeUndefined();
formLayout.setMargin(true);
formLayout.addStyleName("login-formlayout");
usernameField = new TextField("请输入昵称:");
button = new Button("登 陆");
button.addClickListener(clickEvent -> {
try {
fieldGroup.commit();
if (!checkCodeField.getValue().equals(VaadinSession.getCurrent().getAttribute("verifyCode").toString())) {
Notification.show("验证码错误!", Notification.Type.ERROR_MESSAGE);
changeChcekCode(request);
} else {
ChatWindow centerWindow = new ChatWindow(getUI(), request, loginWindow, usernameField.getValue());
loginWindow.setVisible(false);
getCurrent().addWindow(centerWindow);
}
} catch (FieldGroup.CommitException e) {
e.printStackTrace();
}
});
usernameField.addValidator(new StringLengthValidator("\"{0}\" 不是一个有效字符,昵称由2~10个字符组成。", 2, 10, false));
checkCodeField = new TextField("验证码:");
checkCodeField.addValidator(new StringLengthValidator("\"{0}\" 不是一个有效字符,验证码由4位字符组成。", 1, 4, false));
usernameField.focus();
image = new Image("", getChcekCodeSource(request));
image.setDescription("点击刷新");
image.setStyleName("v-check-code");
image.addClickListener(clickEvent -> changeChcekCode(request));
image.setErrorHandler(errorEvent -> errorEvent.getThrowable().printStackTrace());
formLayout.addComponent(usernameField);
formLayout.addComponent(checkCodeField);
formLayout.addComponent(image);
formLayout.addComponent(button);
PropertysetItem propertysetItem = new PropertysetItem();
propertysetItem.addItemProperty("username", new ObjectProperty<String>(""));
propertysetItem.addItemProperty("checkCode", new ObjectProperty<String>(""));
fieldGroup = new FieldGroup(propertysetItem);
fieldGroup.bind(usernameField, "username");
fieldGroup.bind(checkCodeField, "checkCode");
loginWindow.setContent(formLayout);
addWindow(loginWindow);
}
void changeChcekCode(VaadinRequest request) {
image.setSource(getChcekCodeSource(request));
}
StreamResource getChcekCodeSource(VaadinRequest request) {
imageSource = new CheckCodeImageSource(request);
resource = new StreamResource(imageSource, System.currentTimeMillis() + ".png");
resource.setCacheTime(0);
return resource;
}
}
package com.chat.main;
import com.chat.server.Broadcaster;
import com.vaadin.event.ShortcutAction;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.*;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalTime;
import java.util.Date;
/** 聊天主窗口
* Created by Juyan on 2015/5/4.
*/
public class ChatWindow extends Window implements Broadcaster.BroadcastListener {
VerticalLayout root, messageLayout, receiverLayout, inputLayout;
Panel showMessagePanel;
VerticalSplitPanel splitPanel;
Button sendButton;
Window perantWindow;
UI ui;
String username;
public ChatWindow(UI ui, VaadinRequest request, Window perant, String username) {
this.ui = ui;
this.perantWindow = perant;
this.username = username;
Broadcaster.register(this, request);
initUI();
initMessage();
}
void initUI() {
addCloseListener(closeEvent -> {
Broadcaster.unregister(this);
perantWindow.setVisible(true);
});
setCaption(" 欢迎进入聊天室");
setWidth("80%");
setHeight("80%");
setIcon(FontAwesome.USERS);
setResizable(false);
center();
root = new VerticalLayout();
root.setSizeFull();
splitPanel = new VerticalSplitPanel();
splitPanel.setSizeFull();
messageLayout = new VerticalLayout();
messageLayout.setMargin(true);
messageLayout.setSizeFull();
showMessagePanel = new Panel();
showMessagePanel.setHeight("90%");
messageLayout.addComponent(showMessagePanel);
receiverLayout = new VerticalLayout();
receiverLayout.setMargin(true);
receiverLayout.setSpacing(true);
receiverLayout.addStyleName("chat-spacing");
showMessagePanel.setContent(receiverLayout);
splitPanel.setFirstComponent(messageLayout);
inputLayout = new VerticalLayout();
inputLayout.setMargin(true);
inputLayout.setSizeFull();
TextArea textArea = new TextArea();
textArea.setHeight("70%");
textArea.setWidth("100%");
sendButton = new Button("发 送");
sendButton.setClickShortcut(ShortcutAction.ModifierKey.CTRL, ShortcutAction.KeyCode.ENTER);
sendButton.addClickListener(clickEvent -> {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String timeNow = simpleDateFormat.format(new Date());
Broadcaster.broadcast("<font color=\"green\" style=\" font-weight:bold\">" + username + " " + timeNow + "</font></br>" + textArea.getValue());
textArea.setValue("");
textArea.focus();
});
inputLayout.addComponent(textArea);
inputLayout.addComponent(sendButton);
inputLayout.setComponentAlignment(sendButton, Alignment.TOP_RIGHT);
splitPanel.setSecondComponent(inputLayout);
splitPanel.setSplitPosition(75, Unit.PERCENTAGE);
root.addComponent(splitPanel);
setContent(root);
textArea.focus();
}
void initMessage() {
Broadcaster.broadcast("<font color=\"red\">【系统】欢迎 " + username + " 进入聊天室...</font");
}
void setMessage(String message) {
receiverLayout.addComponent(new Label(message, ContentMode.HTML));
showMessagePanel.setScrollTop(Integer.MAX_VALUE);
}
@Override
public void receiveBroadcast(String message) {
ui.access(() -> setMessage(message));
}
}
package com.chat.main;
import com.vaadin.server.StreamResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinSession;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Random;
/**验证码生成类
* Created by Juyan on 2015/5/3.
*/
public class CheckCodeImageSource implements StreamResource.StreamSource {
ByteArrayOutputStream byteArrayOutputStream;
VaadinRequest request;
public CheckCodeImageSource(VaadinRequest request) {
this.request = request;
}
@Override
public InputStream getStream() {
// 在内存中创建图象
int width = 70, height = 23;
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();
// 生成随机类
Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
// 设定字体
g.setFont(new Font("Times New Roman", Font.PLAIN, 24));
// 画边框
g.setColor(getRandColor(160, 200));
g.drawRect(0, 0, width - 1, height - 1);
// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
// 取随机产生的认证码(4位数字)
String sRand = "";
for (int i = 0; i < 4; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
// 将认证码显示到图象中
g.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.drawString(rand, 12 * i + 13, 20);
}
// 将认证码存入SESSION
VaadinSession.getCurrent().setAttribute("verifyCode", sRand);
// 图象生效
g.dispose();
byteArrayOutputStream = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", byteArrayOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
return new ByteArrayInputStream(
byteArrayOutputStream.toByteArray());
}
private Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色
Random random = new Random();
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
}
package com.chat.server;
import com.vaadin.server.VaadinRequest;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**广播类
* Created by Juyan on 2015/5/5.
*/
public class Broadcaster implements Serializable {
static ExecutorService executorService =
Executors.newSingleThreadExecutor();
public interface BroadcastListener {
void receiveBroadcast(String message);
}
private static LinkedList<BroadcastListener> listeners =
new LinkedList<BroadcastListener>();
public static synchronized void register(
BroadcastListener listener, VaadinRequest request) {
listeners.add(listener);
System.out.println("用户" + request.getRemoteHost() + "连接成功...");
}
public static synchronized void unregister(
BroadcastListener listener) {
listeners.remove(listener);
System.out.println("用户退出...");
}
public static synchronized void broadcast(
final String message) {
for (final BroadcastListener listener : listeners)
executorService.execute(() -> listener.receiveBroadcast(message));
}
}
chat.scss
@import "../valo/valo.scss";
@mixin chat {
@include valo;
}
.v-check-code {
cursor: pointer;
}
.login-formlayout {
margin: 20px;
}
.center-formlayout {
margin: 200px;
}
.chat-spacing > .v-spacing {
height: 20px;
}
.v-splitpanel-vsplitter {
height: 5px !important;
}
styles.scss
@import "chat.scss";
@include chat;
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<welcome-file-list>
<welcome-file>/login/s.url</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>VaadinApplicationServlet</servlet-name>
<servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
<init-param>
<param-name>UI</param-name>
<param-value>com.chat.main.Login</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>VaadinApplicationServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>