مكتوبة من قبل:
في نهاية الأسبوع الماضي ، استغرقت بعض الوقت لتسجيل التصميم الأولي والتصميم المفصل من جانب الخادم لبرنامج دردشة Socket Socket Simple. انتظرت أخيرًا شهادة الامتحان الناعمة التي أخذتها قبل التخرج يوم الثلاثاء ، ثم قضيت يوم العمل الإضافي. حدث اليوم يوم الجمعة ، وخططت لتسجيل التصميم التفصيلي للعميل والوحدة المشتركة ، لأنني سأكون مشغولاً بأشياء أخرى تبدأ في نهاية هذا الأسبوع.
تصميم:
ينقسم تصميم العميل بشكل أساسي إلى جزأين ، وهما تصميم وحدة اتصالات Socket والتصميم المتعلق بالاتصالات.
تصميم اتصالات مقبس العميل:
يشبه التصميم هنا في الواقع تصميم الخادم. الفرق هو أن الخادم يتلقى حزم نبضات ، بينما يرسل العميل حزم نبضات القلب. نظرًا لأن العميل يتواصل فقط مع خادم واحد (يتم توزيع الاتصالات بين العملاء أيضًا بواسطة الخادم) ، يتم استخدام مجموعة مؤشرات ترابط بحجم 2 فقط للتعامل مع هذين الأمرين (NewFixedThreadPool (2)). فئات المعالجة المقابلة هي مستقبِل و keepalivedog. عند تهيئة المستقبِل ، يتم إرسال رد الاتصال كرد اتصال على العميل يستقبل رسالة الخادم. التنفيذ الافتراضي لاستدعاء رد الاتصال هو DefaultCallback. يتم توزيع DefaultCallback على معالجات مختلفة من خلال HF وفقًا للأحداث المختلفة. يقوم العميل بتخزين معلومات العميل الحالية. التصميم كما يلي:
التنفيذ المحدد لوحدة اتصال المقبس:
[client.java]
العميل هو مدخل العميل للاتصال بالخادم. لإنشاء عميل ، تحتاج إلى تحديد رد اتصال كرد على رد الاتصال عندما يتلقى العميل رسالة الخادم. ثم تبدأ طريقة Start () للعميل الاستماع إلى الخادم (Receivelistener). عندما يتلقى المستهلك البيانات المرسلة من الخادم ، يتم استدعاء طريقة رد الاتصال (رد الاتصال) لمعالجتها. في الوقت نفسه ، يحتاج العميل أيضًا إلى إرسال حزمة نبضات القلب لإخطار الخادم بأنه لا يزال متصلاً بالخادم. يتم الاحتفاظ بحزمة نبضات القلب من قبل العميل. يتم تشغيله على Alive () بواسطة Keepenivedog ؛ يتم تنفيذ هاتين الخطوتين بواسطة مجموعة مؤشرات ترابط من NewFixedThreadPool (2) بحجم ثابت من 2. قد يكون من المعقول استخدام NewFixedThreadPool (1) و NewsCheduledThreadPool (1) هنا للتعامل معها ، لأن حزمة نبضات القلب يتم إرسالها بانتظام ، وهذا هو كيف ينفس الخادم (هذا التعديل اللاحق). الكود المحدد للعميل هو كما يلي (يتم عرض الطريقتين الأخريين هنا للحصول على المقبس والمستخدم الذي ينتمي إليه المقبس الحالي):
/** * عميل * Author yaolin * */Client Client {Private Final Socket Socket ؛ سلسلة خاصة من ؛ تجمع الخاصين النهائيين للخدمة النهائية ؛ رد اتصال رد الاتصال النهائي الخاص ؛ العميل العام (رد الاتصال) يلقي ioException {this.socket = new Socket (constantValue.server_ip ، constantvalue.server_port) ؛ this.pool = Executors.NewFixedThreadPool (2) ؛ this.callback = رد الاتصال ؛ } public void start () {pool.execute (new Receivelistener (socket ، callback)) ؛ } void public keepalive (سلسلة من) {this.from = from ؛ pool.execute (New Keepivedog (Socket ، from)) ؛ } المقبس العام getSocket () {return Socket ؛ } السلسلة العامة getFrom () {return from ؛ }}[keepalivedog.java]
بعد أن يقوم العميل بإنشاء اتصال مع الخادم (يشير هذا البرنامج إلى تسجيل الدخول الناجح ، لأن مقبس العميل سيتم إدارةه بواسطة Socketholder للخادم بعد أن أصبح تسجيل الدخول ناجحًا) ، فمن الضروري إرسال حزمة نبضات القلب إلى الخادم في كل مرة لإخبار الخادم بأنه لا يزال على اتصال مع الخادم ، وإلا فإن الخادم سوف يتجاهل الجوز دون استخدام بعد فترة من الوقت (رؤية مدونة الخادم للحصول على التفاصيل). يتم تنفيذ مدونة Keepalivedog على النحو التالي (قد يتم تعديلها مع NewsCheduledThreadPool (1) لاحقًا ، وبالتالي سيتم تعديل الكود هنا أيضًا):
/*** keepalivedog: أخبر الخادم أن هذا العميل يعمل ؛ * * Author yaolin */public class keepalivedog تنفذ Runnable {private Final Socket ؛ سلسلة نهائية خاصة من ؛ public keepalivedog (مقبس المقبس ، سلسلة من) {this.socket = socket ؛ this.from = من ؛ } Override public void run () {بينما (socket! = null &&! socket.isclosed ()) {try {printWriter out = new printWriter (socket.getOutputStream ()) ؛ رسالة alivemessage = new alivemessage () ؛ message.setFrom (من) ؛ Out.println (json.tojson (message)) ؛ out.flush () ؛ thread.sleep (constantValue.keep_alive_period * 1000) ؛ } catch (استثناء e) {loggerUtil.error ("فشلت رسالة إرسال العميل!" + E.GetMessage () ، e) ؛ }}}}[requivelistener.java]
تبدأ طريقة Start () للعميل الاستماع إلى الخادم ويتم تنفيذها بواسطة Recielistener. بعد استلام الرسالة من الخادم ، سيقوم Receivelistener باستدعاء طريقة Dowork () مرة أخرى للسماح لاستدعاء رد الاتصال بمنطق العمل المحدد. لذلك ، يكون المستلم مسؤولاً فقط عن الاستماع إلى الرسائل الموجودة على الخادم ، ويتم التعامل مع المعالجة المحددة عن طريق رد الاتصال. تجدر الإشارة هنا إلى أنه عندما يكون نوع الرسالة نوعًا من الملفات ، فسوف ينام لتكوين فاصل التنفيذ ، بحيث يمكن لـ Dowork في رد الاتصال قراءة تدفق الملف إلى الخادم بدلاً من إدخال الحلقة التالية مباشرة. يشبه التصميم هنا الخادم. رمز التنفيذ المحدد لـ Receivelistener هو كما يلي:
طبقة عامة ، قم بتأليف Runnable {Private Final Socket ؛ رد اتصال رد الاتصال النهائي الخاص ؛ مستلم عام (مقبس المقبس ، رد الاتصال على رد الاتصال) {this.socket = socket ؛ this.callback = رد الاتصال ؛ } Override public void run () {if (socket! = null) {while (! socket.isclosed ()) {try {inputStream is = socket.getInputStream () ؛ خط السلسلة = فارغ ؛ StringBuffer SB = NULL ؛ if (is.available ()> 0) {bufferedReader bufr = new BufferEdReader (new inputStreamReader (IS)) ؛ sb = new StringBuffer () ؛ بينما (is.available ()> 0 && (line = bufr.ReadLine ())! = null) {sb.append (line) ؛ } loggerUtil.trach ("receed [" + sb.toString () + "] at" + new date ()) ؛ callback.dowork (socket ، sb.tostring ()) ؛ رسالة basemessage = json.parseObject (sb.toString () ، basemessage.class) ؛ if (message.gettype () == messageType.file) {// إيقاف مؤقت لتلقي ملفات الملفات loggerUtil.trach ("العميل: توقف لتلقي الملف") ؛ thread.sleep (constantvalue.message_period) ؛ }} آخر {thread.sleep (constantValue.Message_Period) ؛ }} catch (استثناء e) {loggerUtil.error ("فشل إرسال رسالة إرسال العميل!" + E.GetMessage () ، e) ؛ }}}}}}}[callback.java ، defaultCallback.java]
مما سبق ، يمكننا أن نرى أن معالجة العميل للرسائل هي رد اتصال رد الاتصال ، وأن رد الاتصال الخاص به هو مجرد واجهة. تنفذ جميع تطبيقات رد الاتصال الواجهة لمعالجة الرسائل وفقًا لاحتياجاتها. هنا ، فإن التنفيذ الافتراضي لاستدعاء رد الاتصال هو DefaultCallback. تقوم DefaultCallback فقط بمعالجة ثلاثة أنواع من الرسائل ، وهي رسائل الدردشة ورسائل الملفات وإرجاع الرسائل. بالنسبة لرسائل الدردشة ، ستحصل DefaultCallback على الواجهة المقابلة من خلال مسار جهاز التوجيه في واجهة المستخدم (انظر تصميم واجهة المستخدم أدناه للحصول على التفاصيل) ، ثم عرض الرسالة في مربع الدردشة المقابل ؛ بالنسبة لرسائل الملفات ، ستقوم DefaultCallback بكتابة الملف إلى المسار المحدد في التكوين (يتم استلام الملف دون إذن المستخدم هنا. هذا التصميم ليس ودود للغاية ، لذلك في الوقت الحالي) ؛ بالنسبة لرسائل الإرجاع ، سيتم استدعاء DefaultCallback إلى معالجات مختلفة وفقًا للمفتاح في رسالة الإرجاع. الرمز المحدد كما يلي:
اتصال الواجهة العامة {public void dowork (Socket Server ، data Object) ؛ } الفئة العامة DefaultCallback تنفذ Callback {Override public void dowork (Socket Server ، data Object) {if (data! = null) {basemessage message = json.parseObject (data.toString () ، basemessage.class) ؛ Switch (message.gettype ()) {case messageType.Chat: HandleChatMessage (data) ؛ استراحة؛ case messageType.file: handlefilemessage (الخادم ، البيانات) ؛ استراحة؛ Case messageType.Return: HandlereturnMessage (Data) ؛ استراحة؛ }}} private void handleChatMessage (data abound) {chatmessage m = json.parseObject (data.toString () ، chatmessage.class) ؛ string tabkey = m.getFrom () ؛ // from jComponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.chattabbed) ؛ if (comp comp exateof 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 (new StringBuffer () .Append (textarea.getText ()). إلحاق (System.LineseParator ()). .ToString ()) ؛ // scroll to bottom textarea.setCaretPosition (textarea.getText (). length ()) ؛ }} private void handlefileMessage (Socket Server ، Data Object) {fileMessage message = json.parseObject (data.toString () ، filemessage.class) ؛ if (message.getSize ()> 0) {outputStream os = null ؛ حاول {if (server! = null) {inputStream is = server.getInputStream () ؛ ملف dir = ملف جديد (constantvalue.client_receive_dir) ؛ if (! dir.exists ()) {dir.mkdirs () ؛ } OS = جديد fileOutputStream (ملف جديد (pathutil.combination (constantvalue.client_receive_dir ، date (). getTime () + message.getName ()))) ؛ int total = 0 ؛ بينما (! server.isclosed ()) {if (is.available ()> 0) {byte [] buff = new byte [constantvalue.buff_size] ؛ int len = -1 ؛ بينما (is.available ()> 0 && (len = iS.Read (buff))! = -1) {os.write (buff ، 0 ، len) ؛ المجموع += لين ؛ loggerUtil.debug ("تلقي Buff [" + len + "]") ؛ } os.flush () ؛ if (total> = message.getSize ()) {loggerUtil.info ("تلقي buff [ok]") ؛ استراحة؛ }}}}}} catch (استثناء e) {loggerUtil.error ("فشل ملف الاستلام!" + e.getMessage () ، e) ؛ } أخيرًا {if (os! = null) {try {os.close () ؛ } catch (استثناء تجاهل) {} os = null ؛ }}}} private void handlereturnMessage (بيانات الكائن) {returnMessage m = json.parseObject (data.toString () ، returnmessage.class) ؛ if (stringutil.isnotempty (m.getKey ())) {switch (m.getKey ()) {case key.notify: // إخطار العميل بتحديث قائمة usr hf.gethandler (key.notify) .handle (data) ؛ استراحة؛ case key.login: hf.gethandler (key.login) .handle (data) ؛ استراحة؛ case key.register: hf.gethandler (key.register) .handle (data) ؛ استراحة؛ case key.listuser: hf.gethandler (key.listuser) .handle (data) ؛ استراحة؛ key.tip: hf.gethandler (key.tip) .handle (data) ؛ استراحة؛ }}}}[Handler.java ، Hf.java ، ListUserHdl.java ...]
مكون المعالج مسؤول عن معالجة رسائل نوع رسالة إرجاع الخادم. DefaultCallback يوزع الرسائل على معالجات مختلفة وفقًا لمفاتيح مختلفة. هذا هو أيضا مكون المصنع بسيط. يشبه البيانات التي يتلقاها الخادم. مخطط الفصل الكامل هو كما يلي:
ويرد رمز هذا القسم أدناه. من أجل تقليل المساحة ، يتم جمع جميع التعليمات البرمجية التي تنفذها المعالج.
معالج الواجهة العامة {مقبض الكائن العام (Object OBJ) ؛ } الفئة العامة hf {public static Handler Gethandler (string key) {switch (key) {case key.notify: return new NotifyHdl () ؛ case key.login: return new loginhdl () ؛ case key.register: return new registerHdl () ؛ case key.listuser: return new ListUserHdl () ؛ case key.tip: return new tiphdl () ؛ } إرجاع فارغ ؛ }} الفئة العامة ListUserHdl تنفذ معالج {Override الكائن العام (Object obj) {if (obj! = null) {try {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 comp exateof jList) {suppressWarnings ("uncheced") // jList <String> listUsrlist = (jList <String>) comp ؛ قائمة <Tring> listUser = new LinkedList <String> () ؛ ListUser.addall (dto.getListuser ()) ؛ collections.sort (listuser) ؛ ListUser.add (0 ، constantValue.to_all) ؛ ListUsrlist.setListData (ListUser.toarray (سلسلة جديدة [] {})) ؛ }}} catch (استثناء e) {loggerUtil.error ("فشل Handle ListUsr!" + E.GetMessage () ، e) ؛ }} الإرجاع null ؛ }} تنفذ loginHdl من الفئة العامة معالج {Override الكائن العام (Object OBJ) {if (obj! = null) {try {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 ...} آخر {container container = router.getView (registerandloginview.class) .Container () ؛ if (Container! = null) {// show error joptionpane.showmessagedialog (Container ، rm.getMessage ()) ؛ }}} catch (استثناء e) {loggerUtil.error ("فشل تسجيل الدخول!" + E.GetMessage () ، e) ؛ }} الإرجاع null ؛ }} الفئة العامة NotifyHdl تنفذ Handler {Override الكائن العام (Object OBJ) {if (obj! = null) {try {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 comp exateof jList) {suppressWarnings ("uncheced") // jList <String> listUsrlist = (jList <String>) comp ؛ قائمة <Tring> 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) ؛ }} آخر {listUser.remove (dto.getUserName ()) ؛ } listusrlist.setListData (listuser.toarray (سلسلة جديدة [] {})) ؛ }}} catch (استثناء e) {loggerUtil.error ("فشل NOFITY!" + E.GetMessage () ، e) ؛ }} الإرجاع null ؛ } قائمة خاصة <Tring> modelToList (listModel <string> listModel) {list <string> list = new LinkedList <string> () ؛ if (listModel! = null) {for (int i = 0 ؛ i <listmodel.getSize () ؛ i ++) {list.add (listmodel.getElementat (i)) ؛ }} قائمة الإرجاع ؛ }} الفئة العامة registerHdl تنفذ معالج {Override الكائن العام (Object obj) {if (obj! = null) {try {returnmessage rm = json.parsebject (obj.toString () ، returnmessage.class) ؛ حاوية الحاوية = router.getView (registerandLoginView.class) .Container () ؛ if (Container! = null) {if (rm.isccecess ()) {joptionpane.showmessagedialog (حاوية ، rm.getContent ()) ؛ } آخر {joptionpane.showmessagedialog (حاوية ، rm.getMessage ()) ؛ }}} catch (استثناء e) {loggerUtil.error ( }} الإرجاع null ؛ }} الفئة العامة TIPHDL تنفذ معالج {Override الكائن العام (Object OBJ) {if (obj! = null) {try {returnmessage m = json.parseObject (obj.toString () ، returnmessage.class) ؛ if (m.issuccess () && m.getContent ()! = null) {string tabkey = m.getFrom () ؛ نصيحة السلسلة = m.getContent (). toString () ؛ JCOMPONENT COMP = ROUTER.GETVIEW (chatroomview.class) .getComponent (chatroomview.chattabbed) ؛ if (comp comp exateof 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 (new StringBuffer () .Append (textarea.getText ()). إلحاق (system.lineseparator ()). // scroll to bottom textarea.setCaretPosition (textarea.getText (). length ()) ؛ }}} catch (استثناء e) {loggerUtil.error ("فشل نصيحة المقبض!" + e.getMessage () ، e) ؛ }} الإرجاع null ؛ }} هناك فئة أخرى لوحدة اتصال المقبس ، أي حامل العميل ، والذي يتم استخدامه لتخزين العميل الحالي ، والذي يشبه Socketholder على الخادم.
/** * Author yaolin */clientholder clientholder {العميل الثابت العام ؛ العميل الثابت العام getClient () {return client ؛ } public static void setClient (عميل العميل) {clientholder.client = client ؛ }}تنفيذ محدد لوحدة واجهة المستخدم:
يسجل أعلاه تصميم وحدة اتصال المقبس. بعد ذلك ، أسجل وحدة تصميم واجهة المستخدم. لا أخطط لكتابة واجهة المستخدم بنفسي. بعد كل شيء ، فإن الكتابة قبيحة للغاية ، لذلك قد أطلب من زملاء الدراسة أو الأصدقاء مساعدتي في ضربها لاحقًا. لذلك أقوم بتسليم معالجة حدث واجهة المستخدم إلى العمل للتعامل معها ، وفصل ببساطة تصميم واجهة المستخدم والاستجابة للحدث. جميع واجهة المستخدم يرث JFrame وتنفيذ واجهة العرض. يتم الحصول على فئة تنفيذ المعالج أعلاه من خلال جهاز التوجيه (سيتم إرجاعه مباشرة إذا كان موجودًا ، وسيتم إنشاؤه وتخزينه إذا لم يكن موجودًا). يوفر العرض إنشاء واجهة المستخدم () ، والحصول على حاوية () ، والحصول على المكونات في GetComponent () ، و Display () ، و Recycle Trash () ؛ ResultWrapper و ReseTholder هي فقط لإنشاء وتخزين علامات الدردشة. التصميم كما يلي:
[Router.java ، view.java]
جميع واجهة المستخدم يرث JFrame وتنفيذ واجهة العرض. تحصل فئة تنفيذ المعالج على واجهة المستخدم المحددة من خلال جهاز التوجيه (يتم إرجاعها مباشرة إذا كانت موجودة ، وتنشئ وتخزن إذا لم يكن موجودًا). يوفر العرض إنشاء واجهة المستخدم () ، ويحصل على الحاوية () ، ويحصل على المكونات في GetComponent () ، وعروض العرض () ، وإعادة تدوير القمامة (). التنفيذ المحدد هو كما يلي:
/*** عرض المسار* Author yaolin*/جهاز توجيه الفئة العامة {خريطة ثابتة خاصة <string ، view> listroute = new hashmap <string ، view> () ؛ عرض ثابت عام getView (الفئة <؟> clazz) {view v = listroute.get (clazz.getName ()) ؛ if (v == null) {try {v = (view) class.forname (clazz.getName ()). newInstance () ؛ listroute.put (clazz.getName () ، v) ؛ } catch (استثناء e) {loggerUtil.error ("Create View فشل!" + E.GetMessage () ، e) ؛ }} return v ؛ }} /** * واجهات قانونية لجميع الواجهات * Author yaolin * */واجهة عامة عرض {/** * */عرض عام create () ؛ / ** * */ حاوية الحاوية العامة () ؛ / ** * @param key */ public jComponent getComponent (مفتاح السلسلة) ؛ / ** * */ public void display () ؛ / ** * */ public void trash () ؛ }[registerandloginview.java ، chatroomview.java]
بما أنني لا أرغب في كتابة واجهة المستخدم بنفسي ، فقد كتبت ببساطة واجهتين واجهة المستخدم هنا ، وهما واجهة التسجيل وتسجيل الدخول وواجهة الدردشة. فيما يلي واجهتان قبيحتان:
تسجيل واجهة تسجيل الدخول
واجهة الدردشة
فيما يلي الرموز المحددة لهاتين الواجهتين:
/*** تسجيل وتسجيل الدخول* Author yaolin*/registerLasslovinview من الفئة العامة يمتد JFrame عرض {private static final serialversionuid = 6322088074312546736l ؛ الإجراء الخاص بـ registerandLoginAction Action = New RecordandLoginAction () ؛ إنشاء منطقي ثابت خاص = خطأ ؛ Override public view create () {if (! create) {init () ؛ إنشاء = صحيح ؛ } إرجاع هذا ؛ } حاوية الحاوية العامة () {create () ؛ إرجاع getContentPane () ؛ } Override Public JComponent getComponent (مفتاح السلسلة) {return null ؛ } Override public void display () {setVisible (true) ؛ } Override public void trash () {dispose () ؛ } private void init () {// setSize (500 ، 300) ؛ setResible (false) ؛ setLocationRelativeto (NULL) ؛ // Container JPanel Pane = New JPanel () ؛ panel.setLayout (null) ؛ // مكون // username jlabel lbusername = new jlabel (i18n.text_username) ؛ lbusername.setBounds (100 ، 80 ، 200 ، 30) ؛ Final JTextfield tfusername = new JTextField () ؛ tfusername.setBounds (150 ، 80 ، 230 ، 30) ؛ Pane.add (lbusername) ؛ Pane.add (tfusername) ؛ // password Jlabel lbpassword = new JLabel (i18n.text_password) ؛ lbpassword.setBounds (100 ، 120 ، 200 ، 30) ؛ Final JPasswordfield PfPassword = New JPasswordfield () ؛ pfpassword.setBounds (150 ، 120 ، 230 ، 30) ؛ Pane.add (lbpassword) ؛ Pane.add (PfPassword) ؛ // btnregister jbutton btnregister = new jbutton (i18n.btn_register) ؛ btnregister.setBounds (100 ، 175 ، 80 ، 30) ؛ // Btnlogin Final Jbutton Btnlogin = New JButton (I18N.BTN_LOGIN) ؛ Btnlogin.setBounds (200 ، 175 ، 80 ، 30) ؛ // btncancel jbutton btnexit = new jbutton (i18n.btn_exit) ؛ btnexit.setBounds (300 ، 175 ، 80 ، 30) ؛ Pane.add (btnregister) ؛ Panel.add (btnlogin) ؛ Panel.add (btnexit) ؛ // الحدث pfassword.addkeylistener (جديد keyadapter () {public void keypressed (final keyevent e) {if ( Actionevent e) {if (stringutil.isempty (tfusername.gettext () Action.HandLereGister (tfusername.getText () ، سلسلة جديدة (pfassword.getpassword ())) ؛ stringutil.isempty (سلسلة جديدة (ppassword.getpassword ()) End of AddActionListener btnexit.addActionListener (New ActionListener () {public void actionperformed (final ActionEvent e) {system.exit (0) ؛ }}) ؛ // نهاية AddActionListener getContentPane (). add (لوحة) ؛ setDefaultCloseOperation (jframe.exit_on_close) ؛ }} /** * نافذة دردشة العميل * * Author yaolin */Class Public ChathoolView يمتد JFrame عرض {Private Static Final Long SerialVersionuid = -4515831172899054818L ؛ Static Static Final Final ListUsrlist = "ListUsrlist" ؛ السلسلة النهائية الثابتة العامة chattabbed = "chattabbed" ؛ إنشاء منطقي ثابت خاص = خطأ ؛ عمل chatroom action الخاص = جديد chatroomaction () ؛ Private JList <String> listUsrlist = null ؛ خاص jtabbedpane chattabbed = null ؛ Override public view create () {if (! create) {init () ؛ إنشاء = صحيح ؛ } إرجاع هذا ؛ } حاوية الحاوية العامة () {create () ؛ إرجاع getContentPane () ؛ } Override public jComponent getComponent (مفتاح السلسلة) {create () ؛ Switch (KEY) {case listUsrlist: return listUsrlist ؛ قضية تشاتاببيد: العودة تشاتاببيد. } إرجاع فارغ ؛ } Override public void display () {setVisible (true) ؛ } Override public void trash () {dispose () ؛ } public void init () {setTitle (i18n.text_app_name) ؛ SetSize (800 ، 600) ؛ setResible (false) ؛ setLocationRelativeto (NULL) ؛ setLayout (New BorderLayout ()) ؛ إضافة (CreateChatpanel () ، borderlayout.center) ؛ إضافة (createusRlistView () ، borderlayout.east) ؛ setDefaultCloseOperation (jframe.exit_on_close) ؛ } private jComponent CreateChatpanel () {// file celector final JfileChooser fileChooser = new JfileChooser () ؛ JPanel Panel = New JPanel (New 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 ()) ؛ // south - file jpanel middle = new jpanel (new BorderLayout ()) ؛ Middle.Add (New Jlabel () ، BorderLayout.Center) ؛ // فقط من أجل padding jbutton btnupload = new jbutton (i18n.btn_send_file) ؛ Middle.Add (btnupload ، borderlayout.east) ؛ South.Add (Middle ، BorderLayout.north) ؛ // South - Textarea Final Jtextarea tasend = New JTextarea () ؛ tasend.setCaretColor (color.blue) ؛ tasend.setMargin (insets جديدة (10 ، 10 ، 10 ، 10)) ؛ tasend.setrows (10) ؛ South.Add (Tasend ، BorderLayout.Center) ؛ // south - btn jpanel bottom = new jpanel (new BorderLayout ()) ؛ Bottom.add (New Jlabel () ، BorderLayout.Center) ؛ // فقط من أجل padding jbutton btnsend = new jbutton (i18n.btn_send) ؛ Bottom.Add (Btnsend ، BorderLayout.EAST) ؛ South.Add (Bottom ، BorderLayout.South) ؛ btnupload.addActionListener (new ActionListener () {public void actionperformed (final actionevent e) {if (! constantvalue.to_all.equals (chattabbed.getTitLeat (chatbed.getselectidex ()))) jfilechooser.approve_option) }}}}) ؛ btnsend.addActionListener (new ActionListener () {public void actionperformed (final actionevent e) {if (stringutil.isnotempty (tasend.getText ())) {action.send }) ؛ Panel.add (South ، BorderLayout.South) ؛ لوحة العودة ؛ } jComponent الخاص createusRlistView () {listusRlist = new JList <String> () ؛ ListUsrlist.setBorder (New LineBorder (color.blue)) ؛ ListUsrlist.setListData (سلسلة جديدة [] {constantValue.to_all}) ؛ ListUsrlist.setFixedCellwidth (200) ؛ ListUsrlist.setFixedCellheight (30) ؛ listUsrlist.addlisteselectionListener (new ListSeSeSeSelectionListener () {Override public void valuechanged (listselectionevent e) {// chat to if (chattabbed.indexoftab (listUsrlist.getSelectedValue ()) == -1 && leldusrlist.getselectedvalue () ! listusrlist.getSelectedValue (). متساوٍ (clientholder.getClient (). getFrom ())) {chattabbed.addtab (listusrlist.getSelectedValue () ، resultholder.get (listusrlist.getselectedvalue ()). getCrollpane ()) ؛ chattabbed.setseLectedIndex (chattabbed.indexoftab (listusrlist.getSelectedValue ()) ؛ الإرجاع ListUsrlist ؛ }}[registerandloginaction.java ، chatroomaction.java]
هنا ، تتم معالجة معالجة أحداث واجهة المستخدم بواسطة Action ، والتي تفصل ببساطة تصميم واجهة المستخدم واستجابة الأحداث. تتم معالجة أحداث registerandloginview بواسطة RecordandLoginAction ، ويتم معالجة أحداث Chat -GroundView بواسطة Chat RoomAction. التنفيذ المحدد هو كما يلي:
الفئة العامة registerAldLoginAction {public void handleregister (string username ، string password) {if (stringUtil.isempty (username) || stringutil.isempty (password)) {return ؛ } registerMessage Message = New RecordMessage () .SetUserName (اسم المستخدم) .setPassword (كلمة المرور) ؛ message.setFrom (اسم المستخدم) ؛ SendHelper.send (ClientHolder.getClient (). getSocket () ، message) ؛ } public void handlelogin (string username ، string password) {if (stringUtil.isempty (username) || stringutil.isempty (password)) {return ؛ } message loginMessage = new loginMessage () .SetUserName (اسم المستخدم) .setPassword (كلمة المرور) ؛ message.setFrom (اسم المستخدم) ؛ SendHelper.send (ClientHolder.getClient (). getSocket () ، message) ؛ }} هناك فئتان آخران لتصميم واجهة المستخدم ، وهما Resultholder و ResultWrapper. ResultWrapper و ReseTholder هي فقط لإنشاء وتخزين علامات الدردشة. التنفيذ المحدد هو كما يلي:
الطبقة العامة resultwrapper {private jscrollpane scrollpane ؛ خاص JTextarea Textarea ؛ public resultwrapper (jscrollpane scrollpane ، jtextarea textarea) {this.scrollpane = scrollpane ؛ this.textarea = textarea ؛ } public jscrollpane getScrollPane () {return scrollpane ؛ } public void setScrollPane (jscrollpane scrollpane) {this.scrollpane = scrollpane ؛ } public jtextarea getTextarea () {return textarea ؛ } public void setTextarea (Jtextarea textarea) {this.textarea = textarea ؛ }} resultholder الفئة العامة {خريطة ثابتة خاصة <string ، resultwrapper> listresultwrapper = new hashmap <string ، resultWrapper> () ؛ Public Static Void PUT (مفتاح السلسلة ، WRESTWRAPPER WRAPTER) {listresultwrapper.put (المفتاح ، التفاف) ؛ } public static resultwrapper get (string key) {resultWrapper clospper = listresultwrapper.get (key) ؛ if (wrapper == null) {wrapper = create () ؛ ضع (مفتاح ، غلاف) ؛ } غلاف العودة ؛ } private static resultwrapper create () {jtextarea resulttextarea = new jtextarea () ؛ resulttextarea.setedable (false) ؛ resultTextarea.setBorder (New 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 * */public class niloaychat {public static void main (string [] args) {view v = router.getView (registerandloginview.class) .create () ؛ حاول {v.display () ؛ عميل العميل = عميل جديد (DefaultCallback () جديد) ؛ client.start () ؛ ClientHolder.setClient (Client) ؛ } catch (ioException e) {joptionpane.showmessagedialog (v.container () ، e.getMessage ()) ؛ }}} عنوان التنزيل التجريبي: العرض التوضيحي
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.