国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

詳解基于java的Socket聊天程序――客戶端(附demo)

2019-11-26 13:25:55
字體:
來源:轉載
供稿:網(wǎng)友

寫在前面:

上周末抽點時間把自己寫的一個簡單Socket聊天程序的初始設計服務端細化設計記錄了一下,周二終于等來畢業(yè)前考的軟考證書,然后接下來就是在加班的日子度過了,今天正好周五,打算把客戶端的詳細設計和Common模塊記錄一下,因為這個周末開始就要去忙其他東西了。

設計:

客戶端設計主要分成兩個部分,分別是socket通訊模塊設計和UI相關設計。

客戶端socket通訊設計:

這里的設計其實跟服務端的設計差不多,不同的是服務端是接收心跳包,而客戶端是發(fā)送心跳包,由于客戶端只與一個服務端進行通訊(客戶端之間的通訊也是由服務端進行分發(fā)的),所以這里只使用了一個大小為2的線程池去處理這兩件事(newFixedThreadPool(2)),對應的處理類分別是ReceiveListener、KeepAliveDog,其中ReceiveListener在初始化的時候傳入一個Callback作為客戶端收到服務端的消息的回調(diào),Callback的默認實現(xiàn)是DefaultCallback,DefaultCallback根據(jù)不同的事件通過HF分發(fā)給不同Handler去處理,而ClientHolder則是存儲當前客戶端信息,設計如下:

Socket通訊模塊具體實現(xiàn):

[Client.java]

Client是客戶端連接服務端的入口,創(chuàng)建Client需要指定一個Callback作為客戶端接收服務端消息時的回調(diào),然后由Client的start()方法啟動對服務端的監(jiān)聽(ReceiveListener),當ReceiveListener接收到服務端發(fā)來的數(shù)據(jù)時,調(diào)用回調(diào)(Callback)的doWork()方法去處理;同時Client中還需要發(fā)送心跳包來通知服務端自己還在連接著服務端,發(fā)心跳包由Client中keepAlive()啟動,由KeepAliveDog實現(xiàn);這兩個步驟由一個固定大小為2為線程池newFixedThreadPool(2)去執(zhí)行,可能這里使用一個newFixedThreadPool(1)和newScheduledThreadPool(1)去處理更合理,因為心跳包是定時發(fā)的,服務端就是這樣實現(xiàn)的(這個后續(xù)調(diào)整),Client的具體代碼如下(這里暴露了另外兩個方法用于獲取socket和當前socket所屬的用戶):

/** * 客戶端 * @author yaolin * */public class Client {    private final Socket socket;  private String from;  private final ExecutorService pool;  private final Callback callback;  public Client(Callback callback) throws IOException {    this.socket = new Socket(ConstantValue.SERVER_IP, ConstantValue.SERVER_PORT);    this.pool = Executors.newFixedThreadPool(2);    this.callback = callback;  }    public void start() {    pool.execute(new ReceiveListener(socket, callback));  }    public void keepAlive(String from) {    this.from = from;    pool.execute(new KeepAliveDog(socket, from));  }    public Socket getSocket() {    return socket;  }    public String getFrom() {    return from;  }}

[KeepAliveDog.java]

客戶端在與服務端建立連接之后(該程序中是指登陸成功之后,因為登陸成功之后客戶端的socket才會被服務端的SocketHolder管理),需要每個一段時間就給服務端發(fā)送心跳包告訴服務端自己還在跟服務端保持聯(lián)系,不然服務端會在一段時間之后將沒有交互的socket丟棄(詳見服務端那篇博客),KeepAliveDog的代碼實現(xiàn)如下(后期可能會調(diào)整為newScheduledThreadPool(1),所以這里的代碼也會調(diào)整):

/** * KeepAliveDog : tell Server this client is running; *  * @author yaolin */public class KeepAliveDog implements Runnable {  private final Socket socket;  private final String from;    public KeepAliveDog(Socket socket, String from) {    this.socket = socket;    this.from = from;  }  @Override  public void run() {    while (socket != null && !socket.isClosed()) {      try {                PrintWriter out = new PrintWriter(socket.getOutputStream());        AliveMessage message = new AliveMessage();        message.setFrom(from);        out.println(JSON.toJSON(message));        out.flush();                Thread.sleep(ConstantValue.KEEP_ALIVE_PERIOD * 1000);              } catch (Exception e) {        LoggerUtil.error("Client send message failed !" + e.getMessage(), e);      }    }  }}

[ReceiveListener.java]

Client的start()方法啟動對服務端的監(jiān)聽由ReceiveListener實現(xiàn),ReceiveListener接收到服務端的消息之后會回調(diào)Callback的doWork()方法,讓回調(diào)去處理具體的業(yè)務邏輯,所以ReceiveListener只負責監(jiān)聽服務端的消息,具體的處理由Callback負責,這里需要提一下的是當消息類型是文件類型的時候會睡眠配置執(zhí)行的間隔時間,這樣Callback中的doWork才能對讀取來至服務端的文件流,而不是直接進入下一次循環(huán),這里的設計跟服務端是類似的。ReceiveListener的具體實現(xiàn)代碼如下:

public class ReceiveListener implements Runnable {  private final Socket socket;  private final Callback callback;  public ReceiveListener(Socket socket, Callback callback) {    this.socket = socket;    this.callback = 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("RECEIVE [" + sb.toString() + "] AT " + new Date());                        callback.doWork(socket, sb.toString());            BaseMessage message = JSON.parseObject(sb.toString(), BaseMessage.class);            if (message.getType() == MessageType.FILE) {              // PAUSE TO RECEIVE FILE              LoggerUtil.trach("CLIENT:PAUSE TO RECEIVE FILE");              Thread.sleep(ConstantValue.MESSAGE_PERIOD);            }          } else {            Thread.sleep(ConstantValue.MESSAGE_PERIOD);          }        } catch (Exception e) {          LoggerUtil.error("Client send message failed !" + e.getMessage(), e);        }      }    }  }}

[Callback.java、DefaultCallback.java]

從上面可以看出Client對消息的處理是Callback回調(diào),其Callback只是一個接口,所有Callback實現(xiàn)該接口根據(jù)自己的需要對消息進行相應地處理,這里Callback默認的實現(xiàn)是DefaultCallback,DefaultCallback只對三種消息進行處理,分別是聊天消息、文件消息、返回消息。對于聊天消息,DefaultCallback將通過UI中的Router路由獲取到相應的界面(詳見下面的UI設計),然后將消息展現(xiàn)在對應的聊天框中;對于文件消息,DefaultCallback則是將文件寫入到配置中指定的路徑中(這里沒有通過用戶的允許就接收文件,這種設計不是很友好,目前先這樣);對于返回消息,DefaultCallback會根據(jù)返回消息中的KEY叫給不同的Handler去處理。具體代碼如下:

 public interface Callback {   public void doWork(Socket server, Object data);  } 
public class DefaultCallback implements Callback {  @Override  public void doWork(Socket server, Object data) {    if (data != null) {      BaseMessage message = JSON.parseObject(data.toString(), BaseMessage.class);      switch (message.getType()) {      case MessageType.CHAT:        handleChatMessage(data);        break;      case MessageType.FILE:        handleFileMessage(server, data);        break;      case MessageType.RETURN:        handleReturnMessage(data);        break;      }    }  }  private void handleChatMessage(Object data) {    ChatMessage m = JSON.parseObject(data.toString(), ChatMessage.class);    String tabKey = m.getFrom();// FROM    JComponent comp = Router.getView(ChatRoomView.class).getComponent(ChatRoomView.CHATTABBED);    if (comp instanceof JTabbedPane) {      JTabbedPane tab = (JTabbedPane) comp;      int index = tab.indexOfTab(tabKey);      if (index == -1) {        tab.addTab(tabKey, ResultHolder.get(tabKey).getScrollPane());      }      JTextArea textArea = ResultHolder.get(tabKey).getTextArea();      textArea.setText(new StringBuffer()          .append(textArea.getText()).append(System.lineSeparator()).append(System.lineSeparator())          .append(" [").append(m.getOwner()).append("] : ").append(System.lineSeparator())          .append(m.getContent())          .toString());      // SCROLL TO BOTTOM      textArea.setCaretPosition(textArea.getText().length());    }  }  private void handleFileMessage(Socket server, Object data) {    FileMessage message = JSON.parseObject(data.toString(), FileMessage.class);    if (message.getSize() > 0) {      OutputStream os = null;      try {        if (server != null) {          InputStream is = server.getInputStream();          File dir = new File(ConstantValue.CLIENT_RECEIVE_DIR);          if (!dir.exists()) {            dir.mkdirs();          }          os = new FileOutputStream(              new File(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("RECEIVE BUFF [" + len + "]");              }              os.flush();              if (total >= message.getSize()) {                LoggerUtil.info("RECEIVE BUFF [OK]");                break;              }            }          }        }      } catch (Exception e) {        LoggerUtil.error("Receive file failed ! " + e.getMessage(), e);      } finally {        if (os != null) {          try {            os.close();          } catch (Exception ignore) {          }          os = null;        }      }    }  }  private void handleReturnMessage(Object data) {    ReturnMessage m = JSON.parseObject(data.toString(), ReturnMessage.class);    if (StringUtil.isNotEmpty(m.getKey())) {      switch (m.getKey()) {      case Key.NOTIFY: // Notify client to update usr list        HF.getHandler(Key.NOTIFY).handle(data);        break;      case Key.LOGIN:        HF.getHandler(Key.LOGIN).handle(data);        break;      case Key.REGISTER:        HF.getHandler(Key.REGISTER).handle(data);        break;      case Key.LISTUSER:        HF.getHandler(Key.LISTUSER).handle(data);        break;      case Key.TIP:        HF.getHandler(Key.TIP).handle(data);        break;      }    }  }}

[Handler.java、HF.java、ListUserHdl.java...]

Handler組件負責對服務端返回消息類型的消息進行處理,DefaultCallback根據(jù)不同的KEY將消息分發(fā)給不同的Handler進行處理,這里也算一套簡單的工廠組件吧,跟服務端處理接收到的數(shù)據(jù)設計是類似的,完整的類圖如下:

下面給出這一塊的代碼,為了縮小篇幅,將所有Handler實現(xiàn)的代碼收起來。 

public interface Handler {   public Object handle(Object obj); }
public class 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;  }}
public class ListUserHdl implements Handler {  @Override  public Object handle(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 instanceof JList) {            @SuppressWarnings("unchecked") //            JList<String> listUsrList = (JList<String>) comp;            List<String> listUser = new LinkedList<String>();            listUser.addAll(dto.getListUser());            Collections.sort(listUser);            listUser.add(0, ConstantValue.TO_ALL);            listUsrList.setListData(listUser.toArray(new String[]{}));          }        }      } catch (Exception e) {        LoggerUtil.error("Handle listUsr failed! " + e.getMessage(), e);      }    }    return null;  }}
public class LoginHdl implements 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(ChatRoomView.class).create().display();          ClientHolder.getClient().keepAlive(rm.getTo()); // KEEP...        } else {          Container container = Router.getView(RegisterAndLoginView.class).container();          if (container != null) {            // show error            JOptionPane.showMessageDialog(container, rm.getMessage());          }        }      } catch (Exception e) {        LoggerUtil.error("Handle login failed! " + e.getMessage(), e);      }    }    return null;  }}
public class NotifyHdl implements Handler {  @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(ChatRoomView.class).getComponent(ChatRoomView.LISTUSRLIST);          if (comp instanceof 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("Handle nofity failed! " + 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));      }    }    return list;  }}
public class RegisterHdl implements Handler {  @Override  public Object handle(Object obj) {    if (obj != null) {      try {        ReturnMessage rm = JSON.parseObject(obj.toString(),ReturnMessage.class);        Container container = Router.getView(RegisterAndLoginView.class).container();        if (container != null) {          if (rm.isSuccess()) {            JOptionPane.showMessageDialog(container, rm.getContent());          } else {            JOptionPane.showMessageDialog(container, rm.getMessage());          }        }      } catch (Exception e) {        LoggerUtil.error("Handle register failed! " + e.getMessage(), e);      }    }    return null;  }}
public class TipHdl implements 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(ChatRoomView.class).getComponent(ChatRoomView.CHATTABBED);          if (comp instanceof JTabbedPane) {            JTabbedPane tab = (JTabbedPane) comp;            int index = tab.indexOfTab(tabKey);            if (index == -1) {              tab.addTab(tabKey, ResultHolder.get(tabKey).getScrollPane());            }            JTextArea textArea = ResultHolder.get(tabKey).getTextArea();            textArea.setText(new StringBuffer()                .append(textArea.getText()).append(System.lineSeparator()).append(System.lineSeparator())                .append(" [").append(m.getOwner()).append("] : ").append(System.lineSeparator())                .append(tip)                .toString());            // SCROLL TO BOTTOM            textArea.setCaretPosition(textArea.getText().length());          }        }      } catch (Exception e) {        LoggerUtil.error("Handle tip failed! " + e.getMessage(), e);      }    }    return null;  }}

 對于Socket通訊模塊還有一個類,那就是ClientHolder,這個類用于存儲當前Client,跟服務端的SocketHolder是類似的。

 /** * @author yaolin */public class ClientHolder {  public static Client client;  public static Client getClient() {    return client;  }  public static void setClient(Client client) {    ClientHolder.client = client;  }}

UI模塊具體實現(xiàn):

上面記錄了socket通訊模塊的設計,接下來記錄一下UI的設計模塊,我不打算自己寫UI,畢竟自己寫出來的太丑了,所以后期可能會叫同學或朋友幫忙敲一下,所以我將UI的事件處理都交由Action去處理,將UI設計和事件響應簡單分離,所有UI繼承JFrame并實現(xiàn)View接口,上面的Handler實現(xiàn)類通過Router獲取(存在則直接返回,不存在則創(chuàng)建并存儲)指定的UI,View中提供了UI的創(chuàng)建create()、獲取container()、獲取UI中的組件getComponent(),顯示display(),回收trash();ResultWrapper和ResultHolder只是為了創(chuàng)建和存儲聊天選項卡。設計如下:

[Router.java、View.java]

所有UI繼承JFrame并實現(xiàn)View接口,Handler實現(xiàn)類通過Router獲取(存在則直接返回,不存在則創(chuàng)建并存儲)指定的UI,View中提供了UI的創(chuàng)建create()、獲取container()、獲取UI中的組件getComponent(),顯示display(),回收trash(),具體實現(xiàn)如下:

/** * View 路由 * @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 failed! " + e.getMessage(), e);      }    }    return v;  }}
/** * 所有界面的規(guī)范接口 * @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]

由于不想自己寫UI,我這里只是簡單的寫了兩個UI界面,分別是注冊和登陸界面、聊天界面,這里給出兩個丑丑的界面:

注冊登錄界面

聊天界面

下面給出這兩個這界面的具體代碼:

/** * 注冊、登陸 * @author yaolin */public class RegisterAndLoginView extends JFrame implements View {  private static final long serialVersionUID = 6322088074312546736L;  private final RegisterAndLoginAction action = new RegisterAndLoginAction();    private static boolean CREATE = false;    @Override  public View create() {    if (! CREATE) {      init();      CREATE = true;    }    return this;  }    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);        // Container    JPanel panel = new JPanel();    panel.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);    panel.add(lbUsername);    panel.add(tfUsername);    // passsword    JLabel lbPassword = new JLabel(I18N.TEXT_PASSWORD);    lbPassword.setBounds(100, 120, 200, 30);    final JPasswordField pfPassword = new JPasswordField();    pfPassword.setBounds(150, 120, 230, 30);    panel.add(lbPassword);    panel.add(pfPassword);    // btnRegister    JButton btnRegister = new JButton(I18N.BTN_REGISTER);    btnRegister.setBounds(100, 175, 80, 30);    // btnLogin    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);    panel.add(btnRegister);    panel.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 of addKeyListener        btnRegister.addActionListener(new ActionListener() {      public void actionPerformed(final ActionEvent e) {        if (StringUtil.isEmpty(tfUsername.getText())             || StringUtil.isEmpty(new String(pfPassword.getPassword()))) {          JOptionPane.showMessageDialog(getContentPane(), I18N.INFO_REGISTER_EMPTY_DATA);          return ;        }        action.handleRegister(tfUsername.getText(), new String(pfPassword.getPassword()));      }    });// end of addActionListener        btnLogin.addActionListener(new ActionListener() {      public void actionPerformed(final ActionEvent e) {        if (StringUtil.isEmpty(tfUsername.getText())             || StringUtil.isEmpty(new String(pfPassword.getPassword()))) {          JOptionPane.showMessageDialog(getContentPane(), I18N.INFO_LOGIN_EMPTY_DATA);          return ;        }        action.handleLogin(tfUsername.getText(), new String(pfPassword.getPassword()));      }    });// end of addActionListener        btnExit.addActionListener(new ActionListener() {      public void actionPerformed(final ActionEvent e) {        System.exit(0);      }    });// end of addActionListener    getContentPane().add(panel);    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  }}
/** * Client 聊天窗口 *  * @author yaolin */public class ChatRoomView extends JFrame implements View {  private static final long serialVersionUID = -4515831172899054818L;  public static final String LISTUSRLIST = "LISTUSRLIST";  public static final String CHATTABBED = "CHATTABBED";  private static boolean CREATE = false;  private ChatRoomAction action = new ChatRoomAction();  private JList<String> listUsrList = null;  private JTabbedPane chatTabbed = null;  @Override  public View create() {    if (!CREATE) {      init();      CREATE = true;    }    return this;  }  public Container container() {    create();    return getContentPane();  }  @Override  public JComponent getComponent(String key) {    create();    switch (key) {    case LISTUSRLIST:      return listUsrList;    case CHATTABBED:      return 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() {    // FILE SELECTOR    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); // JUST FOR 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(new 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); // JUST FOR 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(chatTabbed.getSelectedIndex()))) {          int returnVal = fileChooser.showOpenDialog(ChatRoomView.this);          if (returnVal == JFileChooser.APPROVE_OPTION) {            File file = fileChooser.getSelectedFile();            action.upload(chatTabbed.getTitleAt(chatTabbed.getSelectedIndex()), file);          }        } else {          JOptionPane.showMessageDialog(getContentPane(), I18N.INFO_FILE_TO_ALL_ERROR);        }      }    });    btnSend.addActionListener(new ActionListener() {      public void actionPerformed(final ActionEvent e) {        if (StringUtil.isNotEmpty(taSend.getText())) {          action.send(chatTabbed.getTitleAt(chatTabbed.getSelectedIndex()), taSend.getText());          taSend.setText(null);        }      }    });    panel.add(south, BorderLayout.SOUTH);    return panel;  }  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) { // chat to        if (chatTabbed.indexOfTab(listUsrList.getSelectedValue()) == -1            && listUsrList.getSelectedValue() != null            && !listUsrList.getSelectedValue().equals(ClientHolder.getClient().getFrom())) {          chatTabbed.addTab(listUsrList.getSelectedValue(),              ResultHolder.get(listUsrList.getSelectedValue()).getScrollPane());          chatTabbed.setSelectedIndex(chatTabbed.indexOfTab(listUsrList.getSelectedValue()));        }      }    });    return listUsrList;  }}

[RegisterAndLoginAction.java、ChatRoomAction.java]

這里UI的事件處理都交由Action去處理,將UI設計和事件響應簡單分離,RegisterAndLoginView的事件由RegisterAndLoginAction處理,ChatRoomView的事件由ChatRoomAction處理。具體實現(xiàn)如下:

public class RegisterAndLoginAction {  public void handleRegister(String username, String password) {    if (StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {      return;    }    RegisterMessage message = new RegisterMessage()        .setUsername(username)        .setPassword(password);    message.setFrom(username);    SendHelper.send(ClientHolder.getClient().getSocket(), message);  }      public void handleLogin(String username, String password) {    if (StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {      return;    }    LoginMessage message = new LoginMessage()        .setUsername(username)        .setPassword(password);    message.setFrom(username);    SendHelper.send(ClientHolder.getClient().getSocket(), message);  }}

對于UI設計還有兩個類,分別是ResultHolder和ResultWrapper,ResultWrapper和ResultHolder只是為了創(chuàng)建和存儲聊天選項卡,具體實現(xiàn)如下:

public class 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;  }}
public class ResultHolder {  private static Map<String, ResultWrapper> listResultWrapper = new HashMap<String,ResultWrapper>();    public static void put(String key, ResultWrapper wrapper) {    listResultWrapper.put(key, wrapper);  }    public static ResultWrapper get(String key) {    ResultWrapper wrapper = listResultWrapper.get(key);    if (wrapper == null) {      wrapper = create();      put(key, wrapper);    }    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;  }}

最后的最后給出,客戶端運行的入口:

/** *  * @author yaolin * */public class NiloayChat {  public static void main(String[] args) {    View v = Router.getView(RegisterAndLoginView.class).create();    try {      v.display();      Client client = new Client(new DefaultCallback());      client.start();      ClientHolder.setClient(client);    } catch (IOException e) {      JOptionPane.showMessageDialog(v.container(), e.getMessage());    }  }}

demo下載地址:demo

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 兰州市| 金寨县| 绥中县| 太和县| 宜良县| 固原市| 五指山市| 湟中县| 东平县| 黔东| 松江区| 横峰县| 紫金县| 澄江县| 阳西县| 青神县| 宁德市| 宣恩县| 宁波市| 云梦县| 盐城市| 连城县| 安泽县| 德钦县| 拜泉县| 武威市| 上饶县| 泸西县| 桦甸市| 石阡县| 巴塘县| 北碚区| 青神县| 红河县| 吉首市| 舞阳县| 松江区| 崇州市| 萨迦县| 蛟河市| 北宁市|