Написано ранее:
В прошлые выходные мне потребовалось некоторое время, чтобы записать первоначальный дизайн и подробный дизайн на стороне сервера простой программы чата, которую я написал. Я наконец ждал сертификата с мягким экзаменом, который я сдал до окончания учебы во вторник, а затем провел день сверхурочной работы. Сегодня оказалась пятница, и я планировал записать подробный дизайн клиента и общего модуля, потому что я буду занят другими вещами, начиная с этих выходных.
дизайн:
Дизайн клиента в основном разделен на две части, а именно: конструкция модуля сокета и дизайн, связанный с пользовательским интерфейсом.
Дизайн связи клиента:
Дизайн здесь на самом деле похож на дизайн сервера. Разница в том, что сервер получает пакеты с сердцебиениями, в то время как клиент отправляет пакеты с сердцебиениями. Поскольку клиент общается только с одним сервером (связь между клиентами также распространяется сервером), для обработки этих двух вещей используется только пул потоков 2 (2). Соответствующие классы обработки получают и Keepalivedog. Когда полученная часть инициализируется, обратный вызов отправляется в качестве обратного вызова, что клиент получает сообщение сервера. Реализация обратного вызова по умолчанию - DefaultCallback. DefaultCallback распространяется на различные обработчики через HF в соответствии с различными событиями. Владелец клиента хранит текущую информацию клиента. Дизайн заключается в следующем:
Конкретная реализация модуля связи сокета:
[Client.java]
Клиент - это вход в клиент для подключения к серверу. Чтобы создать клиента, вам необходимо указать обратный вызов в качестве обратного вызова, когда клиент получает сообщение сервера. Затем метод клиента start () запускает прослушивание сервера (PresectiveListener). Когда приемный вариант получает данные, отправленные сервером, метод обратного вызова (обратный вызов) вызывается для его обработки. В то же время клиенту также необходимо отправить пакет Heartbeat, чтобы уведомить сервер, о том, что он все еще подключен к серверу. Пакет Heartbeat Packet хранится клиентом. Alive () запускается и реализуется Keepalivedog; Эти два шага выполняются пулом потоков NewFixedThreadPool (2) с фиксированным размером 2. Может быть более разумным использовать NewFixedThreadpool (1) и NewschedThreadpool (1) здесь, чтобы справиться с ним, потому что пакет Heartbeat регулярно отправляется, и это так, как сервер внедряет его (эта подключенная корректировка). Конкретный код клиента заключается в следующем (два других метода выставлены здесь для получения сокета и пользователя, к которому принадлежит текущая розетка):
/** * Клиент * @Author YAOLIN * */Public Class Client {Private Final Socket Socket; частная строка из; Частный окончательный бассейн исполнителей; частный финальный обратный вызов; public Client (обратный вызов обратного вызова) бросает ioException {this.socket = new Socket (constantValue.server_ip, constantValue.server_port); this.pool = executors.newfixedthreadpool (2); this.callback = обратный вызов; } public void start () {pool.execute (new PresectiveLener (сокет, обратный вызов)); } public void KeepAlive (String от) {this.from = от; pool.execute (new keepalivedog (сокет, от)); } public stocket getSocket () {return Socket; } public String getFrom () {return из; }}[Keepalivedog.java]
После того, как клиент устанавливает соединение с сервером (эта программа относится к успешному входу в систему, поскольку сокет клиента будет управляться ночером сервера после успешного входа), необходимо отправлять пакет Heartbeat на сервер каждый раз, чтобы сообщать серверу, что он все еще находится в контакте с сервером, в противном случае сервер будет отказываться от сокета без взаимодействия после периода времени (см. В блоке сервера). Код Keepalivedog реализован следующим образом (он может быть скорректирован в NewschedledThreadpool (1) позже, поэтому код здесь также будет скорректирован):
/*** KeepALivedOg: сообщите сервер, что этот клиент работает; * * @author yaolin */public class Keepalivedog реализует Runnable {Private Final Socket Socket; частная финальная строка из; public keepalivedog (сокет сокета, строка от) {this.socket = socket; this.from = от; } @Override public void run () {while (socket! = Null &&! Socket.isclosed ()) {try {printwriter out = new PrintWriter (socket.getOutputStream ()); Alivemessage message = new alivemessage (); message.setfrom (от); out.println (json.tojson (сообщение)); out.flush (); Thread.sleep (constantValue.keep_alive_period * 1000); } catch (Exception e) {loggerutil.error ("Отправка клиента отправлена. }}}}[ReceveliveListener.java]
Метод start () клиента запускает прослушивание сервера и реализуется приемным. После получения сообщения с сервера, PresectiveListener вызовет метод DoWork () обратного вызова, чтобы позволить обратному обращению обрабатывать конкретную бизнес -логику. Следовательно, приемный вариант отвечает только за прослушивание сообщений на сервере, а конкретная обработка обрабатывается обратным вызовом. Здесь следует упомянуть, что, когда тип сообщения является типом файла, он будет спать для настройки интервала выполнения, так что Dowork в обратном вызове может прочитать поток файла на сервер вместо непосредственного ввода следующего цикла. Дизайн здесь похож на сервер. Конкретный код реализации приема вывода заключается в следующем:
открытый класс получает реализацию Runnable {Private Final Socket Socket; частный финальный обратный вызов; Public PretingWeeliveener (сокет сокета, обратный вызов) {this.socket = socket; this.callback = обратный вызов; } @Override public void run () {if (socket! = Null) {while (! Socket.isclosed ()) {try {inputStream IS = socket.getInputStream (); String line = null; Stringbuffer sb = null; if (is.available ()> 0) {bufferedReader bufr = new BufferedReader (new InputStreamReader (IS)); sb = new StringBuffer (); while (is.available ()> 0 && (line = bufr.readline ())! = null) {sb.append (line); } LoggerUtil.trach ("eceat [" + sb.tostring () + "] at" + new Date ()); Callback.dowork (Socket, sb.toString ()); BaseMessage message = json.parseobject (sb.tostring (), basemessage.class); if (message.getType () == messageType.file) {// пауза для получения файла loggerutil.trach ("client: pause для получения файла"); Thread.sleep (constantValue.message_period); }} else {thread.sleep (constantValue.message_period); }} catch (Exception e) {loggerutil.error ("client отправить сообщение не удалось!" + e.getMessage (), e); }}}}}}}[Callback.java, defaultcallback.java]
Из вышесказанного мы видим, что обработка сообщений клиента является обратным вызовом, а его обратный вызов - просто интерфейс. Все реализации обратного вызова реализуют интерфейс для обработки сообщений в соответствии с их потребностями. Здесь реализация обратного вызова по умолчанию является DefaultCallback. DefaultCallback обрабатывает только три типа сообщений, а также сообщения чата, сообщения файлов и возвращаемые сообщения. Для сообщений чата DefaultCallback получит соответствующий интерфейс через маршрутизатор маршрутизатора в пользовательском интерфейсе (для получения подробной информации см. Ниже приведен в дизайн пользовательского интерфейса), а затем отобразит сообщение в соответствующем поле чата; Для сообщений файлов DefaultCallback напишет файл в путь, указанный в конфигурации (файл получен без разрешения пользователя здесь. Этот дизайн не очень дружелюбен, так что на данный момент); Для возвращающихся сообщений DefaultCallback будет вызвана для разных обработчиков в соответствии с ключом в ответном сообщении. Конкретный код заключается в следующем:
Callback public Interface {public void dowork (сервер сокета, данные объекта); } открытый класс defaultCallback реализует обратный вызов {@Override public void dowork (Socket Server, Data Data) {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: HandlerTurnMessage (data); перерыв; }}} private void handleChatmessage (объект Data) {Chatmessage m = json.parseObject (data.tostring (), Chatmessage.class); String tabkey = m.getfrom (); // от jcomponent comp = router.getView (чат. if (comp exanceof jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.indexoftab (tabkey); if (index == -1) {tab.addtab (tabkey, oultholder.get (tabkey) .getscrollpane ()); } JTextArea textARea = resultholder.get (tabkey) .getTextArea (); textarea.settext (new stringbuffer () .append (textarea.gettext ()). Append (System.LineseParator ()). Append (System.LineseParator ()) .Append ("[") .append (m.getOwner (). Append ("]:") .Append (System.LineseParator (). .toString ()); // прокручивать внизу textarea.setCareTposition (textarea.getText (). Length ()); }} private void harderfilemessage (сервер сокетов, данные объекта) {filemessage message = json.parseobject (data.tostring (), falemessage.class); if (message.getSize ()> 0) {outputStream OS = null; try {if (server! = null) {inputStream IS = server.getInputStream (); Файл dir = новый файл (constantValue.client_receive_dir); if (! dir.exists ()) {dir.mkdirs (); } os = new FileOutputStream (новый файл (pathutil.combination (constantValue.client_receive_dir, new Date (). getTime () + message.getName ()))); int total = 0; while (! server.isclosed ()) {if (is.available ()> 0) {byte [] buff = new byte [constantvalue.buff_size]; int len = -1; while (is.available ()> 0 && (len = is.read (buff))! = -1) {os.write (buff, 0, len); Всего += Лен; Loggerutil.debug ("получить бафф [" + len + "]"); } os.flush (); if (total> = message.getSize ()) {loggerutil.info ("получить Buff [OK]"); перерыв; }}}}}} catch (Exception e) {loggerUtil.error ("Приема файла не удалось!" + e.getMessage (), e); } наконец {if (os! = null) {try {os.close (); } catch (исключение игнорировать) {} os = null; }}}} private void handlerTurnMessage (объект Data) {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); перерыв; case key.tip: hf.gethandler (key.tip) .handle (data); перерыв; }}}}[Handler.java, hf.java, listuserhdl.java ...]
Компонент обработчика отвечает за обработку сообщений типа ответного сообщения сервера. DefaultCallback распределяет сообщения различным обработчикам в соответствии с разными ключами. Это также простой заводской компонент. Это похоже на данные, полученные сервером. Полная классная диаграмма заключается в следующем:
Код для этого раздела приведен ниже. Чтобы сократить пространство, собирается весь код, реализованный Handler.
обработчик публичного интерфейса {открытый объект Handle (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 (); } return null; }} Общедоступный класс ListUserHdl реализует обработчик {@Override public Object Handle (Object obj) {if (obj! = null) {try {returnmessage rm = json.parseObject (obj.toString (), returnmessage.class); if (rm.issuccess () && rm.getContent ()! = null) {clientlisluserdto dto = json.parseObject (rm.getContent (). toString (), clientlidSerdto.class); Jcomponent comp = router.getView (catroomView.class) .getComponent (чат. if (comp exanceof jlist) {@suppresswarnings ("unchecked") // jlist <string> listusrlist = (jlist <string>) comp; List <string> listuser = new LinkedList <string> (); listUser.addall (dto.getLideSer ()); Collections.sort (listuser); listUser.add (0, constantValue.to_all); listusrlist.setlistdata (listuser.toarray (new String [] {})); }}} catch (Exception e) {loggerutil.Error ("Руководство ListUSR не удалось!" + e.getMessage (), e); }} return null; }} Открытый класс loginhdl реализует Handler {@Override public Object Handle (Object obj) {if (obj! = null) {try {returnmessage rm = json.parseobject (obj.toString (), returnmessage.class); if (rm.issuccess ()) {router.getView (RegisterAndLoginView.class) .trash (); Router.getView (чат. Clientholder.getClient (). KeepAlive (rm.getto ()); // сохранить ...} else {контейнер -контейнер = router.getView (registerAndloginview.class) .container (); if (container! = null) {// show error joptionpane.showmessagedialog (контейнер, rm.getmessage ()); }}} catch (Exception e) {loggerutil.Error ("Отказ от входа в систему!" + e.getMessage (), e); }} return null; }} открытый класс notifyhdl реализует обработчик {@Override public Object Handle (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 (catroomView.class) .getComponent (чат. if (comp exanceof jlist) {@suppresswarnings ("unchecked") // jlist <string> listusrlist = (jlist <string>) comp; List <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 (new String [] {})); }}} catch (Exception e) {loggerutil.Error ("Ручка nofity не удалась!" + e.getMessage (), e); }} return null; } частный список <string> 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)); }} return List; }} открытый класс Registerhdl реализует Handler {@Override public Object Handle (Object obj) {if (obj! = null) {try {returnmessage rm = json.parseObject (obj.toString (), returnmessage.class); Контейнер -контейнер = 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 реализует Handler {@Override public Object Handle (Object obj) {if (obj! = null) {try {returnmessage m = json.parseObject (obj.toString (), returnmessage.class); if (m.issuccess () && m.getContent ()! = null) {string tabkey = m.getFrom (); String Tip = M.GetContent (). ToString (); Jcomponent comp = router.getView (catroomview.class) .getComponent (чат. if (comp exanceof jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.indexoftab (tabkey); if (index == -1) {tab.addtab (tabkey, oultholder.get (tabkey) .getscrollpane ()); } JTextArea textARea = resultholder.get (tabkey) .getTextArea (); TextAREA.Settext (new StringBuffer () .Append (textARea.getText ()). Append (System.LineseParator ()). Append (System.LineseParator ()) .Append ("[") .append (m.getOwner (). Append ("]:") .Append (System.LineseParator (). // прокручивать внизу textarea.setCareTposition (textarea.getText (). Length ()); }}} catch (Exception e) {loggerutil.Error ("Хранение сбой!" + e.getMessage (), e); }} return null; }} Существует еще один класс для модуля связи с сокетом, то есть владельца клиента, который используется для хранения текущего клиента, что аналогично сокетолдеру на сервере.
/** * @author yaolin */public Class Client Holder {public State Client Client; public Static Client getClient () {return Client; } public static void setClient (клиент клиент) {clientholder.client = client; }}Конкретная реализация модуля пользовательского интерфейса:
Вышеупомянутые записывают дизайн модуля связи сокета. Далее я записываю модуль дизайна пользовательского интерфейса. Я не планирую писать интерфейс самостоятельно. В конце концов, написание слишком уродливое, поэтому я могу попросить одноклассников или друзей помочь мне сбить его с собой позже. Поэтому я передаю обработку событий пользовательского интерфейса на действие, чтобы справиться с ним, и просто отделяю дизайн пользовательского интерфейса и реакцию событий. Все интерфейсы наследуют JFrame и реализуйте интерфейс представления. Класс реализации вышеупомянутого обработчика получается через маршрутизатор (он будет возвращен непосредственно, если он будет существовать, и он будет создан и сохранен, если его не существует). В представлении предоставляется пользовательский интерфейс (), получить контейнер (), получить компоненты в пользовательском интерфейсе getComponent (), Display Display () и Recycle Trash (); ResultWrapper и Resultholder - просто для создания и хранения вкладок чата. Дизайн заключается в следующем:
[Router.java, view.java]
Все интерфейсы наследуют JFrame и реализуйте интерфейс представления. Класс реализации обработчика получает указанный пользовательский интерфейс через маршрутизатор (он возвращается напрямую, если он существует, и создает и хранит, если его не существует). Представление предоставляет UI Creation (), получает Container () и получает компоненты в пользовательском интерфейсе getComponent (), Display () и переработает Trash (). Конкретная реализация заключается в следующем:
/*** Просмотр маршрута* @author yaolin*/public class router {private static map <string, view> listroute = new hashmap <string, view> (); Public Static View GetView (Class <?> Clazz) {view v = listroute.get (clazz.getName ()); if (v == null) {try {v = (view) class.forname (clazz.getName ()). newInstance (); listroute.put (clazz.getName (), v); } catch (Exception e) {loggerutil.error ("create view не удастся!" + e.getMessage (), e); }} return v; }} /** * Канонические интерфейсы для всех интерфейсов * @author yaolin * */public interface view {/** * */public view create (); / ** * */ Public Container Container (); / ** * @param key */ public jcomponent getComponent (String Key); / ** * */ public void display (); / ** * */ public void trash (); }[RegisterAndLoginView.java, ChatroomView.java]
Поскольку я не хочу писать интерфейс и интерфейс интерфейса чата и интерфейс чата и интерфейс чата. Вот два уродливых интерфейса:
Зарегистрируйте интерфейс входа в систему
Интерфейс чата
Ниже приведены конкретные коды для этих двух интерфейсов:
/*** Зарегистрировать и входить в систему* @author yaolin*/public class RegisterandLoginView extends jframe реализует View {private Static Long Long Serialversionuid = 6322088074312546736L; Частный окончательный регистр Частный статический логический Create = false; @Override public view create () {if (! Create) {init (); Create = true; } вернуть это; } public Container Container () {create (); return getContentPane (); } @Override public jcomponent getComponent (String Key) {return null; } @Override public void display () {setVisible (true); } @Override public void trash () {dispose (); } private void init () {// attribute setSize (500, 300); setReSizable (false); setlocationRelativeTo (null); // контейнер jPanel панель = new jPanel (); panel.setlayout (null); // компонент // имя пользователя jlabel lbusername = new jlabel (i18n.text_username); lbusername.setbounds (100, 80, 200, 30); Final 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); Final JPasswordfield pfpassword = new jpassword (); 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 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); панель.add (btnregister); панель.add (btnlogin); panel.add (btnexit); // Event pfpassword.addkeyListener (new KeyAdapter () {public void keypressed (final KeyEvent e) {if (e.getKeyCode () == keyEvent.vk_enter) btnlogin.doclick ();}}); // end ofdekkeylister btnregister. ActionEvent e) {if (stringUtil.isempty (tfusername.gettext ()) || stringutil.isempty (new String (pfpassword.getpassword ())) {joptionpane.showmessageedog (getContentPane (), i18n.info_read_empty_data); action.handleregister (tfusername.gettext (), new String (pfpassword.getpassword ()); String (pfpassword.getPassword ())) {joptionPane.showmessageDialog (getContentPane (), i18n.info_login_empty_data); btnexit.addactionListener (new ActionListener () {public void ActionPerformed (Final ActionEvent E) {System.Exit (0); }}); // end of AddactionListener getContentPane (). add (panel); setDefaultCloseoPeration (jframe.exit_on_close); }} /** * Клиентское окно чата * * @author yaolin */public Class ChatroomView Extends JFrame реализует View {Private Static Final Long SerialVersionUID = -4515831172899054818L; public Static Final String ListUsrlist = "listUsrlist"; public Static Final String Chattabbed = "Chattabbed"; Частный статический логический Create = false; частное чат действия Action = New ChatroomAction (); private jlist <string> listusrlist = null; Частный jtabbedpane Chattabbed = null; @Override public view create () {if (! Create) {init (); Create = true; } вернуть это; } public Container Container () {create (); return getContentPane (); } @Override public JComponent getComponent (String Key) {create (); switch (key) {case listusrlist: return listusrlist; Case Chattabbed: вернуть Chattabbed; } return null; } @Override public void display () {setVisible (true); } @Override public void trash () {dispose (); } public void init () {settitle (i18n.text_app_name); setSize (800, 600); setReSizable (false); setlocationRelativeTo (null); setlayout (new Borderlayout ()); add (createChatPanel (), borderlayout.center); add (createUsrlistView (), borderlayout.east); setDefaultCloseoPeration (jframe.exit_on_close); } private jcomponent createChatPanel () {// Селектор файлов final jfilechooser fileChooser = new jfilechooser (); JPanel Panel = new JPanel (new BorderLayout ()); // Центр 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); // просто для накладки jbutton btnupload = new jbutton (i18n.btn_send_file); Middle.add (btnupload, borderlayout.east); South.Add (Middle, Borderlayout.north); // Юг - Textarea final 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 (new borderlayout ()); Bottom.add (new jlabel (), borderlayout.center); // просто для накладки jbutton btnsend = new jbutton (i18n.btn_send); Bottom.add (Btnsend, Borderlayout.east); South.Add (внизу, Borderlayout.South); btnupload.addactionListener (new ActionListener () {public void actionPerformed (final actionEvent e) {if (! constantValue.to_all.equals (Chattabbed.getTitLeat (Chattabbed.getSelectedIndex ()))) {int returnVal = fileCoSer.showopendialog (chathroom); Jfilechooser.approve_option) {file file = filechooser.getSelectedFile (); }}}}); btnsend.addactionListener (new ActionListener () {public void actionPerformed (final actionEvent e) {if (stringUtil.isnotempty (tasend.getText ())) {action.send (Chattabbed.getTitLeat (Chattabbed.getSelecteDex ()), tasend.getText (); tasEndtexte. }); панель. Адд (Юг, Borderlayout.South); возвращаемая панель; } private jcomponent createUsrListView () {listUsrlist = new jlist <string> (); listusrlist.setborder (new Lineborder (color.blue)); listusrlist.setlistdata (new String [] {constantValue.to_all}); listusrlist.setfixedcellwidth (200); listusrlist.setfixedcellheight (30); ListUsrlist.addlistselectionListener (new ListSelectionListener () {@Override public void valueChanged (listselectionevent e) {// чат к if (Chattabbed.indexoftab (listusrlist.getSelectedValue ()) == -1 && listUsrList.getSelectedValue ()! ! listusrlist.getSelectedValue (). equals (client holder.getClient (). getFrom ())) {Chattabbed.Addtab (listusrlist.getSelectedValue (), resultholder.get (listusrlist.getSelectedValue (). getScrollPane ()); chattabbed.setselectedIndex (chattabbed.indexoftab (listusrlist.getSelectedValue ())); return listusrlist; }}[RegisterAndLoginAction.java, ChatroomAction.java]
Здесь обработка событий пользовательского интерфейса обрабатывается действием, что просто разделяет дизайн пользовательского интерфейса и реакцию событий. События RegisterandLoginView обращаются с помощью RegisterAndLoginAction, а события чата обрабатываются чатом. Конкретная реализация заключается в следующем:
открытый класс registerandLoginaction {public void handlereGister (string username, string password) {if (stringUtil.isempty (username) || stringutil.isempty (пароль)) {return; } RegisterMessage Message = new RegisterMessage () .SetUSERNAME (имя пользователя) .SetPassword (пароль); message.setfrom (имя пользователя); Sendhelper.send (clientholder.getClient (). Getsocket (), сообщение); } public void handleLogin (string username, string password) {if (stringUtil.isempty (username) || stringutil.isempty (password)) {return; } LoginMessage Message = new LogInMessage () .SetUSERNAME (имя пользователя) .SetPassword (пароль); message.setfrom (имя пользователя); Sendhelper.send (clientholder.getClient (). Getsocket (), сообщение); }} Есть два других класса для дизайна пользовательского интерфейса, а именно, а также Resultholder и ResultWrapper. ResultWrapper и Resultholder - просто для создания и хранения вкладок чата. Конкретная реализация заключается в следующем:
открытый класс 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 (String Key, ResultWrapper warper) {listresultwrapper.put (key, warper); } public static resultWrapper get (String Key) {resultWrapper warper = listresultWrapper.get (key); if (warper == null) {warper = create (); положить (ключ, обертка); } return wrapper; } 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.seterticalscrollbarpolicy (scrollpaneconstants.vertical_scrollbar_as_needed); ResultWrapper warper = new ResultWrapper (ScrollPane, ResultTextArea); вернуть обертку; }} Последний дан, запись, в которую запускается клиент:
/** * * @author yaolin * */public class niloaychat {public static void main (string [] args) {view v = router.getView (registerandLoginView.class) .create (); try {v.display (); Client Client = новый клиент (новый defaultCallback ()); client.start (); Clientholder.setClient (клиент); } catch (ioException e) {joptionpane.showmessageDialog (v.container (), e.getmessage ()); }}} Демонстрация Адрес: демонстрация
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.