เขียนก่อน:
สุดสัปดาห์ที่ผ่านมาฉันใช้เวลาพอสมควรในการบันทึกการออกแบบเบื้องต้นและการออกแบบรายละเอียดด้านเซิร์ฟเวอร์ของโปรแกรมแชทซ็อกเก็ตอย่างง่ายที่ฉันเขียน ในที่สุดฉันก็รอใบรับรองการสอบนุ่ม ๆ ที่ฉันได้รับก่อนสำเร็จการศึกษาในวันอังคารและจากนั้นฉันใช้เวลาทำงานล่วงเวลา วันนี้เป็นวันศุกร์และฉันวางแผนที่จะบันทึกการออกแบบโดยละเอียดของลูกค้าและโมดูลทั่วไปเพราะฉันจะยุ่งกับสิ่งอื่น ๆ ที่เริ่มต้นสุดสัปดาห์นี้
ออกแบบ:
การออกแบบไคลเอนต์ส่วนใหญ่แบ่งออกเป็นสองส่วนคือการออกแบบโมดูลการสื่อสารซ็อกเก็ตและการออกแบบที่เกี่ยวข้องกับ UI
การออกแบบการสื่อสารซ็อกเก็ตไคลเอนต์:
การออกแบบที่นี่คล้ายกับการออกแบบของเซิร์ฟเวอร์ ความแตกต่างคือเซิร์ฟเวอร์ได้รับแพ็กเก็ต HeartBeat ในขณะที่ไคลเอนต์ส่งแพ็กเก็ต HeartBeat เนื่องจากไคลเอ็นต์เท่านั้นที่สื่อสารกับเซิร์ฟเวอร์เดียว (การสื่อสารระหว่างไคลเอนต์จะถูกแจกจ่ายโดยเซิร์ฟเวอร์) มีเพียงพูลเธรดที่มีขนาด 2 เท่านั้นที่ใช้ในการจัดการสองสิ่งนี้ (NewFixedThreadPool (2)) คลาสการประมวลผลที่สอดคล้องกันคือผู้รับและ keepalivedog เมื่อ receiveListener เริ่มต้นการโทรกลับจะถูกส่งเป็นการโทรกลับไปยังไคลเอนต์จะได้รับข้อความเซิร์ฟเวอร์ การใช้งานเริ่มต้นของการโทรกลับเป็นค่าเริ่มต้น defaultCallback ถูกแจกจ่ายให้กับตัวจัดการที่แตกต่างกันผ่าน HF ตามเหตุการณ์ที่แตกต่างกัน ผู้จัดเก็บข้อมูลลูกค้าปัจจุบัน การออกแบบมีดังนี้:
การใช้งานเฉพาะของโมดูลการสื่อสารซ็อกเก็ต:
[client.java]
ไคลเอนต์คือทางเข้าสู่ไคลเอนต์เพื่อเชื่อมต่อกับเซิร์ฟเวอร์ ในการสร้างไคลเอนต์คุณต้องระบุการโทรกลับเป็นการโทรกลับเมื่อไคลเอนต์ได้รับข้อความเซิร์ฟเวอร์ จากนั้นเมธอดเริ่มต้น () ของไคลเอ็นต์จะเริ่มการฟังเซิร์ฟเวอร์ (รับลิสเทนเนอร์) เมื่อผู้รับรับข้อมูลได้รับข้อมูลที่ส่งโดยเซิร์ฟเวอร์เมธอดการโทรกลับ (การโทรกลับ) จะถูกเรียกให้ประมวลผล ในเวลาเดียวกันไคลเอนต์ยังต้องส่งแพ็กเก็ต HeartBeat เพื่อแจ้งเซิร์ฟเวอร์ว่ายังคงเชื่อมต่อกับเซิร์ฟเวอร์ แพ็คเก็ตการเต้นของหัวใจถูกเก็บไว้โดยลูกค้า มีชีวิตอยู่ () เริ่มต้นและดำเนินการโดย Keepalivedog; สองขั้นตอนเหล่านี้ดำเนินการโดยพูลเธรดของ newfixedthreadpool (2) ที่มีขนาดคงที่ 2. อาจมีเหตุผลมากกว่าที่จะใช้ newfixedthreadpool (1) และ NewscheduledThreadpool (1) ที่นี่เพื่อจัดการกับมัน รหัสเฉพาะของไคลเอนต์มีดังนี้ (อีกสองวิธีการเปิดเผยที่นี่เพื่อรับซ็อกเก็ตและผู้ใช้ที่ซ็อกเก็ตปัจจุบันเป็นของ):
/** * ไคลเอนต์ * @author yaolin * */ไคลเอนต์คลาสสาธารณะ {ซ็อกเก็ตซ็อกเก็ตสุดท้ายส่วนตัว; สตริงส่วนตัวจาก; Private Executorservice Pool; การโทรกลับครั้งสุดท้ายส่วนตัว ไคลเอนต์สาธารณะ (การโทรกลับการโทรกลับ) โยน ioexception {this.socket = ซ็อกเก็ตใหม่ (ConstantValue.server_ip, ConstantValue.server_port); this.pool = executors.newFixedThreadPool (2); this.callback = การโทรกลับ; } public void start () {pool.execute (ใหม่รับเงิน (ซ็อกเก็ต, โทรกลับ)); } โมฆะสาธารณะ Keepalive (สตริงจาก) {this.from = จาก; pool.execute (ใหม่ Keepalivedog (ซ็อกเก็ตจาก)); } ซ็อกเก็ตสาธารณะ getSocket () {return socket; } สตริงสาธารณะ getFrom () {return from; -[Keepalivedog.java]
หลังจากที่ไคลเอนต์สร้างการเชื่อมต่อกับเซิร์ฟเวอร์ (โปรแกรมนี้หมายถึงการเข้าสู่ระบบที่ประสบความสำเร็จเนื่องจากซ็อกเก็ตของลูกค้าจะได้รับการจัดการโดยซ็อกเชอร์ของเซิร์ฟเวอร์หลังจากการเข้าสู่ระบบประสบความสำเร็จ) จำเป็นต้องส่งแพ็กเก็ตหัวใจไปยังเซิร์ฟเวอร์ทุกครั้งเพื่อบอกเซิร์ฟเวอร์ รหัสของ KeepalivedOg ถูกนำไปใช้ดังนี้ (อาจปรับให้เข้ากับ NewscheduledThreadPool (1) ในภายหลังดังนั้นรหัสที่นี่จะถูกปรับด้วย):
/*** KeepaliveDog: บอกเซิร์ฟเวอร์ไคลเอนต์นี้กำลังทำงานอยู่ * * @author yaolin */คลาสสาธารณะ KeepaliveDog ใช้งาน Runnable {ซ็อกเก็ตซ็อกเก็ตสุดท้ายส่วนตัว; สตริงสุดท้ายส่วนตัวจาก; Public Keepalivedog (ซ็อกเก็ตซ็อกเก็ต, สตริงจาก) {this.socket = ซ็อกเก็ต; this.from = จาก; } @Override โมฆะสาธารณะเรียกใช้ () {ในขณะที่ (ซ็อกเก็ต! = null &&! socket.isclosed ()) {ลอง {printwriter out = printwriter ใหม่ (socket.getOutputStream ()); ข้อความ AliveMessage = new AliveMessage (); message.setFrom (จาก); out.println (json.tojson (ข้อความ)); out.flush (); thread.sleep (constantValue.keep_alive_period * 1000); } catch (exception e) {loggerutil.error ("ไคลเอนต์ส่งข้อความล้มเหลว!" + e.getMessage (), e); -[teceiveListener.java]
วิธีการเริ่มต้น () ของไคลเอนต์เริ่มการฟังเซิร์ฟเวอร์และใช้งานโดยผู้รับ หลังจากได้รับข้อความจากเซิร์ฟเวอร์ผู้รับจะต้องโทรกลับวิธีการโทรกลับของ Dowork () เพื่อให้การเรียกกลับมาใช้ตรรกะทางธุรกิจที่เฉพาะเจาะจง ดังนั้นผู้ที่ได้รับการพิจารณาจะรับผิดชอบเฉพาะการฟังข้อความบนเซิร์ฟเวอร์และการประมวลผลเฉพาะจะถูกจัดการโดยการโทรกลับ ควรกล่าวถึงที่นี่ว่าเมื่อประเภทข้อความเป็นประเภทไฟล์มันจะนอนหลับเพื่อกำหนดค่าช่วงเวลาการดำเนินการเพื่อให้ Dowork ในการโทรกลับสามารถอ่านโฟลว์ไฟล์ไปยังเซิร์ฟเวอร์แทนที่จะป้อนลูปถัดไปโดยตรง การออกแบบที่นี่คล้ายกับเซิร์ฟเวอร์ รหัสการใช้งานเฉพาะของ CEAMIVELISTENER มีดังนี้:
Public Class CANTERELISTENER ใช้งาน Runnable {ซ็อกเก็ตซ็อกเก็ตสุดท้ายส่วนตัว; การโทรกลับครั้งสุดท้ายส่วนตัว Public Teceivelistener (ซ็อกเก็ตซ็อกเก็ต, การโทรกลับการโทรกลับ) {this.socket = ซ็อกเก็ต; this.callback = การโทรกลับ; } @Override โมฆะสาธารณะเรียกใช้ () {ถ้า (ซ็อกเก็ต! = null) {ในขณะที่ (! socket.isclosed ()) {ลอง {inputstream คือ = socket.getInputStream (); สตริงบรรทัด = null; StringBuffer SB = NULL; if (is.available ()> 0) {bufferedReader bufr = bufferedReader ใหม่ (ใหม่ inputStreamReader (IS)); sb = new StringBuffer (); ในขณะที่ (is.available ()> 0 && (line = bufr.readline ())! = null) {sb.append (บรรทัด); } loggerutil.trach ("รับ [" + sb.toString () + "] ที่" + วันที่ใหม่ ()); Callback.dowork (ซ็อกเก็ต, sb.toString ()); ข้อความ basemessage = json.parseObject (sb.toString (), basemessage.class); if (message.getType () == messageType.file) {// หยุดชั่วคราวเพื่อรับไฟล์ loggerutil.trach ("ไคลเอนต์: หยุดชั่วคราวเพื่อรับไฟล์"); thread.sleep (constantvalue.message_period); }} else {thread.sleep (ConstantValue.message_period); }} catch (exception e) {loggerutil.error ("ไคลเอนต์ส่งข้อความล้มเหลว!" + e.getMessage (), e); -[callback.java, defaultcallback.java]
จากด้านบนเราจะเห็นว่าการประมวลผลข้อความของลูกค้าเป็นการโทรกลับและโทรกลับเป็นเพียงอินเทอร์เฟซ การใช้งานการโทรกลับทั้งหมดใช้อินเทอร์เฟซเพื่อประมวลผลข้อความตามความต้องการของพวกเขา ที่นี่การใช้งานเริ่มต้นของการโทรกลับเป็นค่าเริ่มต้น defaultCallback จะประมวลผลข้อความสามประเภทเท่านั้นคือข้อความแชทข้อความไฟล์และส่งคืนข้อความ สำหรับข้อความแชท defaultCallback จะได้รับอินเทอร์เฟซที่เกี่ยวข้องผ่านเส้นทางเราเตอร์ใน UI (ดูการออกแบบ UI ด้านล่างสำหรับรายละเอียด) จากนั้นแสดงข้อความในกล่องแชทที่เกี่ยวข้อง สำหรับข้อความไฟล์ defaultCallback จะเขียนไฟล์ไปยังเส้นทางที่ระบุในการกำหนดค่า (ได้รับไฟล์โดยไม่ได้รับอนุญาตจากผู้ใช้ที่นี่การออกแบบนี้ไม่เป็นมิตรมากดังนั้นในตอนนี้); สำหรับข้อความที่ส่งคืนค่าเริ่มต้นจะถูกเรียกไปยังตัวจัดการที่แตกต่างกันตามคีย์ในข้อความส่งคืน รหัสเฉพาะมีดังนี้:
การโทรกลับส่วนต่อประสานสาธารณะ {โมฆะสาธารณะ Dowork (ซ็อกเก็ตเซิร์ฟเวอร์ข้อมูลวัตถุ); - คลาสสาธารณะ DEFECTCALLBACK ใช้การโทรกลับ {@Override โมฆะสาธารณะ Dowork (ซ็อกเก็ตเซิร์ฟเวอร์ข้อมูลวัตถุ) {ถ้า (data! = null) {ข้อความ basemessage = json.parseObject (data.toString (), basemessage.class); switch (message.getType ()) {กรณี messageType.chat: handlechatMessage (ข้อมูล); หยุดพัก; Case MessageType.File: HandleFileMessage (เซิร์ฟเวอร์, ข้อมูล); หยุดพัก; Case Messagetype.Return: HandlerEturnMessage (ข้อมูล); หยุดพัก; }}} โมฆะส่วนตัว handlechatMessage (ข้อมูลวัตถุ) {chatMessage m = json.parseObject (data.toString (), chatMessage.class); String tabkey = m.getFrom (); // จาก jComponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.chattabbed); if (comp instanceof jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.indexoftab (tabkey); if (index == -1) {tab.addtab (tabkey, resultholder.get (tabkey) .getScrollpane ()); } jtextarea textarea = resultholder.get (tabkey) .getTextArea (); textarea.settext (ใหม่ stringbuffer () .append (textarea.getText ()). ผนวก (system.linesparator ()). ผนวก (system.linesparator ()) .append ("[") .append (M.GetOwner ()). // เลื่อนไปที่ textarea.setCaretposition (textarea.getText (). length ()); }} โมฆะส่วนตัวที่ HandleFileMessage (ซ็อกเก็ตเซิร์ฟเวอร์, ข้อมูลวัตถุ) {ข้อความ FileMessage = JSON.ParseObject (data.toString (), fileMessage.class); if (message.getSize ()> 0) {outputStream OS = null; ลอง {ถ้า (เซิร์ฟเวอร์! = null) {inputStream คือ = server.getInputStream (); ไฟล์ dir = ไฟล์ใหม่ (constantValue.client_receive_dir); if (! dir.exists ()) {dir.mkdirs (); } OS = ใหม่ fileOutputStream (ไฟล์ใหม่ (pathUtil.comBination (ConstantValue.Client_Receive_dir, วันที่ใหม่ (). getTime () + message.getName ()))); int ทั้งหมด = 0; ในขณะที่ (! server.isclosed ()) {ถ้า (is.available ()> 0) {byte [] buff = byte ใหม่ [constantValue.buff_size]; int len = -1; ในขณะที่ (is.available ()> 0 && (len = is.read (buff))! = -1) {os.write (buff, 0, len); ทั้งหมด += len; loggerutil.debug ("รับ buff [" + len + "]"); } os.flush (); if (total> = message.getSize ()) {loggerutil.info ("รับบัฟ [ตกลง]"); หยุดพัก; }}}}}} catch (Exception e) {loggerutil.error ("รับไฟล์ล้มเหลว!" + e.getMessage (), e); } ในที่สุด {ถ้า (os! = null) {ลอง {os.close (); } catch (ข้อยกเว้นละเว้น) {} os = null; }}}} โมฆะส่วนตัว handlerEturnMessage (ข้อมูลวัตถุ) {returnMessage m = json.parseObject (data.toString (), returnmessage.class); if (stringutil.isnotEmpty (m.getKey ())) {switch (m.getKey ()) {case key.notify: // แจ้งไคลเอนต์ให้อัปเดตรายการ USR usr hf.gethandler (key.notify) .handle (data); หยุดพัก; Case Key.login: HF.Gethandler (key.login) .handle (ข้อมูล); หยุดพัก; คีย์เคสบันทึก: hf.gethandler (key.register) .handle (ข้อมูล); หยุดพัก; Case Key.listUser: HF.Gethandler (key.listuser) .handle (ข้อมูล); หยุดพัก; CASE.TIP: hf.gethandler (key.tip) .handle (ข้อมูล); หยุดพัก; -[handler.java, hf.java, listuserhdl.java ... ]
ส่วนประกอบตัวจัดการมีหน้าที่ในการประมวลผลข้อความประเภทข้อความส่งคืนของเซิร์ฟเวอร์ defaultCallback แจกจ่ายข้อความไปยังตัวจัดการที่แตกต่างกันตามคีย์ที่แตกต่างกัน นี่เป็นองค์ประกอบของโรงงานอย่างง่าย มันคล้ายกับข้อมูลที่ได้รับจากเซิร์ฟเวอร์ แผนภาพคลาสที่สมบูรณ์มีดังนี้:
รหัสสำหรับส่วนนี้ได้รับด้านล่าง เพื่อลดพื้นที่ใช้งานรหัสทั้งหมดที่ใช้โดยตัวจัดการจะถูกรวบรวม
ตัวจัดการส่วนต่อประสานสาธารณะ {ด้ามวัตถุสาธารณะ (Object OBJ); - คลาสสาธารณะ HF {ตัวจัดการสาธารณะคงที่ Gethandler (คีย์สตริง) {สวิตช์ (คีย์) {case key.Notify: ส่งคืน notifyhdl ใหม่ (); CASE KEY.LOGIN: ส่งคืนใหม่ loginHDL (); คีย์เคสบันทึก: ส่งคืน registerhdl ใหม่ (); CASE KEY.LISTUSER: ส่งคืน LISTUSERHDL ใหม่ (); CASE.TIP: ส่งคืน TIPHDL ใหม่ (); } return null; - คลาสสาธารณะ ListUserHdl ใช้ Handler {@Override ที่จับวัตถุสาธารณะ (Object obj) {ถ้า (obj! = null) {ลอง {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); if (rm.issuccess () && rm.getContent ()! = null) {clientlistuserdto dto = json.parseObject (rm.getContent (). toString (), clientlistuserdto.class); JComponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.listusrlist); if (comp instanceof jlist) {@suppresswarnings ("unchected") // jlist <String> listUsRlist = (jlist <String>) comp; รายการ <String> listUser = ใหม่ linkedList <String> (); ListUser.Addall (dto.getListuser ()); collections.sort (listuser); listuser.add (0, constantValue.to_all); listUsrlist.setListData (listUser.toArray (สตริงใหม่ [] {})); }}} catch (exception e) {loggerutil.error ("handle listusr ล้มเหลว!" + e.getMessage (), e); }} return null; - คลาสสาธารณะ LoginHdl ใช้ตัวจัดการ {@Override ที่จับวัตถุสาธารณะ (Object obj) {ถ้า (obj! = null) {ลอง {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); if (rm.issuccess ()) {router.getView (registerandloginView.class) .trash (); Router.getView (chatroomview.class) .Create (). display (); clientholder.getClient (). keepalive (rm.getto ()); // Keep ... } else {container container = router.getView (registerandloginView.class) .container (); if (container! = null) {// แสดงข้อผิดพลาด joptionpane.showMessageDialog (คอนเทนเนอร์, rm.getMessage ()); }}} catch (exception e) {loggerutil.error ("มือจับเข้าสู่ระบบล้มเหลว!" + e.getMessage (), e); }} return null; - คลาสสาธารณะ NotifyHdl ใช้ตัวจัดการ {@Override ที่จับวัตถุสาธารณะ (Object obj) {ถ้า (obj! = null) {ลอง {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); if (rm.issuccess () && rm.getContent ()! = null) {clientNotifydto dto = json.parseObject (rm.getContent (). toString (), clientNotifydto.class); JComponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.listusrlist); if (comp instanceof jlist) {@suppresswarnings ("unchected") // jlist <String> listUsRlist = (jlist <String>) comp; รายการ <String> listUser = modelToList (listUsrlist.getModel ()); if (dto.isflag ()) {if (! listuser.contains (dto.getUserName ())) {listuser.add (dto.getUserName ()); ListUser.remove (ConstantValue.to_all); collections.sort (listuser); listuser.add (0, constantValue.to_all); }} else {listuser.remove (dto.getUserName ()); } listUsrlist.setListData (listUser.toArray (สตริงใหม่ [] {})); }}} catch (exception e) {loggerutil.error ("จัดการ nofity ล้มเหลว!" + e.getMessage (), e); }} return null; } รายการส่วนตัว <String> ModelToList (ListModel <String> ListModel) {รายการ <String> list = new LinkedList <String> (); if (listmodel! = null) {สำหรับ (int i = 0; i <listmodel.getSize (); i ++) {list.add (listmodel.getElementat (i)); }} รายการส่งคืน; - คลาสสาธารณะ registerhdl ใช้ตัวจัดการ {@Override ที่จับวัตถุสาธารณะ (Object obj) {ถ้า (obj! = null) {ลอง {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); container container = router.getView (registerandloginView.class) .container (); if (container! = null) {if (rm.issuccess ()) {joptionpane.showMessagedialog (คอนเทนเนอร์, rm.getContent ()); } else {joptionpane.showMessageDialog (คอนเทนเนอร์, rm.getMessage ()); }}} catch (exception e) {loggerutil.error ("มือจับการลงทะเบียนล้มเหลว!" + e.getMessage (), e); }} return null; - ชั้นเรียนสาธารณะ TIPHDL ใช้ตัวจัดการ {@Override ที่จับวัตถุสาธารณะ (Object obj) {ถ้า (obj! = null) {ลอง {returnMessage m = json.parseObject (obj.toString (), returnMessage.class); if (m.issuccess () && m.getContent ()! = null) {สตริง tabkey = m.getFrom (); String tip = m.getContent (). toString (); JComponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.chattabbed); if (comp instanceof jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.indexoftab (tabkey); if (index == -1) {tab.addtab (tabkey, resultholder.get (tabkey) .getScrollpane ()); } jtextarea textarea = resultholder.get (tabkey) .getTextArea (); textarea.settext (สตริงใหม่ () .append (textarea.getText ()). ผนวก (System.linesparator ()). ผนวก (system.linesparator ()) .append ("[") .append (m.getowner ()). // เลื่อนไปที่ textarea.setCaretposition (textarea.getText (). length ()); }}} catch (exception e) {loggerutil.error ("ด้ามจับเคล็ดลับล้มเหลว!" + e.getMessage (), e); }} return null; - มีคลาสอื่นสำหรับโมดูลการสื่อสารของซ็อกเก็ตนั่นคือผู้ถือครองลูกค้าซึ่งใช้ในการจัดเก็บไคลเอนต์ปัจจุบันซึ่งคล้ายกับซ็อกเคโธด์บนเซิร์ฟเวอร์
/** * @author Yaolin */ผู้ให้บริการลูกค้าระดับสาธารณะ {ไคลเอนต์ลูกค้าคงที่สาธารณะ; ไคลเอนต์สแตติกสาธารณะ GetClient () {ส่งคืนไคลเอนต์; } โมฆะคงที่สาธารณะ setClient (ไคลเอนต์ไคลเอนต์) {clientHolder.Client = ไคลเอนต์; -การใช้งานเฉพาะของโมดูล UI:
บันทึกข้างต้นการออกแบบโมดูลการสื่อสารซ็อกเก็ต ต่อไปฉันบันทึกโมดูลการออกแบบของ UI ฉันไม่ได้วางแผนที่จะเขียน UI ด้วยตัวเอง ท้ายที่สุดการเขียนนั้นน่าเกลียดเกินไปดังนั้นฉันอาจขอให้เพื่อนร่วมชั้นหรือเพื่อนช่วยฉันเคาะมันในภายหลัง ดังนั้นฉันจึงส่งมอบการประมวลผลเหตุการณ์ UI เพื่อดำเนินการเพื่อจัดการและแยกการออกแบบ UI และการตอบสนองเหตุการณ์ UIs ทั้งหมดสืบทอด JFrame และใช้อินเทอร์เฟซมุมมอง คลาสการใช้งานตัวจัดการด้านบนจะได้รับผ่านเราเตอร์ (จะถูกส่งคืนโดยตรงหากมีอยู่และจะถูกสร้างและจัดเก็บหากไม่มีอยู่) มุมมองให้การสร้าง UI (), รับคอนเทนเนอร์ (), รับส่วนประกอบใน UI getComponent (), แสดงผลการแสดงผล () และรีไซเคิลถังขยะ (); ResultWrapper และ Resultholder เป็นเพียงการสร้างและจัดเก็บแท็บแชท การออกแบบมีดังนี้:
[router.java, view.java]
UIs ทั้งหมดสืบทอด JFrame และใช้อินเทอร์เฟซมุมมอง คลาสการใช้งานตัวจัดการจะได้รับ UI ที่ระบุผ่านเราเตอร์ (จะกลับมาโดยตรงหากมีอยู่และมันจะสร้างและจัดเก็บหากไม่มีอยู่) มุมมองให้การสร้าง UI () รับคอนเทนเนอร์ () และได้รับส่วนประกอบใน UI getComponent () แสดงการแสดงผล () และถังขยะรีไซเคิล () การใช้งานเฉพาะมีดังนี้:
/*** ดูเส้นทาง* @author yaolin*/เราเตอร์คลาสสาธารณะ {แผนที่คงที่ส่วนตัว <สตริง, ดู> listroute = ใหม่ hashmap <สตริง, ดู> (); มุมมองคงที่สาธารณะ getView (คลาส <?> clazz) {ดู v = listroute.get (clazz.getName ()); if (v == null) {ลอง {v = (ดู) class.forName (clazz.getName ()). newInstance (); listroute.put (clazz.getName (), v); } catch (exception e) {loggerutil.error ("สร้างมุมมองล้มเหลว!" + e.getMessage (), e); }} return v; - /** * อินเทอร์เฟซ Canonical สำหรับอินเทอร์เฟซทั้งหมด * @author yaolin * */มุมมองอินเตอร์เฟสสาธารณะ {/** * */มุมมองสาธารณะสร้าง (); / ** * */ คอนเทนเนอร์สาธารณะ (); / ** * คีย์ @param */ สาธารณะ JComponent getComponent (คีย์สตริง); / ** * */ public void display (); / ** * */ ถังขยะสาธารณะ (); -[registerandloginview.java, chatroomview.java]
เนื่องจากฉันไม่ต้องการเขียน UI ด้วยตัวเองฉันแค่เขียนอินเทอร์เฟซ UI สองตัวที่นี่คืออินเทอร์เฟซการลงทะเบียนและเข้าสู่ระบบและอินเทอร์เฟซแชท นี่คือสองอินเทอร์เฟซน่าเกลียด:
ลงทะเบียนอินเตอร์เฟสเข้าสู่ระบบ
อินเทอร์เฟซแชท
ต่อไปนี้เป็นรหัสเฉพาะสำหรับอินเทอร์เฟซทั้งสองนี้:
/*** ลงทะเบียนและเข้าสู่ระบบ* @author yaolin*/คลาสสาธารณะลงทะเบียนและการลงทะเบียนสาธารณะและการขยายการดำเนินการ jframe onplame {ส่วนตัวคงที่สุดท้ายสุดท้าย serialversionuid = 6322088074312546736L; การลงทะเบียนครั้งสุดท้ายส่วนตัวและการดำเนินการใด ๆ = ใหม่ registerandloginaction (); บูลีนคงที่ส่วนตัวสร้าง = false; @Override Public View สร้าง () {ถ้า (! สร้าง) {init (); สร้าง = true; } ส่งคืนสิ่งนี้; } คอนเทนเนอร์สาธารณะ () {create (); กลับ getContentPane (); } @Override สาธารณะ JComponent getComponent (คีย์สตริง) {return null; } @Override Public Void Display () {setVisible (จริง); } @Override ถังขยะสาธารณะ () {dispose (); } โมฆะส่วนตัว init () {// แอตทริบิวต์ setsize (500, 300); setResizable (เท็จ); SetLocationRelativeto (NULL); // คอนเทนเนอร์ jPanel Panel = new JPanel (); Panel.setLayout (NULL); // ส่วนประกอบ // ชื่อผู้ใช้ jlabel lbusername = new JLabel (i18n.text_username); lbusername.setbounds (100, 80, 200, 30); jtextfield สุดท้าย tfusername = new jtextfield (); tfusername.setbounds (150, 80, 230, 30); panel.add (lbusername); panel.add (tfusername); // รหัสผ่าน jlabel lbpassword = new jlabel (i18n.text_password); lbpassword.setbounds (100, 120, 200, 30); ขั้นสุดท้าย JPasswordField PFPassword = new JPasswordField (); PFPassword.setBounds (150, 120, 230, 30); Panel.Add (LBPassword); Panel.Add (PFPassword); // btnregister jbutton btnregister = new jbutton (i18n.btn_register); btnregister.setbounds (100, 175, 80, 30); // btnlogin สุดท้าย jbutton btnlogin = ใหม่ jbutton (i18n.btn_login); btnlogin.setbounds (200, 175, 80, 30); // btncancel jbutton btnexit = ใหม่ jbutton (i18n.btn_exit); btnexit.setbounds (300, 175, 80, 30); Panel.add (btnregister); Panel.add (btnlogin); Panel.add (btnexit); // event pfpassword.addkeyListener (keyadapter ใหม่ () {โมฆะสาธารณะคีย์กด (keyevent สุดท้าย e) {ถ้า (e.getkeycode () == keyevent.vk_enter) btnlogin.doclick ();}}); ActionPerformed (Final ActionEvent E) {if (StringUtil.isEmpty (tfusername.getText ()) || stringutil.isempty (สตริงใหม่ (pfassword.get.getpassword ())) {joptionpane.showmessagedialog (getContentpane () action.handleregister (tfusername.getText (), สตริงใหม่ (pfpassword.getPassword ())); StringUtil.isempty (สตริงใหม่ (PFPassword.getPassword ())) {joptionpane.ShowMessageHialog (getContentPane (), i18n.info_login_empty_data); ของ AddactionListener btnexit.addactionListener (ใหม่ actionListener () {โมฆะสาธารณะ actionperformed (สุดท้าย actionEvent e) {system.exit (0); }}); // สิ้นสุด addactionListener getContentPane (). เพิ่ม (แผง); SetDefaultCloseoperation (jframe.exit_on_close); - /** * หน้าต่างแชทไคลเอนต์ * * @author yaolin */คลาสสาธารณะ Chatroomview ขยาย jframe onplame view {ส่วนตัวคงที่สุดท้ายสุดท้าย serialversionuid = -4515831172899054818l; สตริงสุดท้ายคงที่สาธารณะ listUSRLIST = "LISTUSRLIST"; String String สุดท้ายของสาธารณะ chattabbed = "chattabbed"; บูลีนคงที่ส่วนตัวสร้าง = false; Action Action การแชทส่วนตัว = new Chatroomaction (); jlist ส่วนตัว <String> listUsRlist = null; jtabbedpane ส่วนตัว chattabbed = null; @Override Public View สร้าง () {ถ้า (! สร้าง) {init (); สร้าง = true; } ส่งคืนสิ่งนี้; } คอนเทนเนอร์สาธารณะ () {create (); กลับ getContentPane (); } @Override สาธารณะ JComponent getComponent (คีย์สตริง) {create (); สวิตช์ (คีย์) {case listusrlist: return listusrlist; Case Chattabbed: กลับ chattabbed; } return null; } @Override Public Void Display () {setVisible (จริง); } @Override ถังขยะสาธารณะ () {dispose (); } โมฆะสาธารณะ init () {settitle (i18n.text_app_name); setsize (800, 600); setResizable (เท็จ); SetLocationRelativeto (NULL); SetLayout (New BorderLayout ()); เพิ่ม (createchatpanel (), borderlayout.center); เพิ่ม (createUsrlistView (), BorderLayout.east); SetDefaultCloseoperation (jframe.exit_on_close); } ส่วนตัว JComponent CreateChatPanel () {// ตัวเลือกไฟล์สุดท้าย JFileChooser FileChooser = JFileChooser ใหม่ (); แผง jPanel = new JPanel (ใหม่ BorderLayout ()); // center chattabbed = new jtabbedpane (); chattabbed.addtab (ConstantValue.to_all, resultholder.get (ConstantValue.to_all) .getScrollpane ()); Panel.add (Chattabbed, Borderlayout.center); // South JPanel South = New JPanel (New BorderLayout ()); // ใต้ - ไฟล์ JPanel Middle = new JPanel (ใหม่ BorderLayout ()); Middle.add (New Jlabel (), BorderLayout.Center); // เพียงสำหรับการเติม jbutton btnupload = ใหม่ jbutton (i18n.btn_send_file); middle.add (btnupload, borderlayout.east); SOUTH.ADD (กลาง, Borderlayout.north); // ใต้ - textarea สุดท้าย jtextarea tasend = new jtextarea (); tasend.setCaretColor (color.blue); TASEND.SETMARGIN (สิ่งที่ใส่เข้าไปใหม่ (10, 10, 10, 10)); tasend.setrows (10); SOUTH.ADD (TASEND, BorderLayout.Center); // ใต้ - btn jpanel bottom = new JPanel (ใหม่ BorderLayout ()); Bottom.add (ใหม่ Jlabel (), BorderLayout.Center); // เพียงสำหรับการเติม jbutton btnsend = new jbutton (i18n.btn_send); BOTTOR.ADD (BTNSEND, BorderLayout.east); SOUTH.ADD (ล่าง, Borderlayout.South); btnupload.addactionListener (ใหม่ actionListener () {โมฆะสาธารณะ ActionPerformed (ActionEvent E) {ถ้า (! ConstantValue.to_all.equals (chattabbed.getTitleat (chattabbed.getSelectedIndex ())) jfilechooser.approve_option) {ไฟล์ไฟล์ = filechooser.getSelectedFile (); - btnsend.addactionListener (ใหม่ actionListener () {โมฆะสาธารณะ ActionPerformed (ActionEvent E) {ถ้า (Stringutil.isnotEmpty (tasend.getText ())) {action.send (chattabbed.getTitleat - Panel.add (ใต้, Borderlayout.south); แผงส่งคืน; } ส่วนตัว jComponent createUsrlistView () {listUsRlist = new JList <String> (); ListUsrList.setBorder (ใหม่ lineborder (color.blue)); listUsRlist.setListData (สตริงใหม่ [] {ConstantValue.to_all}); ListUSRLIST.SETFIXEDCELLWIDTH (200); ListUsrlist.setFixedCellHeight (30); listusrlist.addlistselectionListener (ใหม่ listselectionListener () {@Override โมฆะสาธารณะ valueChanged (listselectionEvent e) {// แชทไปที่ (chattabbed.indexoftab ! listusrlist.getSelectedValue (). เท่ากับ (clientholder.getClient (). getFrom ())) {chattabbed.addtab (listusrlist.getSelectedValue (), resultholder.get Chattabbed.setSelectedIndex (chattabbed.indexoftab (listusrlist.getSelectedValue ()));}}}); return listusrlist; -[registerandloginaction.java, chatroomaction.java]
ที่นี่การประมวลผลเหตุการณ์ UI ได้รับการจัดการโดยการกระทำซึ่งแยกการออกแบบ UI และการตอบสนองเหตุการณ์ กิจกรรมของ RegisterandLoginView ได้รับการจัดการโดย registerandloginaction และกิจกรรมของ Chatroomview ได้รับการจัดการโดย Chatroomaction การใช้งานเฉพาะมีดังนี้:
การลงทะเบียนคลาสสาธารณะและการทำหน้าที่เป็นโมฆะสาธารณะ handleregister (ชื่อผู้ใช้สตริง, รหัสผ่านสตริง) {ถ้า (stringutil.isempty (ชื่อผู้ใช้) || stringutil.isempty (รหัสผ่าน)) {return; } registerMessage message = new registerMessage () .setUserName (ชื่อผู้ใช้) .SetPassword (รหัสผ่าน); message.setFrom (ชื่อผู้ใช้); sendhelper.send (clientholder.getClient (). getSocket (), ข้อความ); } โมฆะสาธารณะ handlelogin (ชื่อผู้ใช้สตริง, รหัสผ่านสตริง) {ถ้า (stringutil.isempty (ชื่อผู้ใช้) || stringutil.isempty (รหัสผ่าน)) {return; } LoginMessage Message = new LoginMessage () .setUserName (ชื่อผู้ใช้) .setPassword (รหัสผ่าน); message.setFrom (ชื่อผู้ใช้); sendhelper.send (clientholder.getClient (). getSocket (), ข้อความ); - มีอีกสองคลาสสำหรับการออกแบบ UI คือ resultholder และ resultwrapper ResultWrapper และ Resultholder เป็นเพียงการสร้างและจัดเก็บแท็บแชท การใช้งานเฉพาะมีดังนี้:
Public Class ResultWapper {Private JScrollpane Scrollpane; jtextarea ส่วนตัว textarea; Public ResultsWrapper (JScrollpane Scrollpane, Jtextarea textarea) {this.scrollpane = scrollpane; this.textarea = textarea; } สาธารณะ jscrollpane getScrollpane () {return scrollpane; } โมฆะสาธารณะ setscrollpane (jscrollpane scrollpane) {this.scrollpane = scrollpane; } สาธารณะ jtextarea getTextArea () {return textarea; } โมฆะสาธารณะ setTextArea (jtextarea textarea) {this.textarea = textarea; - Public Class Resultholder {แผนที่คงที่ส่วนตัว <String, ResultWrapper> ListResultWrapper = new HashMap <string, resultWrapper> (); Public Static Void Put (คีย์สตริง, resultwrapper wrapper) {listresultwrapper.put (คีย์, wrapper); } Public Static ResultWrapper รับ (คีย์สตริง) {resultWrapper wrapper = listresultWrapper.get (คีย์); if (wrapper == null) {wrapper = create (); ใส่ (กุญแจ, เสื้อคลุม); } ส่งคืน wrapper; } private static resultwrapper สร้าง () {jtextarea resultTextArea = new JTextArea (); resultTextArea.setEditable (เท็จ); resultTextArea.setBorder (ใหม่ lineBorder (color.blue)); jscrollpane scrollpane = new JScrollpane (resultTextArea); Scrollpane.sethorizontalscrollbarpolicy (ScrollPaneconstants.horizontal_scrollbar_never); Scrollpane.setverticalscrollbarpolicy (scrollpaneconstants.vertical_scrollbar_as_needed); resultWrapper wrapper = new resultWrapper (scrollpane, resultTextArea); เสื้อคลุมกลับ; - รายการสุดท้ายจะได้รับรายการที่ลูกค้ารัน:
/** * * @author yaolin * */คลาสสาธารณะ niloaychat {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ดู v = router.getView (registerandloginView.class) .Create (); ลอง {V.Display (); ไคลเอนต์ไคลเอนต์ = ไคลเอนต์ใหม่ (ใหม่ defaultCallback ()); client.start (); clientholder.setClient (ไคลเอนต์); } catch (ioexception e) {joptionpane.showMessagedialog (v.container (), e.getMessage ()); - ที่อยู่ดาวน์โหลดตัวอย่าง: ตัวอย่าง
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น