以前に書かれた:
先週末、私が書いたシンプルなソケットチャットプログラムの初期デザインとサーバー側の詳細設計を記録するのに時間がかかりました。私はついに火曜日の卒業前に撮影したソフト試験証明書を待ちました、そして、私は残業の日を過ごしました。今日はたまたま金曜日であり、クライアントと共通モジュールの詳細な設計を記録する予定でした。今週末から他のことで忙しいからです。
デザイン:
クライアントの設計は、主にソケット通信モジュールの設計とUI関連の設計という2つの部分に分かれています。
クライアントソケット通信設計:
ここの設計は、実際にはサーバーの設計に似ています。違いは、サーバーがハートビートパケットを受信し、クライアントがハートビートパケットを送信することです。クライアントは1つのサーバーとのみ通信するため(クライアント間の通信もサーバーによって配信されます)、サイズ2のスレッドプールのみがこれらの2つのことを処理するために使用されます(NewFixedThreadPool(2))。対応する処理クラスは、ReceIveListenerとKeepalivedogです。 ReceiveListenerが初期化されると、クライアントへのコールバックがサーバーメッセージを受信するとコールバックが送信されます。コールバックのデフォルトの実装はdefaultCallBackです。 DefaultCallbackは、異なるイベントに従って、HFを介して異なるハンドラーに配布されます。クライアントホルダーは、現在のクライアント情報を保存します。デザインは次のとおりです。
ソケット通信モジュールの特定の実装:
[client.java]
クライアントは、サーバーに接続するためのクライアントへの入り口です。クライアントを作成するには、クライアントがサーバーメッセージを受信したときにコールバックとしてコールバックを指定する必要があります。次に、クライアントのstart()メソッドがサーバーの聴取を開始します(ReceiveListener)。 ReceiveListenerがサーバーから送信されたデータを受信すると、Callback(Callback)メソッドが呼び出され、処理されます。同時に、クライアントは、サーバーにまだ接続されていることをサーバーに通知するために、ハートビートパケットを送信する必要があります。ハートビートパケットはクライアントによって保持されます。 Alive()は、Keepalivedogによって開始および実装されています。これらの2つのステップは、固定サイズの2つのnewfixedthreadpool(2)のスレッドプールによって実行されます。Healbeatedthreadpool(1)とNewscheduledthreadpool(1)を使用するために、Heartbeatパッケージが定期的に送信されるため、ここでnewfixedthreadthreadpool(1)を使用する方が合理的かもしれません。クライアントの特定のコードは次のとおりです(他の2つの方法は、ソケットと現在のソケットが属するユーザーを取得するためにここで公開されます):
/** * client * @author yaolin * */public class client {プライベート最終ソケットソケット;からのプライベート文字列;プライベート最終executorserviceプール。プライベートファイナルコールバックコールバック。パブリッククライアント(コールバックコールバック)はiOExceptionをスローします{this.socket = new Socket(custrantValue.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(from){this.from = from; pool.execute(new Keepalivedog(socket、from)); } public socket getSocket(){return socket; } public string getFrom(){return; }}[Keepalivedog.java]
クライアントがサーバーとの接続を確立した後(このプログラムは、ログインが成功した後、クライアントのソケットがサーバーのソックホルダーによって管理されるため、ログインの成功を参照します)、サーバーがまだサーバーに接触していることをサーバーに送信するためにサーバーにハートビートパケットを送信する必要があります)。 KeepAlivedogのコードは次のように実装されます(後でニュースチェドルされたThreadpoolに調整される可能性があるため、ここのコードも調整されます):
/*** KeepAlivedog:このクライアントが実行されているサーバーに伝えます。 * * @author yaolin */public class keepalivedog emplements runnable {private final socket socket;プライベートファイナルストリングから; 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(メッセージ)); out.flush(); thread.sleep(custrentValue.keep_alive_period * 1000); } catch(Exception e){loggerutil.error( "クライアント送信メッセージの失敗!" + e.getmessage()、e); }}}}[Receivelistener.java]
クライアントのstart()メソッドは、サーバーの聴取を開始し、receiveListenerによって実装されます。サーバーからメッセージを受信した後、ReceIveListenerはCallbackのDowork()メソッドを呼び出して、コールバックが特定のビジネスロジックを処理させます。したがって、ReceIveListenerはサーバー上のメッセージを聞くことのみを担当し、特定の処理はコールバックによって処理されます。ここでは、メッセージタイプがファイルタイプである場合、実行間隔を構成するために眠り、コールバックのDoworkが次のループを直接入力するのではなく、サーバーへのファイルフローを読み取ることができることに言及する必要があります。ここの設計はサーバーに似ています。 ReceIveListenerの特定の実装コードは次のとおりです。
パブリッククラスは、runnable {private final socket Socket;プライベートファイナルコールバックコールバック。 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();文字列line = null; stringbuffer sb = null; if(is.abaible()> 0){bufferedreader bufr = new BufferedReader(new inputstreamReader(is)); sb = new StringBuffer(); while(is.abailable()> 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){// file loggerutil.trachを受信するために一時停止( "client:pause to spause file"); thread.sleep(custrentValue.message_period); }} else {thread.sleep(custrentValue.message_period); }} catch(Exception E){loggerutil.error( "クライアント送信メッセージが失敗!" + e.getmessage()、e); }}}}}}}[callback.java、defaultcallback.java]
上記から、クライアントのメッセージの処理はコールバックコールバックであり、そのコールバックは単なるインターフェースであることがわかります。すべてのコールバック実装は、ニーズに応じてメッセージを処理するインターフェイスを実装します。ここで、コールバックのデフォルトの実装はdefaultCallBackです。 DefaultCallbackは、3種類のメッセージ、つまりチャットメッセージ、ファイルメッセージ、および返信メッセージのみを処理します。チャットメッセージの場合、DefaultCallbackはUIのルータールートを介して対応するインターフェイスを取得し(詳細については以下のUIデザインを参照)、対応するチャットボックスにメッセージを表示します。ファイルメッセージの場合、defaultCallbackは構成で指定されたパスにファイルを書き込みます(ここではユーザーの許可なしにファイルが受信されます。このデザインはあまり友好的ではないので、今のところ)。返されるメッセージの場合、DefaultCallbackは、RETURNメッセージのキーに従って異なるハンドラーに呼び出されます。特定のコードは次のとおりです。
パブリックインターフェイスコールバック{public void dowork(socket server、object data); }パブリッククラスDefaultCallbackは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);壊す; case messageType.file:handleFileMessage(サーバー、データ);壊す; Case MessageType.Return:HandlereturnMessage(data);壊す; }}} private void handlechatmessage(object data){chatmessage m = json.parseobject(data.tostring()、chatmessage.class); string tabkey = m.getfrom(); // if(comp instance of 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())。 // 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(custrentValue.client_receive_dir); if(!dir.exists()){dir.mkdirs(); } os = new fileoutputStream(new file(pathutil.combination(custrantValue.client_receive_dir、new date()。getTime() + message.getName())); int total = 0; while(!server.isclosed()){if(is.abailable()> 0){byte [] buff = new byte [construmentValue.buff_size]; int len = -1; while(is.abailable()> 0 &&(len = is.read(buff))!= -1){os.write(buff、0、len);合計 += len; loggerutil.debug( "buff [" + len + "]"); } os.flush(); if(total> = message.getSize()){loggerutil.info( "receive buff [ok]");壊す; }}}}}}} catch(Exception e){loggerutil.error( "File Failed!" + e.getMessage()、e); }最後に{if(os!= null){try {os.close(); } catch(例外無視){} 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://通知クライアントにusrリストhf.gethandler(key.notify).handle(data);壊す; case key.login:hf.gethandler(key.login).handle(data);壊す; case key.register:hf.gethandler(key.register).handle(data);壊す; case key.listuser:hf.gethandler(key.listuser).handle(data);壊す;ケースkey.tip:hf.gethandler(key.tip).handle(data);壊す; }}}}[handler.java、hf.java、listuserhdl.java ...]
ハンドラーコンポーネントは、サーバーの返信メッセージタイプのメッセージを処理する責任があります。 DefaultCallbackは、異なるキーに従って異なるハンドラーにメッセージを配布します。これは単純な工場コンポーネントでもあります。サーバーが受信したデータに似ています。完全なクラス図は次のとおりです。
このセクションのコードを以下に示します。スペースを減らすために、ハンドラーによって実装されたすべてのコードが収集されます。
パブリックインターフェイスハンドラー{パブリックオブジェクトハンドル(オブジェクトOBJ); } public class HF {public static Handler gethandler(string key){switch(key){case key.notify:return new notifyhdl(); case key.login:new loginhdl()を返します。 case key.register:new RegisterHdl()を返します。 case key.listuser:new listuserhdl()を返します。 case key.tip:new tiphdl()を返します。 } nullを返します。 }} public class 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){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、custrantValue.to_all); listusrlist.setlistdata(listuser.toarray(new String [] {})); }}} catch(Exception e){loggerutil.error( "handle listusr failed!" + e.getmessage()、e); }} nullを返します。 }}パブリッククラスのloginhdlはハンドラーを実装します{@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()); //キープ...} else {container container = router.getView(RegisterAndLoginView.Class).Container(); if(container!= null){//エラーjoptionpane.showmessageialog(container、rm.getmessage()); }}} catch(Exception e){loggerutil.error( "ハンドルログインに失敗!" + e.getmessage()、e); }} 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(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(custrentValue.to_all); collections.sort(listuser); listuser.add(0、custrantValue.to_all); }} else {listuser.remove(dto.getusername()); } listusrlist.setlistdata(listuser.toarray(new String [] {})); }}} catch(Exception e){loggerutil.error( "ハンドルnofityが失敗!" + e.getmessage()、e); }} 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はハンドラーを実装しています{@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.showmessageialog(container、rm.getContent()); } else {joptionpane.showMessageAlog(container、rm.getMessage()); }}} catch(Exception e){loggerutil.error( "resigch Register failed!" + e.getmessage()、e); }} nullを返します。 }}パブリッククラスのTIPHDLはハンドラーを実装します{@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();文字列ティップ= m.getContent()。toString(); jcomponent comp = router.getView(chatroomview.class).getComponent(chatroomview.chattabbed); if(comp instance of 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())。 // bottom textarea.setcaretposition(textarea.getText()。length())にスクロールします。 }}} catch(Exception e){loggerutil.error( "ハンドルチップが失敗!" + e.getmessage()、e); }} nullを返します。 }}ソケット通信モジュールには別のクラス、つまり、クライアントホルダーがあります。クライアントは、サーバー上のソックソルダーに似た現在のクライアントを保存するために使用されます。
/** * @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モジュールの特定の実装:
上記は、ソケット通信モジュールの設計を記録します。次に、UIの設計モジュールを記録します。私は自分でUIを書くつもりはありません。結局のところ、執筆はあまりにも醜いので、クラスメートや友人に後でそれをノックするのを手伝ってもらうように頼むかもしれません。そこで、UIイベント処理を処理して処理し、UIの設計とイベントの応答を分離します。すべてのUISはJFrameを継承し、ビューインターフェイスを実装します。上記のハンドラー実装クラスはルーターを介して取得されます(存在する場合は直接返され、存在しない場合は作成および保存されます)。このビューは、UIの作成()、コンテナ()を取得し、UI getComponent()、display display()、およびリサイクルゴミ()のコンポーネントを取得します。 ResultWrapperとResultholderは、チャットタブを作成および保存するためだけです。デザインは次のとおりです。
[router.java、view.java]
すべてのUISはJFrameを継承し、ビューインターフェイスを実装します。ハンドラー実装クラスは、指定されたUIを介してルーターを介して取得します(存在する場合は直接戻り、存在しない場合は作成および保存します)。このビューは、UIの作成()を提供し、Container()を取得し、UI getComponent()のコンポーネントを取得し、Display()を表示し、ゴミをリサイクルします。特定の実装は次のとおりです。
/*** view route* @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( "view failed!" + e.getmessage()、e); }} return v; }} /** *すべてのインターフェイスの正規インターフェイス * @author yaolin * */publicインターフェイスビュー{/** * * */public View Create(); / ** * */パブリックコンテナコンテナ(); / ** * @param key */ public jcomponent getComponent(string key); / ** * */ public void display(); / ** * */ public void trash(); }[Registerandloginview.java、chatroomview.java]
私は自分でUIを書きたくないので、ここに2つのUIインターフェイス、つまり登録とログインインターフェイスとチャットインターフェイスを書いただけです。ここに2つの醜いインターフェイスがあります:
ログインインターフェイスを登録します
チャットインターフェイス
以下は、これら2つのインターフェイスの特定のコードです。
/**登録とログイン* @Author Yaolin*/public class RegisterandLoginView拡張jFrame実装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; }これを返します。 } 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(){//属性setSize(500、300); setSresizable(false); setLocationRelativeto(null); // container jpanelパネル= 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); //パスワード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();}}); // Void ActionPerformed(final ActionEvent e){if(stringutil.isempty.getText())|| stringutil.isempty(pfpassword.getPassword())){joptionpane.showmessageialog( action.handleregister(tfusername.getText()、new String(pfpassword.getPassword()); || Isempty(pfpassword.getPassword())){getContentPane()、i18n.info_login_empty_data)。 }}); // addactionListener btnexit.addactionlistener(new ActionListener(){public void ActionPerformed(final ActionEvent e){System.Exit(0); }}); // addactionlistener getContentPane()。add(panel); setDefaultCloseoperation(jframe.exit_on_close); }} /** *クライアントチャットウィンドウ * * @author yaolin */public class chatroomview拡張jframe exmments view {private static final long serialversionuid = -4515831172899054818l; public static final string listusrlist = "listusrlist"; public static final string chattabbed = "chattabbed"; private static boolean create = false;プライベートチャットルームアクション= new Chatroomaction(); private jlist <string> listusrlist = null; private 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;ケースチャタブ:チャッタブを返します。 } nullを返します。 } @Override public void display(){setVisible(true); } @Override public void trash(){dispose(); } public void init(){settitle(i18n.text_app_name); SetSize(800、600); setSresizable(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パネル= new jPanel(new borderlayout()); // center chattabbed = new jtabbedpane(); chattabbed.addtab(construmentValue.to_all、resultholder.get(custrantValue.to_all).getScrollpane()); panel.add(chattabbed、borderlayout.center); // South JPanel South = new JPanel(new borderlayout()); //サウス - ファイル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); // South -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ボトム= new jPanel(new borderlayout()); bottom.add(new Jlabel()、borderlayout.center); // PADDING JBUTTON BTNSEND = new JButton(i18n.btn_send); bottom.add(btnsend、borderlayout.east); south.add(bottom、borderlayout.south); btnupload.addactionlistener(new ActionListener(){public void actionPerformed(final actionEvent e){if(!construmentValue.to_all.equals(chattabbed.getTitleat(chattabbed.getSelectendEx())){int returngview = filechooser.showendiarog(chat); jfilechooser.approve_option){file filechooser.getSelectedFile(); 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.gettextext(); }}}); Panel.Add(南、Borderlayout.South);返品パネル。 } private jcomponent createusrlistview(){listusrlist = new jlist <string>(); listusrlist.setborder(new lineborder(color.blue)); listusrlist.setlistdata(new String [] {custrentValue.to_all}); listusrlist.setfixedcellwidth(200); listusrlist.setfixedcellheight(30); listusrlist.addlistselectionlistener(new listselectionListener(){@Override public void valueChanged(listSelectionEvent e){//チャットif !listusrlist.getSelectedValue()。 chattabbed.setselectedindex(chattabbed.indexoftab(listusrlist.getSelectedValue())); RETURN LISTUSRLIST; }}[RegistandLoginaction.java、chatroomaction.java]
ここでは、UIイベント処理はアクションによって処理されます。これにより、UIの設計とイベントの応答が単純に分離されています。 RegisterAndLoginViewのイベントはRegisterAndLoginactionによって処理され、チャットルームビューのイベントはチャットルームで処理されます。特定の実装は次のとおりです。
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デザインには他に2つのクラスがあります。つまり、ResultholderとResultWrapperです。 ResultWrapperとResultholderは、チャットタブを作成および保存するためだけです。特定の実装は次のとおりです。
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); }ラッパーを返します。 } private static resultwrapper create(){jtextarea resultTextarea = new jtextarea(); restertextarea.setedable(false); restertextarea.setborder(new lineborder(color.blue)); jscrollpane scrollpane = new jscrollpane(restertextarea); scrollpane.sethorizontalscrollbarpolicy(scrollpaneconstants.horizontal_scrollbar_never); scrollpane.setverticalscrollbarpolicy(scrollpaneconstants.vertical_scrollbar_as_needed); resultswrapper wrapper = new resultWrapper(scrollpane、resultTextarea);ラッパーを返します。 }}最後の1つが与えられます。クライアントが実行するエントリ:
/** * * @author yaolin * */public class niloaychat {public static void main(string [] args){vise v = router.getView(Registerandloginview.class).create(); try {v.display();クライアントクライアント= new Client(new DefaultCallback()); client.start(); clientHolder.setClient(クライアント); } catch(ioexception e){joptionpane.showmessageialog(v.container()、e.getmessage()); }}}デモのダウンロードアドレス:デモ
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。