我想把QQ上的功能都在自己的聊天室上实现,不出意外,会一直更新。
目标:通过点击窗口抖动的按钮,实现给对方发送窗口抖动的效果。
个人想法:点击窗口抖动的时候,发送一段特殊字符,让其他聊天室的线程的socket接收到。将接收到的字符串进行一个判断,看是否含有点击窗口抖动后发送的字符串,如果有,则开始抖动。
抖动的定义及实现:抖动实际上就是聊天室的位置在屏幕中发生了偏移。因此可以通过改变stage的位置,也就是通过setX()或者setY()这个方法去改变聊天是的位置。从而实现窗口抖动的功能。
添加btn_shake按钮:
1 Button btn_shake = new Button("窗口抖动"); 2 btn_shake.setOnAction(this::btnShake); 3 /*btn_shake的位置*/ 4 AnchorPane.setTopAnchor(btn_shake, 224.0); 5 AnchorPane.setLeftAnchor(btn_shake, 220.0);
btnShake()方法实现:
1 public void btnShake(ActionEvent enent) { 2 try { 3 String msg = "shake"; 4 DatagramPacket p = new DatagramPacket(msg.getBytes(), msg.getBytes().length, ia, 6760); 5 socket.send(p); 6 } catch (IOException e) { 7 // TODO 自动生成的 catch 块 8 e.printStackTrace(); 9 } 10 }
线程监听判断是否要抖动:
1 if (str.contains("shake")) { 2 try { 3 FX_UDP.s1.setX(50); 4 Thread.sleep(50); 5 // FX_UDP.s1.centerOnScreen(); 6 FX_UDP.s1.setX(450); 7 } catch (InterruptedException e) { 8 // TODO 自动生成的 catch 块 9 e.printStackTrace(); 10 } 11 }
完整代码如下:
1 package 测试; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.io.OutputStream; 9 import java.net.DatagramPacket; 10 import java.net.InetAddress; 11 import java.net.MulticastSocket; 12 import java.text.SimpleDateFormat; 13 import java.util.Date; 14 import java.util.Optional; 15 16 import javafx.application.Application; 17 import javafx.event.ActionEvent; 18 import javafx.scene.Scene; 19 import javafx.scene.control.Alert; 20 import javafx.scene.control.Button; 21 import javafx.scene.control.ButtonType; 22 import javafx.scene.control.Label; 23 import javafx.scene.control.ScrollPane; 24 import javafx.scene.control.TextArea; 25 import javafx.scene.control.TextField; 26 import javafx.scene.control.TitledPane; 27 import javafx.scene.image.Image; 28 import javafx.scene.image.ImageView; 29 import javafx.scene.layout.AnchorPane; 30 import javafx.scene.layout.BorderPane; 31 import javafx.scene.layout.VBox; 32 import javafx.scene.text.Font; 33 import javafx.stage.FileChooser; 34 import javafx.stage.Stage; 35 import sun.misc.BASE64Decoder; 36 import sun.misc.BASE64Encoder; 37 38 public class FX_UDP extends Application { 39 static InetAddress ia; 40 static MulticastSocket socket, socket2, socket1;// socket(6760),聊天 41 // 42 // socket2(6880) 图片流 43 static TextArea ta_1, ta_2; 44 static TextField tf_name; 45 static Stage s1; 46 47 public static void main(String[] args) { 48 try { 49 ia = InetAddress.getByName("228.9.6.7"); 50 socket = new MulticastSocket(6760); 51 socket.joinGroup(ia); 52 new Private_recive().start(); 53 new Image_recive().start(); 54 } catch (IOException e1) { 55 System.out.println("组播地址绑定失败"); 56 } 57 Application.launch(); 58 } 59 60 @Override 61 public void start(Stage s) throws Exception { 62 s1 = s; 63 Label lb_net = new Label("网名:"); 64 lb_net.setFont(Font.font(17));// 设置字体大小 65 tf_name = new TextField("JDR"); 66 67 /* 消息记录 */ 68 ta_1 = new TextArea(); 69 ta_1.setPrefSize(400, 200); 70 ScrollPane pane_info = new ScrollPane(ta_1); 71 TitledPane pane_01 = new TitledPane("消息记录", pane_info); 72 /* 发送窗口 */ 73 ta_2 = new TextArea(); 74 ta_2.setPrefSize(400, 100); 75 ScrollPane pane_send = new ScrollPane(ta_2); 76 TitledPane pane_02 = new TitledPane("发送窗口", pane_send); 77 /* 发送和按钮事件 */ 78 Button btn_send = new Button("发送"); 79 btn_send.setOnAction(this::btnSend); 80 /* 关闭和按钮事件 */ 81 Button btn_close = new Button("关闭"); 82 btn_close.setOnAction(this::btnClose); 83 /* 发送图片和按钮事件 */ 84 Button btn_img = new Button("选择图片"); 85 btn_img.setOnAction(this::btnImg); 86 Button btn_preview = new Button("预览图片"); 87 btn_preview.setOnAction(this::btnPreview); 88 /* 窗口抖动 */ 89 Button btn_shake = new Button("窗口抖动"); 90 btn_shake.setOnAction(this::btnShake); 91 /* 按钮大小 */ 92 btn_send.setPrefSize(70, 30); 93 btn_close.setPrefSize(70, 30); 94 /* 按钮的位置 */ 95 AnchorPane.setTopAnchor(btn_close, 350.0); 96 AnchorPane.setLeftAnchor(btn_close, 0.0); 97 AnchorPane.setTopAnchor(btn_send, 350.0); 98 AnchorPane.setLeftAnchor(btn_send, 330.0); 99 AnchorPane.setTopAnchor(btn_img, 224.0); 100 AnchorPane.setLeftAnchor(btn_img, 100.0); 101 AnchorPane.setTopAnchor(btn_preview, 224.0); 102 AnchorPane.setLeftAnchor(btn_preview, 160.0); 103 AnchorPane.setTopAnchor(btn_shake, 224.0); 104 AnchorPane.setLeftAnchor(btn_shake, 220.0); 105 106 AnchorPane.setTopAnchor(lb_net, 20.0); 107 AnchorPane.setLeftAnchor(lb_net, 420.0); 108 AnchorPane.setTopAnchor(tf_name, 20.0); 109 AnchorPane.setLeftAnchor(tf_name, 470.0); 110 111 VBox vb = new VBox(pane_01, pane_02); 112 113 AnchorPane apane = new AnchorPane(vb, btn_send, btn_close, btn_img, btn_preview, btn_shake, lb_net, tf_name); 114 apane.setStyle("-fx-background-color:#FFE4C4");// 设置背景色 115 Scene scene = new Scene(apane); 116 117 s.setOnCloseRequest(event -> { 118 Alert alert = new Alert(Alert.AlertType.CONFIRMATION); 119 alert.setTitle("退出"); 120 alert.setHeaderText("你是否要退出?"); 121 Optional<ButtonType> result = alert.showAndWait(); 122 if (result.get() == ButtonType.OK) { 123 try { 124 socket.leaveGroup(ia); 125 System.exit(0); 126 } catch (IOException e) { 127 e.printStackTrace(); 128 } 129 } else { 130 event.consume(); 131 } 132 }); 133 s.setTitle("JDR的午夜群聊"); 134 s.setScene(scene); 135 s.setHeight(450); 136 s.setWidth(650); 137 s.show(); 138 } 139 140 /* 窗口抖动 */ 141 public void btnShake(ActionEvent enent) { 142 143 try { 144 String msg = "shake"; 145 DatagramPacket p = new DatagramPacket(msg.getBytes(), msg.getBytes().length, ia, 6760); 146 socket.send(p); 147 } catch (IOException e) { 148 // TODO 自动生成的 catch 块 149 e.printStackTrace(); 150 } 151 } 152 153 /* 发送按钮事件 */ 154 public void btnSend(ActionEvent enent) { 155 try { 156 String msg = ta_2.getText(); 157 DatagramPacket p = new DatagramPacket(msg.getBytes(), msg.getBytes().length, ia, 6760); 158 socket.send(p); 159 } catch (IOException e) { 160 // TODO 自动生成的 catch 块 161 e.printStackTrace(); 162 } 163 } 164 165 /* 关闭按钮事件 */ 166 public void btnClose(ActionEvent event) { 167 Alert alert = new Alert(Alert.AlertType.CONFIRMATION); 168 alert.setTitle("退出"); 169 alert.setHeaderText("你是否要退出"); 170 Optional<ButtonType> result = alert.showAndWait(); 171 if (result.get() == ButtonType.OK) { 172 System.exit(0); 173 } else { 174 event.consume(); 175 } 176 } 177 178 /* 发送图片按钮事件 */ 179 public void btnImg(ActionEvent event) { 180 FileChooser fileChooser = new FileChooser(); 181 fileChooser.setTitle("选择你要查看的文件"); 182 File file = fileChooser.showOpenDialog(null); 183 if (file != null) { 184 InputStream in = null; 185 byte data[] = new byte[5]; 186 // 读取图片字节数组 187 try { 188 in = new FileInputStream(file); 189 data = new byte[in.available()]; 190 in.read(data); 191 in.close(); 192 } catch (IOException e) { 193 e.printStackTrace(); 194 } 195 196 try { 197 /* 发送消息,告诉别人你发了图片 */ 198 String str = tf_name.getText() + "说:我给你发了张图片"; 199 DatagramPacket p1 = new DatagramPacket(str.getBytes(), str.getBytes().length, ia, 6760); 200 socket.send(p1); 201 // 对字节数组Base64编码 202 BASE64Encoder encoder = new BASE64Encoder(); 203 socket2 = new MulticastSocket(6770); 204 DatagramPacket p2 = new DatagramPacket(encoder.encode(data).getBytes(), 205 encoder.encode(data).getBytes().length, ia, 6770); 206 socket2.joinGroup(ia); 207 socket2.send(p2); 208 } catch (IOException e) { 209 e.printStackTrace(); 210 } 211 } 212 } 213 214 public void btnPreview(ActionEvent event) { 215 FileChooser fileChooser = new FileChooser(); 216 fileChooser.setTitle("选择你要查看的文件"); 217 File file = fileChooser.showOpenDialog(null); 218 219 if (file != null) { 220 Stage stage = new Stage(); 221 BorderPane root = new BorderPane(); 222 String path = file.getPath().toString(); 223 String str = path.replaceAll("\\\\", "\\\\\\\\"); 224 Image image = new Image("file:" + str); 225 ImageView iv = new ImageView(); 226 iv.setImage(image); 227 root.setCenter(iv); 228 Scene scene = new Scene(root); 229 stage.setScene(scene); 230 stage.setWidth(400); 231 stage.setHeight(400); 232 stage.show(); 233 } 234 } 235 } 236 237 class Image_recive extends Thread { 238 public void run() { 239 byte[] buf = new byte[5]; 240 DatagramPacket recv = new DatagramPacket(buf, buf.length); 241 while (true) { 242 BASE64Decoder decoder = new BASE64Decoder(); 243 try { 244 FX_UDP.socket2.receive(recv); 245 String str = new String(recv.getData(), 0, recv.getLength()); 246 // Base64解码 247 byte[] b = decoder.decodeBuffer(str); 248 for (int i = 0; i < b.length; ++i) { 249 if (b[i] < 0) {// 调整异常数据 250 b[i] += 256; 251 } 252 } 253 // 生成png图片 254 Date day = new Date(); 255 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); 256 String name = df.format(day); 257 OutputStream out = new FileOutputStream("D:/(" + name + ").jpg"); 258 out.write(b); 259 out.flush(); 260 out.close(); 261 } catch (Exception e) { 262 } 263 } 264 } 265 } 266 267 class Private_recive extends Thread { 268 public void run() { 269 byte[] buf = new byte[100]; 270 DatagramPacket recv = new DatagramPacket(buf, buf.length); 271 Date day = new Date(); 272 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 273 try { 274 while (true) { 275 FX_UDP.socket.receive(recv); 276 String str = new String(recv.getData(), 0, recv.getLength()); 277 if (str.contains("shake")) { 278 try { 279 FX_UDP.s1.setX(50); 280 Thread.sleep(50); 281 // FX_UDP.s1.centerOnScreen(); 282 FX_UDP.s1.setX(450); 283 } catch (InterruptedException e) { 284 // TODO 自动生成的 catch 块 285 e.printStackTrace(); 286 } 287 } else { 288 FX_UDP.ta_1.appendText(df.format(day) + "\n"); 289 FX_UDP.ta_1.appendText(str + "\n"); 290 } 291 } 292 } catch (IOException e1) { 293 System.out.println("接受失败"); 294 } 295 } 296 }
实验验证:
实现了窗口抖动,但是本人不会弄动态图。卑微了。
唯一缺陷:FX_UDP.s1.centerOnScreen();使用这个将stage居中会报异常。