Escrito antes:
No fim de semana passado, levei algum tempo para gravar o design inicial e o design detalhado do servidor de um programa de bate-papo simples que escrevi. Finalmente esperei pelo certificado de exame suave que fiz antes da formatura na terça -feira e depois passei o dia da prorrogação. Hoje aconteceu na sexta -feira e planejei gravar o design detalhado do cliente e do módulo comum, porque estarei ocupado com outras coisas a partir deste fim de semana.
projeto:
O design do cliente é dividido principalmente em duas partes, como o design do módulo de comunicação de soquete e o design relacionado à UI.
Design de comunicação do soquete do cliente:
O design aqui é realmente semelhante ao design do servidor. A diferença é que o servidor recebe pacotes de batimentos cardíacos, enquanto o cliente envia pacotes de batimentos cardíacos. Como o cliente se comunica apenas com um servidor (a comunicação entre clientes também é distribuída pelo servidor), apenas um pool de threads do tamanho 2 é usado para lidar com essas duas coisas (newFixedThreadpool (2)). As classes de processamento correspondentes são o Receivenener e o Keepalivedog. Quando o Receivenener é inicializado, um retorno de chamada é enviado como um retorno de chamada para o cliente recebe a mensagem do servidor. A implementação padrão do retorno de chamada é o DefaultCallback. DefaultCallback é distribuído a diferentes manipuladores através da HF, de acordo com diferentes eventos. O cliente armazena as informações atuais do cliente. O design é o seguinte:
A implementação específica do módulo de comunicação do soquete:
[Client.java]
O cliente é a entrada do cliente para se conectar ao servidor. Para criar um cliente, você precisa especificar um retorno de chamada como retorno de chamada quando o cliente receber a mensagem do servidor. Em seguida, o método START () do cliente inicia a audição do servidor (ReceiveListener). Quando o Receivenener recebe os dados enviados pelo servidor, o método de retorno de chamada (retorno de chamada) é chamado para processá -los. Ao mesmo tempo, o cliente também precisa enviar um pacote de batimentos cardíacos para notificar o servidor de que ainda está conectado ao servidor. O pacote de batimentos cardíacos é mantido pelo cliente. Alive () é iniciado e implementado por Keepalivedog; Essas duas etapas são executadas por um pool de threads de newfixedthreadpool (2) com um tamanho fixo de 2. Pode ser mais razoável usar um newFixedThreadpool (1) e o NewsCheduledThreadpool (1) aqui para lidar com isso, porque o pacote de batidas cardíacas é enviado regularmente, e é assim que o servidor de TI (esse ajuste seguinte). O código específico do cliente é o seguinte (os outros dois métodos são expostos aqui para obter o soquete e o usuário ao qual o soquete atual pertence):
/** * cliente * @author yaolin * */public class cliente {soquete final privado; string privada de; Pool de ExecutoraService Final Service privado; Retorno de chamada final privado de retorno de chamada; public client (retorno de chamada) lança ioexception {this.socket = new Socket (constantValue.server_ip, constantValue.server_port); this.pool = executores.newfixedthreadpool (2); this.callback = retorno de chamada; } public void Start () {Pool.execute (New ReceiveListener (soquete, retorno de chamada)); } public void Keepalive (string de) {this.from = de; pool.execute (novo Keepalivedog (soquete, de)); } Public Socket getSocket () {return Socket; } public string getFrom () {return of; }}[Keepalivedog.java]
Depois que o cliente estabelece uma conexão com o servidor (este programa refere -se ao login bem -sucedido, porque o soquete do cliente será gerenciado pelo soldador de demão do servidor após o sucesso do login), é necessário enviar um pacote de batimentos cardíacos para o servidor sempre para informar o servidor que ainda está em contato com o servidor; caso contrário, o blog do servidor será descartado sem o servidor após um período de tempo que ainda está em contato com o servidor; O Código de Keepalivedog é implementado da seguinte maneira (ele pode ser ajustado ao NewsCheduledThreadpool (1) posteriormente, para que o código aqui também seja ajustado):
/*** Keepalivedog: diga ao servidor que este cliente está em execução; * * @author yaolin */public classe keepalivedog implementa Runnable {Setor de soquete final privado; string final privada de; public Keepalivedog (soquete, string de) {this.socket = soket; this.from = de; } @Override public void run () {while (soquete! Alivemessage mensagem = new alivemessage (); message.setFrom (de); out.println (json.tojson (mensagem)); out.flush (); Thread.sleep (constantValue.keep_alive_period * 1000); } Catch (Exceção e) {LoggerUtil.error ("Cliente Enviar mensagem Falha!" + E.GetMessage (), e); }}}}[RECEBELISTENER.Java]
O método START () do cliente inicia a audição do servidor e é implementado pelo ReceiveListener. Depois de receber a mensagem do servidor, o ReceiveListener ligará de volta o método Dowork () de retorno de chamada para permitir que o retorno de chamada lide com a lógica de negócios específica. Portanto, o Receivenener é responsável apenas por ouvir as mensagens no servidor e o processamento específico é tratado por retorno de chamada. Deve -se mencionar aqui que, quando o tipo de mensagem for um tipo de arquivo, ele dormirá para configurar o intervalo de execução, para que o Dowork no retorno de chamada possa ler o fluxo de arquivo para o servidor, em vez de entrar diretamente no próximo loop. O design aqui é semelhante ao servidor. O Código de Implementação específico do RECELEGEREN é o seguinte:
public class ReceiveListener implementa Runnable {Setor de soquete final privado; Retorno de chamada final privado de retorno de chamada; Public ReceiveListener (soquete, retorno de chamada) {this.socket = soquete; this.callback = retorno de chamada; } @Override public void run () {if (soquete! Linha de string = null; Stringbuffer sb = null; if (is.Available ()> 0) {BufferReader bufr = new BufferredReader (novo InputStreamReader (IS)); sb = new StringBuffer (); while (is.Available ()> 0 && (linha = bufr.readline ())! = null) {sb.append (line); } Loggerutil.trach ("Receba [" + sb.toString () + "] em" + new Date ()); retorno de chamada.dowork (soquete, sb.toString ()); BasEmessage Message = json.parseObject (sb.toString (), basemessage.class); if (message.getType () == MessageType.File) {// Pause para receber o arquivo loggerutil.trach ("Cliente: pause para receber o arquivo"); Thread.sleep (constantValue.message_period); }} else {thread.sleep (constantValue.message_period); }} catch (Exceção e) {LoggerUtil.error ("Cliente Envie a mensagem Falha!" + E.GetMessage (), e); }}}}}}}[Callback.java, DefaultCallback.java]
Pelo exposto, podemos ver que o processamento de mensagens do cliente é um retorno de chamada e seu retorno de chamada é apenas uma interface. Todas as implementações de retorno de chamada implementam a interface para processar mensagens de acordo com suas necessidades. Aqui, a implementação padrão do retorno de chamada é o DefaultCallback. DefaultCallback processa apenas três tipos de mensagens, ou seja, mensagens de bate -papo, mensagens de arquivo e retornar mensagens. Para mensagens de bate -papo, o DefaultCallback obterá a interface correspondente através da rota do roteador na interface do usuário (consulte o design da interface do usuário abaixo para obter detalhes) e exibirá a mensagem na caixa de bate -papo correspondente; Para mensagens de arquivo, o DefaultCallback gravará o arquivo no caminho especificado na configuração (o arquivo é recebido sem a permissão do usuário aqui. Esse design não é muito amigável, então por enquanto); Para mensagens de retorno, o DefaultCallback será chamado para diferentes manipuladores de acordo com a chave na mensagem de retorno. O código específico é o seguinte:
interface pública Retorno de chamada {public void Dowork (servidor de soquete, dados do objeto); } classe pública DefaultCallback implementa o retorno de chamada {@Override public void Dowork (servidor de soquete, dados do objeto) {if (data! = null) {basemessage message = json.parseObject (data.tostring (), basemessage.class); switch (message.gettype ()) {case messageType.chat: handlechatmessage (dados); quebrar; case messageType.File: handleFilemessage (servidor, dados); quebrar; Case Messagetype.return: HandleReturnMessage (dados); quebrar; }}} private void handlechatMessage (dados do objeto) {chatmessage m = json.parseObject (data.toString (), chatmessage.class); String tabKey = m.getFrom (); // do JComponent Comp = Router.getView (ChatroomView.class) .getComponent (ChatroomView.chattabbed); if (comp instância de 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 (texttarea.getText ()). append (system.lineseParator ()). append (system.lineseparator ()) .append ("[") .Append (m.gEtowner ()). .ToString ()); // role para a parte inferior textarea.setCaretPosition (textarea.getText (). Length ()); }} private void handleFilemessage (servidor de soquete, dados do objeto) {filemessage message = json.parseObject (data.toString (), filemessage.class); if (message.getSize ()> 0) {outputStream OS = null; tente {if (server! = null) {inputStream is = server.getInputStream (); Arquivo dir = novo arquivo (constantValue.client_receive_dir); if (! Dir.Exists ()) {Dir.mkdirs (); } OS = new FileOutputStream (novo arquivo (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); total += len; Loggerutil.debug ("Receba Buff [" + len + "]"); } os.flush (); if (total> = message.getSize ()) {loggerutil.info ("receba buff [ok]"); quebrar; }}}}}} catch (Exceção e) {LoggerUtil.error ("Receba o arquivo falhado!" + E.GetMessage (), e); } finalmente {if (os! = null) {tente {os.close (); } catch (exceção ignorar) {} os = null; }}}} private void HandleReturnMessage (dados do objeto) {returnMessage m = json.parseObject (data.toString (), returnMessage.class); if (stringutil.isnotEmpty (m.getKey ())) {switch (m.getKey ()) {case key.notify: // notifique o cliente para atualizar a lista de usr hf.gethandler (key.notify) .Handle (dados); quebrar; Case Key.Login: hf.gethandler (key.login) .Handle (dados); quebrar; case key.Register: hf.gethandler (key.register) .Handle (dados); quebrar; Case Key.ListUser: HF.Gethandler (Key.ListUser) .Handle (Data); quebrar; case key.tip: hf.gethandler (key.tip) .Handle (dados); quebrar; }}}}[Handler.java, hf.java, listuserhdl.java ...]
O componente Handler é responsável pelo processamento de mensagens do tipo de mensagem de retorno do servidor. DefaultCallback distribui mensagens para diferentes manipuladores de acordo com as teclas diferentes. Este também é um componente simples de fábrica. É semelhante aos dados recebidos pelo servidor. O diagrama de aulas completo é o seguinte:
O código para esta seção é fornecido abaixo. Para reduzir o espaço, todo o código implementado pelo manipulador é coletado.
manipulador de interface pública {public objeto identificador (objeto obj); } public class HF {public estático manipulador Gethandler (chave de string) {switch (key) {case key.notify: retorna new notifyhdl (); key.login de case: retorna new LogInHdl (); CASE KEY.REGISTER: Return New RegisterHdl (); key.ListUser: retornar novo listuserHdl (); chave de case.tip: retornar novo tiphdl (); } retornar nulo; }} classe pública listuserHdl implementa Handler {@Override Public Object Handle (objeto 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 (), clientelistuserdto.class); JComponent Comp = Router.getView (ChatroomView.class) .getComponent (ChatroomView.Listusrlist); if (comp instância de jlist) {@suppresswarnings ("desmarcado") // jlist <string> listusrlist = (jlist <string>) comp; List <String> listuser = new LinkedList <String> (); listuser.addall (dto.getListUser ()); Coleções.sort (listuser); listuser.add (0, constantValue.to_all); listusrlist.setListData (listuser.toarray (new string [] {})); }}} catch (Exceção e) {LoggerUtil.error ("Handle listusr falhou!" + E.GetMessage (), e); }} retornar nulo; }} classe pública LogInhdl implementa Handler {@Override Public Object Handle (objeto obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.tostring (), returnMessage.class); if (rm.issuccess ()) {router.getView (RegisterLoginView.class) .trash (); Router.getView (ChatroomView.class) .Create (). Display (); Clientholder.getClient (). Keepalive (rm.getto ()); // mantenha ...} else {contêiner contêiner = router.getView (RegisterLoginView.class) .Container (); if (contêiner! = null) {// mostre erros JoptionPane.showMessagedialog (contêiner, rm.getMessage ()); }}} catch (Exceção e) {LoggerUtil.error ("Handle Login falhou!" + E.GetMessage (), e); }} retornar nulo; }} public class NotifyHdl implementa Handler {@Override Public Object Handle (objeto 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 instância de jlist) {@suppresswarnings ("desmarcado") // 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); Coleções.sort (listuser); listuser.add (0, constantValue.to_all); }} else {listuser.remove (dto.getUserName ()); } listusrlist.setListData (listuser.toArray (new String [] {})); }}} catch (Exceção e) {LoggerUtil.error ("Handle nofity falhou!" + E.getMessage (), e); }} retornar nulo; } Lista privada <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)); }} Lista de retorno; }} classe pública RegisterHdl implementa Handler {@Override Public Object Handle (objeto obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.tostring (), returnMessage.class); Contêiner contêiner = router.getView (RegisterLoginView.class) .Container (); if (contêiner! = null) {if (rm.issuccess ()) {joptionpane.showMessagedialog (contêiner, rm.getContent ()); } else {JoptionPane.showMessagedialog (contêiner, rm.getMessage ()); }}} Catch (Exceção e) {LoggerUtil.error ("Handle Register falhou!" + E.GetMessage (), e); }} retornar nulo; }} public class tiphdl implementa Handler {@Override Public Object Handle (objeto obj) {if (obj! = null) {try {returnMessage m = json.parseObject (obj.tostring (), returnMessage.class); if (m.issuccess () && m.getContent ()! = null) {string tabKey = m.getFrom (); Dica de corda = m.getContent (). ToString (); JComponent Comp = Router.getView (ChatroomView.class) .GetComponent (ChatroomView.Chattabbed); if (comp instância de 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 (textAea.getText ()). Append (System.LineSeParator ()). Append (System.LineSeParator ()) .Append ("[") .Append (M.Getowner ()). // role para a parte inferior textarea.setCaretPosition (textarea.getText (). Length ()); }}} Catch (Exceção e) {LoggerUtil.error ("Dica Handle Falha!" + E.GetMessage (), e); }} retornar nulo; }} Há outra classe para o módulo de comunicação do soquete, ou seja, o cliente, usado para armazenar o cliente atual, que é semelhante ao soldador de demão no servidor.
/** * @author yaolin */public class Clientholder {public static Client Client; cliente estático public static getClient () {return client; } public static void SetClient (cliente cliente) {clientholder.client = client; }}Implementação específica do módulo da interface do usuário:
O acima registra o design do módulo de comunicação do soquete. Em seguida, gravei o módulo de design da interface do usuário. Não pretendo escrever a interface do usuário sozinha. Afinal, a escrita é muito feia, para que eu possa pedir a colegas de classe ou amigos que me ajudem a bater mais tarde. Por isso, entrego o processamento de eventos da interface do usuário à ação para lidar com isso e simplesmente separo o design da interface do usuário e a resposta do evento. Todas as UIs herdam JFrame e implementam a interface de visualização. A classe de implementação do manipulador acima é obtida através do roteador (ela será retornada diretamente se existir e será criada e armazenada se não existir). A visualização fornece a criação da interface do usuário (), obtenha contêiner (), obtenha os componentes no UI getComponent (), display display () e reciclagem de lixo (); ResultWrapper e ResultTholder são apenas para criar e armazenar guias de bate -papo. O design é o seguinte:
[Router.java, View.java]
Todas as UIs herdam JFrame e implementam a interface de visualização. A classe de implementação do manipulador obtém a interface do usuário especificada através do roteador (ele retorna diretamente se existir e cria e armazena se não existir). A visualização fornece a criação da interface do usuário (), obtém contêiner () e obtém os componentes no UI getComponent (), exibe o display () e recicla o Trash (). A implementação específica é a seguinte:
/*** View Route* @Author Yaolin*/Public Class Router {mapa estático privado <string, view> listrote = new hashmap <string, view> (); public static View getView (classe <?> clazz) {view v = listrotete.get (clazz.getName ()); if (v == null) {try {v = (view) class.ForName (clazz.getName ()). NewInstance (); listrote.put (clazz.getName (), v); } catch (Exceção e) {LoggerUtil.error ("Criar visualização falhou!" + E.GetMessage (), e); }} retornar v; }} /** * interfaces canônicas para todas as interfaces * @author yaolin * */interface pública View {/** * */public View Create (); / ** * */ contêiner de contêiner público (); / ** * @param key */ public jComponent getComponent (chave da string); / ** * */ public void Display (); / ** * */ public void Trash (); }[RegerandLoginView.java, Chatroomview.java]
Como não quero escrever a interface do usuário sozinha, simplesmente escrevi duas interfaces de interface do usuário aqui, a saber, a interface de registro e login e a interface de bate -papo. Aqui estão duas interfaces feias:
Registre a interface de login
Interface de bate -papo
A seguir, são apresentados os códigos específicos para essas duas interfaces:
/*** Registro e login* @author yaolin*/classe pública RegisterAndLoginView estende JFrame implementos View {private estático final serialversionUid = 6322088074312546736l; Ação do RegisterLoginAction final do RegisterLoginAction privado = new RegisterAndLoginAction (); Private estático booleano crie = false; @Override public View create () {if (! Create) {init (); Criar = true; } retornar isso; } public container container () {create (); retornar getContentPane (); } @Override public jComponent getComponent (chave de string) {return null; } @Override public void Display () {setVisible (true); } @Override public void Trash () {dispete (); } private void init () {// Setsize de atributo (500, 300); setResizable (false); setLocationRelativeTo (NULL); // Painel JPanel de contêineres = new jpanel (); painel.setLayout (nulo); // componente // nome de usuário jlabel lbusername = new jlabel (i18n.text_username); lbusername.setbounds (100, 80, 200, 30); final jtextfield tfuserName = new jtextfield (); tfusername.setbounds (150, 80, 230, 30); painel.add (lbusername); painel.add (tfusername); // senha jlabel lbpassword = new jlabel (i18n.text_password); lbPassword.setBounds (100, 120, 200, 30); final jpasswordfield pfpassword = new jpasswordfield (); pfpassword.setbounds (150, 120, 230, 30); painel.add (lbpassword); painel.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); painel.add (Btnregister); painel.add (btnlogin); painel.add (btnexit); // Event PfPassword.adDkeyListener (new KeyAdapter () {public void KeyPressed (final KeyEvent E) {if (e.getKeyCode () == keyEvent.vk_enter) Btnlogin.doclick ();}}); ActionPerformed (ActionEvent E) {if (stringutil.isempty (tfusername.getText ()) || stringutil.isempty (new string (pfpassword.getpassword ())) {JoptionPane.Showsedialog (getContentPane)) action.Handleregister (tfusername.getText (), new String (pfpassword.getpassword ())); Stringutil.isempty (new String (pfpassword.getpassword ()))) {JoptionPane.ShowMessagedialog (getContentPane (), i18n.info_login_empty_data); Fim do AddactionListener btnexit.addactionListener (new ActionListener () {public void ActionPerformed (Final ActionEvent E) {System.exit (0); }}); // final do addactionListener getContentPane (). Add (painel); setDefaultCloseoperation (jframe.exit_on_close); }} /** * Janela de bate -papo do cliente * * @author yaolin */public classe ChatroomView estende JFrame implementos View {private estático final seriialversionuid = -45158311728999054818L; public static final string listusrlist = "listusrlist"; public static final string chattabbed = "chattabbed"; Private estático booleano crie = false; Ação de ChatroomAction Private = New ChatroomAction (); private jlist <string> listusrlist = null; privado jtabbedpane chattabbed = null; @Override public View create () {if (! Create) {init (); Criar = true; } retornar isso; } public container container () {create (); retornar getContentPane (); } @Override public jComponent getComponent (string key) {create (); switch (key) {case listusrlist: return listusrlist; Caso Chattabbed: Return chattabbed; } retornar nulo; } @Override public void Display () {setVisible (true); } @Override public void Trash () {dispete (); } public void init () {Settitle (i18n.text_app_name); SetSize (800, 600); setResizable (false); setLocationRelativeTo (NULL); setLayout (new BorderLayout ()); add (createchatPanel (), borderlayout.center); add (createUUSRListView (), borderlayout.east); setDefaultCloseoperation (jframe.exit_on_close); } private JComponent CrereChatPanel () {// Seletor de arquivo final jfileChooser filechooser = new jfileChooser (); JPanel Painel = new JPanel (new BorderLayout ()); // Center chattabbed = new JtabbedPane (); chattabbed.addtab (constantValue.to_all, resultholder.get (constantValue.to_all) .getScrollPane ()); painel.Add (Chattabbed, borderlayout.center); // jpanel sul sul = novo jpanel (new borderlayout ()); // sul - arquivo jpanel meio = new jpanel (new borderlayout ()); Middle.Add (New Jlabel (), BorderLayout.Center); // apenas para preencher o jbutton btnupload = new jbutton (i18n.btn_send_file); Middle.Add (btnuPload, borderlayout.East); South.Add (Middle, Borderlayout.North); // sul - textarea final jtextarea taseend = new jtextarea (); tasend.setCaretColor (color.blue); taseend.setMargin (novas inserções (10, 10, 10, 10)); Tasend.SetRows (10); South.Add (Tasend, Borderlayout.Center); // sul - BTN JPanel Bottom = new JPanel (new BorderLayout ()); Bottom.Add (New Jlabel (), BorderLayout.Center); // apenas para preencher o jbutton btnsend = new jbutton (i18n.btn_send); Bottom.Add (Btnsend, BorderLayout.East); South.Add (Bottom, BorderLayout.South); btnuPload.addactionListener (new ActionListener () {public void ActionPormed (Final ActionEvent e) {if (! constantValue.to_all.equals (chattabbed.gettitleat (chattabbed.getSelected ())) {Intrornval = filoCoSer.SHOWOPENSENSIMENSIMENSIMENSIMENSIMENSIMENSIMENSIMENCIDO (chat); Jfilechooser.approve_option) {arquivo file = filechooser.getSelectFile (); I18n.info_file_to_all_error); btnsend.addactionListener (new ActionListener () {public void ActionPerformed (final ActionEvent e) {if (stringutil.isnotEmpty (taseend.getText ())) {action.send (chattabbed.gettleat (chattabbed.TextEtexEx (). }}); pain.add (sul, borderlayout.south); painel de retorno; } private JComponent CreateUSRListView () {listusrlist = new JList <String> (); listusrlist.setborder (new lineborder (color.blue)); listusrlist.setListData (new String [] {constantValue.to_all}); listusrlist.setFixedCellWidth (200); listusrlist.setFixedCelHeight (30); listusrlist.addlistSelectionListener (new ListSelectionListener () {@Override public void valuechanged (listselectionEvent e) {// Chat to (chattabbed.indexoftab (listusrlist.getSelectValue () == -1 && ListUsRlist.SelectedValue ()) == -1 && ListRlist.SelectedValue ()) ! listusrlist.getSelectedValue (). Iguals (clientholder.getClient (). getFrom ())) {chattabbed.addtab (listusrlist.getSelectedValue (), resultholdder.get (listUsrlist.getSelectedValue (). chattabbed.setSelectedIndex (chattabbed.indexoftab (listusrlist.getSelectValue ())); retornar listusrlist; }}[RegerandLoginaction.java, Chatroomaction.java]
Aqui, o processamento de eventos da interface do usuário é tratado por ação, que simplesmente separa o design da interface do usuário e a resposta do evento. Os eventos do RegisterLoginView são tratados pelo RegisterLoginAction, e os eventos do Chatroomview são tratados pela ChatroomAction. A implementação específica é a seguinte:
classe pública RegisterAndLoginAction {public void handleregister (string userName, string senha) {if (stringUtil.isEmpty (nome de usuário) || stringutil.isEmpty (senha)) {return; } RegisterMessage message = new RegisterMessage () .setUserName (nome de usuário) .SetPassword (senha); message.setFrom (nome de usuário); SendHelper.send (clientholder.getClient (). GetSocket (), mensagem); } public void handleLogin (String UserName, String senha) {if (stringUtil.isEmpty (nome de usuário) || stringUtil.isEmpty (senha)) {return; } LoginMessage message = new LoginMessage () .SetUserName (nome de usuário) .SetPassword (senha); message.setFrom (nome de usuário); SendHelper.send (clientholder.getClient (). GetSocket (), mensagem); }} Existem duas outras classes para o design da interface do usuário, a saber, reselhas e resulta. ResultWrapper e ResultTholder são apenas para criar e armazenar guias de bate -papo. A implementação específica é a seguinte:
classe pública ResultWrapper {private JScrollPane ScrollPane; private 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; }} classe pública Resultholder {private estático mapa <string, resultadowrapper> listresultwrapper = new hashmap <string, resultwrapper> (); public static void put (chave de string, resultwrapper wrapper) {listresultwrapper.put (chave, wrapper); } public static resultwrapper get (string key) {resultwrapper wrapper = listresultwrapper.get (key); if (wrapper == null) {wrapper = create (); put (chave, invólucro); } Return wrapper; } Private Static ResultWrapper Create () {JTexTarea ResultTextarea = new JTexTarea (); ResultTexTarea.setEditable (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); Return wrapper; }} O último é dado, a entrada para a qual o cliente é executado:
/** * * @Author yaolin * */public class niloaychat {public static void main (string [] args) {view v = router.getView (RegisterLoginView.class) .create (); tente {v.display (); Cliente cliente = novo cliente (new defaultCallback ()); client.start (); Clientholder.setClient (cliente); } catch (ioexception e) {JoptionPane.showMessagedialog (v.Container (), e.getMessage ()); }}} Endereço de download de demonstração: demonstração
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.