利用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>