///-------------------------------------------------------------------------------------------------------------------------------------//
如果有人真的对GUI感兴趣的话emmmm, 我推荐你们去下载SceneBuilder琢磨一下这个,这个针对javafx的,可以加入样式表....emmm,感觉还是挺有意思的。
1.今天看到一个有意思的API,可以插入HTMl, 赞一下..呵呵
你要先写好HTML然后直接复制到代码里,,,,
1 package jffx.blogs.gui; 2 3 import javafx.application.Application; 4 import javafx.scene.Group; 5 import javafx.scene.Scene; 6 import javafx.scene.control.ScrollPane; 7 import javafx.scene.layout.VBox; 8 import javafx.scene.web.WebEngine; 9 import javafx.scene.web.WebView; 10 import javafx.stage.Stage; 11 12 13 public class MenuTest2 extends Application { 14 @Override 15 public void start(Stage primaryStage) { 16 primaryStage.setWidth(300) ; 17 primaryStage.setHeight(400) ; 18 Scene scene = new Scene(new Group()) ; 19 20 VBox vbox = new VBox() ; 21 22 final WebView webView = new WebView() ; 23 final WebEngine webEngine = webView.getEngine() ; 24 25 ScrollPane scrollPane = new ScrollPane() ; 26 scrollPane.setContent(webView) ; 27 webEngine.loadContent("<!DOCTYPE html> " + 28 "<html> " + 29 "<head> " + 30 " <meta charset="utf-8"> " + 31 " <title></title> " + 32 "</head> " + 33 "<body> " + 34 " <!-- ==== 开始页面级页眉 ==== --> " + 35 " <header role="banner"> " + 36 " <!-- 站点标识 --> " + 37 " <nav role="navigation"> " + 38 " <ul> " + 39 " <li><a href="http://www.baidu.com">www.baidu.com</a></li> " + 40 " <li><a href="http://www.google.com.cn">www.google.com</a></li> " + 41 " </ul> " + 42 " </nav> " + 43 " </header> " + 44 " " + 45 " <!-- 开始主要内容 --> " + 46 " <main role="main"> " + 47 " <h1>Arts & Entertainment; Museums</h1> " + 48 " " + 49 " <article> " + 50 " <h2>Gallery</h2> " + 51 " <img src="/home/jff/图片/1.jpeg" width="300" height="175" alt="Blue Flax"> " + 52 " <p>ajishudasssssssssssssssssssssssssssssssssssssssssassssssssssssssssssssss " + 53 " asddddddddddddddddddddddddddddddddddddddddddddddaasdddddddd " + 54 " asdddddddddddddddddddddddddddddddddaasdddddddddddddddd</p> " + 55 " </article> " + 56 " " + 57 " <aside> " + 58 " <h2>Subject</h2> " + 59 " <ul> " + 60 " <li><a href="http://www.baidu.com">baidu</a></li> " + 61 " </ul> " + 62 " </aside> " + 63 " </main> " + 64 " " + 65 " <!-- 开始附注栏 --> " + 66 " <aside> " + 67 " <!-- 次级导航 --> " + 68 " </aside> " + 69 " " + 70 " <footer role="contentinfo"> " + 71 " <p><small>We are young</small></p> " + 72 " </footer> " + 73 "</body> " + 74 "</html>") ; 75 76 vbox.getChildren().add(scrollPane) ; 77 scene.setRoot(vbox) ; 78 79 primaryStage.setScene(scene) ; 80 primaryStage.setTitle("-----") ; 81 primaryStage.show() ; 82 83 } 84 }
这次实现了基本功能,不过呢,对于客户端的代码有点要求,就是有点差强人意...
下面给完整代码
服务器代码:
1 package jffx.blogs.net; 2 3 import java.io.*; 4 import java.net.*; 5 import java.util.*; 6 7 /** 8 * 代码文件: TalkRoomServer.java 9 * 功能描述: 管理服务器与客户端的活动连接 10 */ 11 public class TalkRoomServer { 12 public static void main(String[] args) { 13 try { 14 //服务器端serversocket, 绑定端口(5210), 随意选(1024后的) 15 ServerSocket server = new ServerSocket(5210); 16 17 /** 18 * 容器来保存服务器与客户端的连接, 键为姓名,值为socket 19 */ 20 Map<String, Socket> socketMap = new HashMap<>() ; 21 22 while(true) { 23 //监听客户端的连接, accept的方式是阻塞的 24 try { 25 Socket ss = server.accept(); 26 27 //创建流 28 //采用缓冲流,提高效率 29 DataInputStream in = new DataInputStream( 30 new BufferedInputStream(ss.getInputStream()) 31 ) ; 32 DataOutputStream out = new DataOutputStream( 33 new BufferedOutputStream(ss.getOutputStream()) 34 ) ; 35 36 /** 37 * 在客户端设计时,一个新的用户登录到这个服务器上时,就向服务器发送其姓名 38 */ 39 String name = in.readUTF() ; 40 //获取IP 41 String IP = ss.getInetAddress().toString() ; 42 //显示到服务器 43 System.out.println(name + " : " + IP); 44 45 //查看已监听的客户,并发送新用户登录消息 46 Collection<Socket> values = socketMap.values() ; 47 Iterator<Socket> iter = values.iterator() ; 48 while(iter.hasNext()) { 49 Socket temp = iter.next() ; 50 DataOutputStream write = new DataOutputStream(temp.getOutputStream()) ; 51 System.out.println(temp) ; 52 write.writeUTF("Add:" + name) ; 53 write.flush() ; 54 } 55 //将新用户添加到容器中 56 socketMap.put(name, ss) ; 57 58 59 /** 60 * 向新登录的用户发送都有谁在线 61 */ 62 Set<String> names = socketMap.keySet() ; 63 Iterator<String> iterName = names.iterator() ; 64 while(iterName.hasNext()) { 65 String loginUser = iterName.next(); 66 out.writeUTF("Add:" + loginUser) ; 67 out.flush() ; 68 } 69 70 71 /** 72 * 创建新线程转发用户给服务器发送的消息 73 * 由于需要分 74 * 向某个人发送消息,即:私聊 75 * 向所有发送消息,即:广播吧. 76 * 我们采用修改处理客户端的发送方式: 77 * 在消息的基础上,给前面即消息前缀加上一些表示发送目标的字符串. 78 * 具体看Talking.java的处理方式 79 */ 80 //由于客户有可能下线,所以需要将姓名和容器都传递给线程类 81 new Thread(new Talking(name, ss, socketMap)).start() ; 82 83 } catch (Exception ex) { 84 ex.printStackTrace() ; 85 } 86 } 87 } catch (Exception ex) { 88 ex.printStackTrace() ; 89 } 90 } 91 }
package jffx.blogs.net; import java.io.*; import java.net.*; import java.util.*; /** * 代码文件:Talking.java * 功能描述:线程类转发用户的数据 */ public class Talking implements Runnable { String name ; Socket connecter ; Map<String, Socket> socketMap = null ; public Talking(String name, Socket socket, Map<String, Socket> socketMap) { this.name = name ; this.connecter = socket ; this.socketMap = socketMap ; } @Override public void run() { try { DataInputStream in = new DataInputStream( new BufferedInputStream(connecter.getInputStream()) ) ; while(true) { String words = in.readUTF() ; /** * 我们约定,客户端发送("name@text")这种格式的消息 * 不过用户不需要处理,我们交由客户端程序在发送消息时自动加上 */ String [] tokens = words.split("@") ; String sendName = tokens[0] ; String text = tokens[1] ; if("All".equals(sendName)) { //容器的值为Socket变量 Collection<Socket> sockets = socketMap.values() ; Iterator<Socket> iter = sockets.iterator() ; while(iter.hasNext()) { //创建流.并以固定格式写出 Socket sendSocket = iter.next() ; DataOutputStream out = new DataOutputStream(sendSocket.getOutputStream()) ; out.writeUTF("Text:" + text) ; out.flush() ; } } else { //私聊 Socket sendSocket = socketMap.get(sendName) ; DataOutputStream out = new DataOutputStream(sendSocket.getOutputStream()) ; out.writeUTF("Text:" + text) ; out.flush() ; } } } catch (Exception ex) { ex.printStackTrace() ; } finally { //当登陆的用户退出后,就会跳出while(true) try { /** * 查找登陆的用户,删除服务器与其的连接,并发送给所有的客户端 */ this.socketMap.remove(this.name) ; Collection<Socket> sockets = this.socketMap.values() ; Iterator<Socket> iter = sockets.iterator() ; while(iter.hasNext()) { Socket sender = iter.next() ; DataOutputStream out = new DataOutputStream(sender.getOutputStream()) ; out.writeUTF("Del:" + this.name) ; out.flush() ; } } catch (Exception ex) { ex.printStackTrace() ; } } } }
客户端代码:
package jffx.blogs.net; import javafx.application.Application; import javafx.application.Platform; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.layout.*; import javafx.scene.text.Font; import javafx.scene.text.FontPosture; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.stage.Stage; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; import java.util.Date; //采用切换舞台的方式来切换登录界面和聊天界面 /** * 代码文件: TalkRoomServer.java * 功能描述: 客户端代码 */ public class TalkRoomClient extends Application { /** * 将两个组件放在一个类内,耦合性太高了,不过我还不会解耦和.. */ //=================GUI组件=============================== private TextArea ta = new TextArea(); //聊天记录框 private TextField tf = new TextField(); //信息发送框 private ComboBox<String> userList = new ComboBox<>(); //在线用户下拉列表 //===================网络组件============================ String name ; //用户名称 Socket socket ; //与服务器端连接的Socket DataInputStream in ; //和服务器进行交互的流 DataOutputStream out ; //同上 @Override public void start(Stage primaryStage) { /** * 画登录界面,采用GridPane */ GridPane mainPane = new GridPane() ; //设置面板及布局 mainPane.setAlignment(Pos.CENTER) ; //向中间靠齐 mainPane.setHgap(10) ; //节点的水平间距 mainPane.setVgap(10) ; //节点的垂直间距 mainPane.setPadding(new Insets(5, 5, 5, 5)) ; //文本框 Text text = new Text("Welcome") ; text.setFont(Font.font("CourierNew", FontWeight.BOLD, FontPosture.REGULAR, 23)); mainPane.setId("welcome-text"); mainPane.add(text, 0, 0, 2, 1) ; //标签加上输入的文本域 Label serviceName = new Label("SericeIP: ") ; //如果是在本机的话是127.0.0.1 TextField tfForService = new TextField() ; mainPane.add(serviceName, 0, 1) ; mainPane.add(tfForService, 1, 1); //昵称加上输入的文本域 Label inputName = new Label("Name: ") ; TextField tfForName = new TextField() ; mainPane.add(inputName, 0, 2) ; mainPane.add(tfForName, 1, 2) ; //按钮这行单独处理,用一个Hbox包装一下 Button btForLogin = new Button("Login") ; HBox box = new HBox(10) ; box.getChildren().add(btForLogin) ; box.setAlignment(Pos.BOTTOM_RIGHT) ; //靠向最右边 //然后再加入主面板 mainPane.add(box, 1, 4) ; //显示一下--将面板放入舞台,至于为什么清查阅相关资料,-----h Scene scene = new Scene(mainPane, 300, 200) ; primaryStage.setScene(scene) ; primaryStage.setTitle("Login") ; primaryStage.show() ; /** * 监听按钮事件,以更换舞台 */ //这里用lambda表达式,因为就算你写完整的继承事件接口即 // EventHandle<ActionEvent>处理,也只是处理一个handle方法 //所以还不如写lamda,因为handle()只有一个参数,所以event只是一个标识符 // 代表只有一个参数. btForLogin.setOnAction(event -> { //登陆之后,给成员name初始化 this.name = tfForName.getText() ; String hostName = tfForService.getText() ; /** * 继续画需要切换的聊天界面图 */ ta.setWrapText(true) ; //自动换行 Pane pane = new ChatPane(ta, tf, userList) ; //CharPane在另一个文件 Scene chatScene = new Scene(pane, 550, 400) ; primaryStage.setTitle("Chatting--" + this.name) ; primaryStage.setScene(chatScene) ; primaryStage.show() ; //连接服务器 try { this.socket = new Socket(hostName, 5210) ; //初始化流 in = new DataInputStream(this.socket.getInputStream()) ; out = new DataOutputStream(this.socket.getOutputStream()) ; //向服务器发送用户登录的昵称 out.writeUTF(this.name) ; out.flush() ; } catch (Exception ex) { ex.printStackTrace() ; } //监听文本域的事件 tf.setOnAction(e -> { String words = tf.getText() ; //重新将文本域滞空,显示一种消息发送的感觉 tf.setText(""); if(words.length() == 0) { System.out.println("警告:不允许 消息为空!!") ; } else { //从ComboBox获取要发送的对象 String sendName = userList.getValue() ; try { //区分群聊和私聊 if (sendName.equals("All")) { out.writeUTF(sendName + "@" + words); out.flush() ; } else { ta.appendText(this.name + ":") ; ta.appendText(" " + words + " "); out.writeUTF(sendName + "@" + words) ; out.flush() ; } } catch (Exception ex) { ex.printStackTrace() ; } } }); //发送看完了,就该到接收了,为提高效率,用一个线程处理 new Thread(new GetMessage()).start() ; }) ; } /** * 类类型: 内部类 * 功能描述: 接收服务器发来的数据 */ //解释一下,因为是内部类,所以外部类的私有数据内部类也可以访问,所以不需要接收 //什么数据,当然如果你想写到另一个文件就得加入数据域了 private class GetMessage implements Runnable { @Override public void run() { try { while(true) { //获取服务器发过来的数据 //格式为 Add:name, Text:msg Del:name String text = in.readUTF() ; String [] token = text.split(":") ; String type = token[0] ; //Add.. String msg = token[1] ; //name.. //根据信息类型的不同做不同的处理 if("Add".equals(type)) { System.out.println(msg) ; //如果添加的不是自己,那么将其加入到下拉列表中 //name是客户端的姓名 if(!msg.equals(name)) { userList.getItems().add(msg) ; } //在聊天记录框中显示 //因为是在另一个线程中所以调用 Platform.runLater(() -> { ta.appendText(msg + " " + new Date() + "上线了! "); }); } if("Del".equals(type)) { Platform.runLater(() -> { userList.getItems().remove(msg); ta.appendText(msg + " " + new Date() + "下线了! "); }); } if("Text".equals(type)) { Platform.runLater(() -> { ta.appendText(msg + " ") ; }) ; } } } catch (Exception ex) { ex.printStackTrace() ; } } } }
package jffx.blogs.net; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.ComboBox; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; /** * 将聊天面板从主程序中抠出来, 可以以内部类方式实现,就不需接受这些参数了,更方便些, */ class ChatPane extends VBox { private TextArea ta ; private ComboBox<String> userList ; private TextField tf ; public ChatPane(TextArea ta, TextField tf, ComboBox<String> userList) { this.ta = ta ; this.userList = userList ; this.tf = tf ; super.setSpacing(30); setPadding(new Insets(20, 20, 20, 20)); //设置文本域的属性 this.ta.setPrefColumnCount(70); this.ta.setEditable(false); //不可编辑 this.ta.setWrapText(true); //自动换行 getChildren().add(new ScrollPane(ta)) ; HBox hBox = new HBox(150) ; //加入在线用户及输入文本框 //getItems()方法返回一个选项列表 this.userList.getItems().add("All") ; //默认给所有人 this.userList.setStyle("-fx-color: White") ; this.userList.setValue("All") ; setAlignment(Pos.CENTER) ; this.tf.setPrefColumnCount(30); hBox.getChildren().addAll(this.userList, this.tf) ; getChildren().addAll(ta, hBox) ; } }
//-------------------------我是华丽的分割线-----------------------------