Écrit avant:
Le week-end dernier, j'ai pris un certain temps pour enregistrer la conception initiale et la conception détaillée du côté serveur d'un simple programme de chat de socket que j'ai écrit. J'ai finalement attendu le certificat d'examen doux que j'ai pris avant mon diplôme mardi, puis j'ai passé la journée des heures supplémentaires. Aujourd'hui s'est avéré vendredi, et j'avais prévu d'enregistrer la conception détaillée du client et du module commun, car je serai occupé avec d'autres choses à partir de ce week-end.
conception:
La conception du client est principalement divisée en deux parties, à savoir la conception du module de communication à socket et la conception liée à l'interface utilisateur.
Conception de communication du socket client:
La conception ici est en fait similaire à la conception du serveur. La différence est que le serveur reçoit des paquets de battements de cœur, tandis que le client envoie des paquets de battements cardiaques. Étant donné que le client ne communique qu'avec un seul serveur (la communication entre les clients est également distribuée par le serveur), seul un pool de threads de taille 2 est utilisé pour gérer ces deux choses (newFixEdThreadPool (2)). Les classes de traitement correspondantes sont ReceiveListERner et Keepalivedog. Lorsque le receiouslener est initialisé, un rappel est envoyé sous forme de rappel au client reçoit le message du serveur. L'implémentation par défaut du rappel est defaultCallback. DefaultCallback est distribué à différents gestionnaires via HF en fonction de différents événements. Le titulaire du client stocke les informations actuelles du client. La conception est la suivante:
La mise en œuvre spécifique du module de communication de socket:
[Client.java]
Le client est l'entrée du client pour se connecter au serveur. Pour créer un client, vous devez spécifier un rappel comme rappel lorsque le client reçoit le message du serveur. Ensuite, la méthode start () du client démarre l'écoute du serveur (ReceiveListERner). Lorsque le receiveListener reçoit les données envoyées par le serveur, la méthode de rappel (rappel) est appelée pour la traiter. Dans le même temps, le client doit également envoyer un paquet de battements cardiaques pour informer le serveur qu'il est toujours connecté au serveur. Le paquet de rythmes cardiaques est conservé par le client. Alive () est démarré et mis en œuvre par Keepalivedog; Ces deux étapes sont exécutées par un pool de fils de newFixEdThreadpool (2) avec une taille fixe de 2. Il peut être plus raisonnable d'utiliser un NewFixEdThreadPool (1) et NewscheduleDThreadPool (1) ici pour le gérer, car le package de battements de cœur est envoyé régulièrement, et c'est ainsi que le serveur l'impulse (cet ajustement sous-précédent). Le code spécifique du client est le suivant (les deux autres méthodes sont exposées ici pour obtenir la prise et l'utilisateur à laquelle appartient la prise actuelle):
/ ** * Client * @author yaolin * * / public class Client {private final socket socket; chaîne privée de; Pool de service d'exécution final privé; rappel final privé; Client public (rappel de rappel) lève ioException {this.socket = new socket (constantValue.server_ip, constantValue.server_port); this.pool = exécutors.newFixEdThreadpool (2); this.callback = callback; } public void start () {Pool.Execute (new ReceiListERner (socket, rappel)); } public void keepalive (string from) {this.from = from; Pool.Execute (new Keepalivedog (socket, from)); } public socket getSocket () {return socket; } public String getFrom () {return de; }}[Keepalivedog.java]
Après que le client établit une connexion avec le serveur (ce programme fait référence à la connexion réussie, car la prise du client sera gérée par le socketholder du serveur après le succès de la connexion), il est nécessaire d'envoyer un paquet de battements cardiaques au serveur à chaque fois pour indiquer au serveur qu'il est toujours en contact avec le serveur, sinon le serveur mettra les détails). Le code de Keepalivedog est mis en œuvre comme suit (il peut être ajusté à NewscheduleDThreadPool (1) plus tard, de sorte que le code ici sera également ajusté):
/ ** * keepalivedog: Tell Server Ce client est en cours d'exécution; * * @author yaolin * / classe publique Keepalivedog implémente Runnable {socket final private final; chaîne finale privée de; public keepalivedog (socket socket, chaîne de) {this.socket = socket; this.from = de; } @Override public void run () {while (socket! = Null &&! Socket.isclosed ()) {try {printwriter out = new printwriter (socket.getOutputStream ()); Message AliveMessage = new AliveMessage (); message.setFrom (from); out.println (json.tojson (message)); out.flush (); Thread.sleep (constantevalue.keep_alive_period * 1000); } catch (exception e) {LoggerUtil.Error ("Client Send Message a échoué!" + e.getMessage (), e); }}}}[ReceiveListner.java]
La méthode start () du client démarre l'écoute du serveur et est implémentée par le receiveListERner. Après avoir reçu le message du serveur, le ReceiListERner rappellera la méthode Dowork () du rappel pour permettre au rappel de gérer la logique métier spécifique. Par conséquent, le receiveListERner n'est responsable que de l'écoute des messages sur le serveur et le traitement spécifique est géré par rappel. Il convient de mentionner ici que lorsque le type de message est un type de fichier, il dormira pour configurer l'intervalle d'exécution, afin que la Dowork en rappel puisse lire le flux de fichiers vers le serveur au lieu de saisir directement la boucle suivante. La conception ici est similaire au serveur. Le code d'implémentation spécifique de ReceiveListERner est le suivant:
La classe publique ReceIveListERNER implémente Runnable {privative socket final socket; rappel final privé; public receiveListERner (socket socket, rappel rappel) {this.socket = socket; this.callback = callback; } @Override public void run () {if (socket! = Null) {while (! Socket.isclosed ()) {try {inputStream is = socket.getInputStream (); Chaîne 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 ("reçoivent [" + sb.toString () + "] à" + new Date ()); callback.dowork (socket, sb.toString ()); Message BasEnsage = JSON.ParseObject (SB.TOSTRING (), BasEMessage.class); if (message.getType () == MessageType.File) {// Pause pour recevoir le fichier LoggerUtil.trach ("Client: Pause pour recevoir le fichier"); Thread.sleep (constantevalue.message_period); }} else {thread.sleep (constantValue.Message_period); }} catch (exception e) {LoggerUtil.Error ("Client Send Message a échoué!" + e.getMessage (), e); }}}}}}}[Callback.java, defaultCallback.java]
D'après ce qui précède, nous pouvons voir que le traitement des messages par le client est un rappel de rappel, et son rappel n'est qu'une interface. Toutes les implémentations de rappel implémentent l'interface pour traiter les messages en fonction de leurs besoins. Ici, l'implémentation par défaut du rappel est defaultCallback. DefaultCallback ne traite que trois types de messages, à savoir les messages de chat, les messages de fichier et les messages de retour. Pour les messages de chat, DefaultCallback obtiendra l'interface correspondante via l'itinéraire du routeur dans l'interface utilisateur (voir la conception de l'interface utilisateur ci-dessous pour plus de détails), puis afficher le message dans la boîte de chat correspondante; Pour les messages de fichier, DefaultCallback écrira le fichier sur le chemin spécifié dans la configuration (le fichier est reçu sans l'autorisation de l'utilisateur ici. Cette conception n'est pas très conviviale, donc pour l'instant); Pour les messages de retour, DefaultCallback sera appelé à différents gestionnaires en fonction de la clé du message de retour. Le code spécifique est le suivant:
Rappel d'interface publique {public void Dowork (serveur de socket, données d'objet); } classe publique defaultCallback implémente le rappel {@Override public void Dowork (socket server, objet data) {if (data! = null) {BasEmsage Message = JSON.ParseObject (data.toString (), BasEmsage.Class); switch (message.getType ()) {case messageType.chat: handleChatMessage (data); casser; Case MessageType.File: manchefileMessage (serveur, données); casser; Case MessageType.Return: HandleReturnMessage (données); casser; }}} private void handleChatMessage (Données d'objet) {ChatMessage m = json.parseObject (data.toString (), chatMessage.class); String tabkey = m.getFrom (); // de jComponent comp = router.getView (ChatroomView.class) .getComponent (ChatroomView.Chattabbed); if (compinestof 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 () .Apnd (textarea.getText ()). APPEND (System.LineSeparator ()). APPEND (System.LineSeparator ()) .Apnd ("[") .Apnd (m.getowner ()). .ToString ()); // Faites défiler vers TextArea.SetCaretPosition (TextArea.GetText (). Length ()); }} private void handleFileMessage (socket serveur, données d'objet) {message fileMessage = json.parseObject (data.toString (), fileMessage.class); if (message.getSize ()> 0) {outputStream os = null; essayez {if (server! = null) {inputStream est = server.getInputStream (); File dir = nouveau fichier (constantevalue.client_receive_dir); if (! dir.exists ()) {dir.mkDirs (); } os = new FileOutputStream (nouveau fichier (pathutil.combination (constantevalue.client_receive_dir, new Date (). GetTime () + message.getName ()))); int total = 0; while (! server.isclosed ()) {if (is.available ()> 0) {byte [] buff = new byte [constantevalue.buff_size]; int len = -1; while (is.available ()> 0 && (len = is.read (buff))! = -1) {os.write (buff, 0, len); Total + = len; LoggerUtil.debug ("recevoir buff [" + len + "]"); } os.flush (); if (total> = message.getSize ()) {loggerUtil.info ("recevoir buff [ok]"); casser; }}}}}} catch (exception e) {LoggerUtil.Error ("Fichier de réception a échoué!" + e.getMessage (), e); } enfin {if (os! = null) {try {os.close (); } catch (exception ignore) {} os = null; }}}} private void handlereTurnMessage (objet data) {returnMessage m = json.parseObject (data.toString (), returnMessage.class); if (stringUtil.isnotempty (m.getKey ())) {switch (m.getKey ()) {case key.notify: // informer le client pour mettre à jour la liste USR hf.gethandler (key.notify) .handle (data); casser; Case key.login: hf.gethandler (key.login) .handle (data); casser; Case Key.Register: Hf.Gethandler (Key.Register) .Handle (données); casser; case key.listUser: hf.Gethandler (key.listUser) .Handle (data); casser; Case key.tip: hf.Gethandler (key.tip) .handle (data); casser; }}}}[Handler.java, hf.java, listUserhdl.java ...]
Le composant de gestionnaire est responsable du traitement des messages du type de message de retour du serveur. DefaultCallback distribue des messages à différents gestionnaires en fonction de différentes clés. Il s'agit également d'un composant d'usine simple. Il est similaire aux données reçues par le serveur. Le diagramme de classe complet est le suivant:
Le code de cette section est donné ci-dessous. Afin de réduire l'espace, tout le code implémenté par Handler est collecté.
Public Interface Handler {Public Object Handle (Object OBJ); } classe publique 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; }} classe publique listUserHDL implémente Handler {@Override Public Object Handle (objet obj) {if (obj! = null) {try {returnMsage rm = json.parseObject (obj.toString (), returnMessage.class); if (rm.isSuccress () && rm.getContent ()! = null) {clientListUserdTo dto = json.parseObject (rm.getContent (). toString (), clientListUserDTO.class); JComponent comp = router.getView (ChatroomView.class) .getComponent (ChatroomView.ListUSRList); if (compinestof jlist) {@SuppressWarnings ("Unchecked") // jList <string> listUsrlist = (jlist <string>) compt; List <string> listUser = new LinkedList <string> (); listUser.addall (dto.getListUser ()); Collection.Sort (listUser); listUser.add (0, constantValue.to_all); listUsrlist.setListData (listUser.toArray (new String [] {})); }}} catch (exception e) {LoggerUtil.Error ("Handle listUsr a échoué!" + e.getMessage (), e); }} return null; }} La classe publique LoginHdl implémente Handler {@Override Public Object Handle (objet obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); if (rm.isSuccress ()) {router.getView (registerAndLoginView.class) .trash (); Router.getView (ChatroomView.class) .Create (). Affichage (); ClientHolder.getClient (). Keepalive (rm.getTo ()); // Keep ...} else {contener Container = router.getView (registerAndLoginView.class) .Container (); if (Container! = null) {// Afficher l'erreur joptionpane.showMessageDialog (contener, rm.GetMessage ()); }}} catch (exception e) {loggerUtil.Error ("Handle Login a échoué!" + e.getMessage (), e); }} return null; }} classe publique NotifyHDL implémente Handler {@Override public Object Gande (objet obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); if (rm.isSuccress () && rm.getContent ()! = null) {clientNotifyDto dto = json.parseObject (rm.getContent (). toString (), clientNotifyDto.class); JComponent comp = router.getView (ChatroomView.class) .getComponent (ChatroomView.ListUSRList); if (compinestof jlist) {@SuppressWarnings ("Unchecked") // jList <string> listUsrlist = (jlist <string>) compt; List <string> listUser = ModelTolist (listUsrlist.getModel ()); if (dto.isflag ()) {if (! listUser.Contains (dto.getUsername ())) {listUser.add (dto.getUserName ()); listUser.Remove (constantValue.to_all); Collection.Sort (listUser); listUser.add (0, constantValue.to_all); }} else {listUser.Remove (dto.getUsername ()); } listUsrlist.setListData (listUser.toArray (new String [] {})); }}} catch (exception e) {LoggerUtil.Error ("Gire nofity a échoué!" + e.getMessage (), e); }} return null; } private list <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)); }} Retour List; }} classe publique RegisterHdl implémente Handler {@Override Public Object Handle (objet obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); Conteneur conteneur = router.getView (registerAndLoginView.class) .Container (); if (Container! = null) {if (rm.isSucCess ()) {jOptionPane.showMessageDialog (contener, rm.getContent ()); } else {jOptionPane.showMessageDialog (contener, rm.getMessage ()); }}} catch (exception e) {LoggerUtil.Error ("Handle Register a échoué!" + e.getMessage (), e); }} return null; }} classe publique TIPHDL implémente Handler {@Override Public Object Handle (objet 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 (ChatroomView.class) .getComponent (ChatroomView.Chattabbed); if (compinestof 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 () .Apnd (textArea.getText ()). APPEND (System.LineSeparator ()). APPEND (System.LineSeparator ()) .Apnd ("[") .Apnd (m.getowner ()). APPEND ("]:"). // Faites défiler vers TextArea.SetCaretPosition (TextArea.GetText (). Length ()); }}} catch (exception e) {LoggerUtil.Error ("Gireat Tip a échoué!" + e.getMessage (), e); }} return null; }} Il existe une autre classe pour le module de communication Socket, c'est-à-dire le client du client, qui est utilisé pour stocker le client actuel, qui est similaire au Socketholder sur le serveur.
/ ** * @author yaolin * / public class ClientHolder {public static client client; Client statique public getClient () {return client; } public static void setClient (client client) {clientHolder.Client = client; }}Implémentation spécifique du module d'interface utilisateur:
Le ci-dessus enregistre la conception du module de communication de socket. Ensuite, j'enregistre le module de conception de l'interface utilisateur. Je n'ai pas l'intention d'écrire l'interface utilisateur par moi-même. Après tout, l'écriture est trop laide, donc je peux demander aux camarades de classe ou à des amis de m'aider à le frapper plus tard. Je remets donc le traitement des événements de l'interface utilisateur à l'action pour le gérer et sépare simplement la conception de l'interface utilisateur et la réponse de l'événement. Toutes les UIS héritent de JFrame et implémentent l'interface de vue. La classe d'implémentation de gestionnaire ci-dessus est obtenue via le routeur (il sera retourné directement s'il existe, et il sera créé et stocké s'il n'existe pas). La vue fournit la création d'interface utilisateur (), obtenez un conteneur (), obtenez les composants dans l'interface utilisateur GetComponent (), Display Display () et Recycle Trash (); ResultWrapper et Resultholder sont juste pour créer et stocker des onglets de chat. La conception est la suivante:
[Router.java, vue.java]
Toutes les UIS héritent de JFrame et implémentent l'interface de vue. La classe d'implémentation du gestionnaire obtient l'interface utilisateur spécifiée via le routeur (il revient directement s'il existe, et il crée et stocke s'il n'existe pas). La vue fournit la création d'interface utilisateur (), obtient Container () et obtient les composants dans l'interface utilisateur GetComponent (), affiche l'affichage () et recycle trash (). La mise en œuvre spécifique est la suivante:
/ ** * Affichage de la route * @author yaolin * / public class router {map statique privé <chaîne, vue> liStoute = new hashmap <string, vue> (); View statique public GetView (class <?> Clazz) {View v = ListRoute.get (Clazz.getName ()); if (v == null) {try {v = (View) class.forname (Clazz.getName ()). NewInstance (); Listoute.put (Clazz.getName (), v); } catch (exception e) {loggerUtil.Error ("Create View a échoué!" + e.getMessage (), e); }} return v; }} / ** * Interfaces canoniques pour toutes les interfaces * @Author Yaolin * * / View de l'interface publique {/ ** * * / View Public Create (); / ** * * / public Container Container (); / ** * @param key * / public jComponent getComponent (string key); / ** * * / public void display (); / ** * * / public void trash (); }[RegisterAndLoginView.java, Chatroomview.java]
Comme je ne veux pas écrire l'interface utilisateur par moi-même, j'ai simplement écrit deux interfaces d'interface utilisateur ici, à savoir l'interface d'enregistrement et de connexion et l'interface de chat. Voici deux interfaces laides:
Enregistrer l'interface de connexion
Interface de chat
Voici les codes spécifiques pour ces deux interfaces:
/ ** * Inscrivez-vous et connexion * @author yaolin * / classe publique RegisterAndLoginView étend JFrame implémente View {private static final long SerialVersionUID = 6322088074312546736l; Action privée RegisterAndLoginaction = new RegisterAndLoginaction (); Boolean statique privé Create = false; @Override Public View Create () {if (! Create) {init (); Create = true; } retourne ceci; } public Container Container () {Create (); return getContentPane (); } @Override public JComponent getComponent (string key) {return null; } @Override public void display () {setVisible (true); } @Override public void trash () {dissose (); } private void init () {// attribut setSize (500, 300); setResizable (false); setLocationRelativeto (null); // Container JPanel Pannel = new JPanel (); PANNEL.SETLAYOUT (NULL); // Component // 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); PANNEL.ADD (LBUSERNAME); PANNEL.ADD (TFUSERNAME); // mot de passe jLabel lbpassword = new JLabel (i18n.text_password); lbpassword.setbounds (100, 120, 200, 30); final jpasswordfield pfpassword = new jpasswordfield (); pfpassword.setbounds (150, 120, 230, 30); PANNEL.ADD (LBPASSWORD); PANNEL.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); PANNEL.ADD (BTNREGISTER); PANNEL.ADD (BTNLOGIN); PANNEL.ADD (BTNEXIT); // Event pfpassword.addkeyListener (new KeyAdapter () {public void keyPressed (final keyEvent e) {if (e.getkeycode () == keyevent.vk_enter) btnlogin.doclick ();}}); // fin de addkeyListener btnErFister.AddactionListener (new ActionListener () {public vOID AGORFORMED. ActionEvent e) {if (stringUtil.isempty (tfusername.getText ()) || stringUtil.isempty (new String (pfpassword.getPassword ())) {joptionpane.showMessageAdAg (getContentPane (), i18n.info_register_empty_data); Action.HandleRegister (tfusername.getText (), new String (pfpassword.getPassword ());}}); // fin de AddActionListener btnlogin.AddactionListener (New ActionListener () {public void Actionperformed (final ActionEvent e) {if (StringUtil.Isempty (TfuserName.Get. StringUtil.isempty (new String (pfpassword.getPassword ())) {jOptionPane.ShowMessageDialog (getContentPane (), i18n.info_login_empty_data); }); // fin de addActionListener btNexit.AddActionListener (new ActionListener () {public void ActionPerformed (final ActionEvent e) {System.Exit (0); }}); // fin de addActionListener getContentPane (). Add (panneau); setDefaultCloseOperation (jframe.exit_on_close); }} / ** * Fenêtre de chat client * * @author yaolin * / classe publique ChatroomView étend JFrame implémente View {private static final long SerialVersionUID = -4515831172899054818l; chaîne finale statique publique listUsrList = "listUsrlist"; Public Static Final String Chattabbed = "Chattabbed"; Boolean statique privé Create = false; Action de ChatroomAction privée = new ChatroomAction (); JLIST PRIVÉ <string> listUsrList = null; JtabbedPane privé Chattabbed = null; @Override Public View Create () {if (! Create) {init (); Create = true; } retourne ceci; } public Container Container () {Create (); return getContentPane (); } @Override public jComponent getComponent (string key) {create (); switch (key) {case listUsrlist: return listUsrlist; Case Chattabbed: retour chattabbed; } return null; } @Override public void display () {setVisible (true); } @Override public void trash () {dissose (); } 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 () {// Sélecteur de fichiers final jFileChooser fileChoOser = new JFileChooser (); Jpanel Panel = new JPanel (new BorderLayout ()); // Centre chattabbed = new JTabbedPane (); chattabbed.addtab (constantValue.to_all, resultholder.get (constantValue.to_all) .getscrollPane ()); PANNEL.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); // juste pour le 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 (nouveaux encarts (10, 10, 10, 10)); tasend.setrows (10); South.Add (Tasend, BorderLayout.Center); // South - BTN JPanel Bottom = New JPanel (New BorderLayout ()); inférieur.add (new JLabel (), BorderLayout.Center); // juste pour le padding jbutton btnsend = new JButton (i18n.btn_send); Basing.Add (BTNSEND, BorderLayout.East); South.Add (en bas, BorderLayout.South); btnupload.addactionListener (new ActionListener () {public void ActionPerformed (final ActionEvent e) {if (! constanteValue.to_all.equal JFileChooser.approve_OPTION) {fichier file = filechooser.getSelectedFile (); }}}); BTNSEND.AdDactionListener (new ActionListener () {public void ActionPerformed (final ActionEvent e) {if (stringUtil.isnotempty (tasend.getText ())) {Action.Send (chattabbed.getTitleat (chattabbed.getSelectIndex (); }); Panel.Add (South, BorderLayout.South); panneau de retour; } private jComponent createUsrListView () {listUsrlist = new JList <string> (); listUsrlist.setborder (new lineborder (colore.blue)); listUsrlist.setListData (new String [] {constanteValue.to_all}); listUsrlist.setFixedCellWidth (200); listUsrList.setFixedCellHeight (30); listUsrList.AddListSelectionListener (new ListSelectionListener () {@Override public void ValueChanged (listSelectionEvent e) {// chat to if (chattabbed.indexoftab (listusrList.getSelectedValue ()) == -1 && listrList.getSelectedValue ()! = null && ! listUsrlist.getSelectedValue (). Equals (clientHolder.getClient (). GetFrom ())) {chattabbed.addtab (listUsrlist.getSelectedValue (), resultholder.get (listUsrlist.getSelectedValue ()). GetSultholder ()); chattabbed.setselectedIndex (chattabbed.indexoftab (listUsrlist.getSelectedValue ());}}}); return listUsrlist; }}[RegisterAndLoginaction.Java, ChatroomAction.java]
Ici, le traitement des événements d'interface utilisateur est géré par Action, qui sépare simplement la conception de l'interface utilisateur et la réponse des événements. Les événements de RegisterAndLoginView sont gérés par RegisterAndLoginaction, et les événements du ChatroomView sont gérés par ChatroomAction. La mise en œuvre spécifique est la suivante:
classe publique RegisterAndLogInAction {public void handleRegister (String username, String Motword) {if (stringUtil.iSempty (nom d'utilisateur) || stringUtil.isempty (mot de passe)) {return; } RegisterMessage Message = new RegisterMessage () .SetUserName (nom d'utilisateur) .SetPassword (mot de passe); message.setFrom (nom d'utilisateur); SendHelper.Send (clientHolder.getClient (). GetSocket (), message); } public void handlelogin (String username, string mot de passe) {if (stringUtil.isempty (nom d'utilisateur) || stringUtil.iSempty (mot de passe)) {return; } LoginMessage Message = new LoginMessage () .SetUserName (nom d'utilisateur) .SetPassword (mot de passe); message.setFrom (nom d'utilisateur); SendHelper.Send (clientHolder.getClient (). GetSocket (), message); }} Il existe deux autres classes pour la conception de l'interface utilisateur, à savoir Resultholder et Resultimewrapper. ResultWrapper et Resultholder sont juste pour créer et stocker des onglets de chat. La mise en œuvre spécifique est la suivante:
classe publique resultWrapper {private jscrollPane ScrollPane; JEXTAREA PRIVÉE TextArea; public resultWrapper (jscrollPane ScrollPane, jTextArea textArea) {this.scrollPane = scrollPane; this.textArea = textArea; } public jscrollpane getCrollPane () {return ScrollPane; } public void setscrollPane (jscrollpane scrollPane) {this.scrollpane = scrollPane; } public jTextArea getTextArea () {return textArea; } public void setTextArea (jTextArea textArea) {this.textArea = textArea; }} classe publique Resultholder {Map statique privé <String, resultWrapper> listResultWrapper = new HashMap <String, resultWrapper> (); Public static void put (clé de chaîne, ResultWrapper Wrapper) {ListResultWrapper.put (clé, wrapper); } public static resultwrapper get (string key) {resultwrapper wrapper = listresultwrapper.get (key); if (wrapper == null) {wrapper = create (); put (clé, wrapper); } return wrapper; } private static resultwrapper create () {jTextArea resultTextArea = new JTextArea (); resultTextArea.setEdIT (false); resultTextArea.setBorder (new lineborder (color.blue)); JscrollPane ScrollPane = new JscrollPane (resulteTTextArea); scrollPane.sethorizontalscrollBarpolicy (scrollpanEconstants.horizontal_scrollbar_never); scrollpane.setverticalscrollbarpolicy (scrollpanEconstants.vertical_scrollbar_as_needed); Resultwrapper wrapper = new resultWrapper (scrollPane, resultTextArea); wrapper de retour; }} Le dernier est donné, l'entrée à laquelle le client s'exécute:
/ ** * * @author yaolin * * / public class niloayChat {public static void main (String [] args) {View v = router.getView (registerAndLoginView.class) .Create (); essayez {v.display (); Client client = nouveau client (nouveau defaultCallback ()); client.start (); ClientHolder.SetClient (client); } catch (ioException e) {jOptionPane.showMessageDialog (v.Container (), e.getMessage ()); }}} Adresse de téléchargement de démo: démo
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.