Ditulis sebelumnya:
Akhir pekan lalu saya meluangkan waktu untuk merekam desain awal dan desain terperinci sisi server dari program obrolan soket sederhana yang saya tulis. Saya akhirnya menunggu sertifikat ujian lunak yang saya ambil sebelum lulus pada hari Selasa, dan kemudian saya menghabiskan hari lembur. Hari ini kebetulan hari Jumat, dan saya berencana untuk merekam desain rinci klien dan modul umum, karena saya akan sibuk dengan hal -hal lain mulai akhir pekan ini.
desain:
Desain klien terutama dibagi menjadi dua bagian, yaitu desain modul komunikasi soket dan desain terkait UI.
Desain Komunikasi Soket Klien:
Desain di sini sebenarnya mirip dengan desain server. Perbedaannya adalah bahwa server menerima paket detak jantung, sementara klien mengirimkan paket detak jantung. Karena klien hanya berkomunikasi dengan satu server (komunikasi antara klien juga didistribusikan oleh server), hanya kumpulan utas ukuran 2 yang digunakan untuk menangani kedua hal ini (NewfixedThreadPool (2)). Kelas pemrosesan yang sesuai adalah penerima dan keepalivedog. Ketika penerima diinisialisasi, panggilan balik dikirim sebagai panggilan balik ke klien menerima pesan server. Implementasi callback default adalah DefaultCallback. DefaultCallback didistribusikan ke penangan yang berbeda melalui HF sesuai dengan berbagai peristiwa. ClientHolder menyimpan informasi klien saat ini. Desainnya adalah sebagai berikut:
Implementasi spesifik dari modul komunikasi soket:
[Klien.java]
Klien adalah pintu masuk ke klien untuk terhubung ke server. Untuk membuat klien, Anda perlu menentukan panggilan balik sebagai panggilan balik ketika klien menerima pesan server. Kemudian metode start () klien memulai mendengarkan server (receivelistener). Ketika penerima menerima data yang dikirim oleh server, metode callback (callback) dipanggil untuk memprosesnya. Pada saat yang sama, klien juga perlu mengirim paket detak jantung untuk memberi tahu server bahwa itu masih terhubung ke server. Paket detak jantung disimpan oleh klien. Alive () dimulai dan diimplementasikan oleh Keepaliveedog; Kedua langkah ini dieksekusi oleh kumpulan benang NewfixedThreadpool (2) dengan ukuran tetap 2. Mungkin lebih masuk akal untuk menggunakan NewfixedThreadpool (1) dan NewsCheduledThreadPool (1) di sini untuk menangani, karena Paket Detak Jantung dikirimkan secara teratur, dan ini adalah bagaimana server memaksakannya (penyesuaian selanjutnya). Kode spesifik klien adalah sebagai berikut (dua metode lainnya diekspos di sini untuk mendapatkan soket dan pengguna yang menjadi milik soket saat ini):
/** * klien * @author yaolin * */Klien kelas publik {soket soket final privat; string pribadi dari; Pool Final ExecutorService Pribadi; Panggilan balik balik final pribadi; klien publik (callback callback) melempar ioException {this.socket = soket baru (constantValue.server_ip, constantValue.server_port); this.pool = executors.newfixedThreadpool (2); this.callback = callback; } public void start () {pool.execute (new receivelistener (soket, callback)); } public void KeepAlive (string from) {this.from = from; pool.execute (Keepalivedog baru (soket, dari)); } public socket getSocket () {return socket; } public String getFrom () {return from; }}[Keepalivedog.java]
Setelah klien membuat koneksi dengan server (program ini mengacu pada login yang berhasil, karena soket klien akan dikelola oleh socketHolder server setelah login berhasil), perlu untuk mengirim paket detak jantung ke server setiap saat untuk memberi tahu server bahwa itu masih kontak dengan server, jika tidak server akan membuang soket tanpa detail setelah periode waktu waktu (lihat server. Kode Keepaliveedog diimplementasikan sebagai berikut (dapat disesuaikan dengan newscheduledthreadpool (1) kemudian, sehingga kode di sini juga akan disesuaikan):
/*** Keepaliveedog: Tell Server Klien ini berjalan; * * @Author Yaolin */Kelas Publik Keepalivedog mengimplementasikan runnable {soket soket final privat; string final pribadi dari; Public Keepalivedog (soket soket, string dari) {this.socket = soket; this.from = from; } @Override public void run () {while (socket! = Null &&! Socket.isclosed ()) {coba {printwriter out = printwriter baru (socket.getoutputStream ()); Alivemessage pesan = alivemessage baru (); pesan.setFrom (dari); out.println (json.toJson (pesan)); out.flush (); Thread.sleep (constantValue.Eke_alive_period * 1000); } catch (Exception e) {loggerutil.Error ("Klien Kirim pesan gagal!" + e.getMessage (), e); }}}}[Receivelistener.java]
Metode start () klien memulai mendengarkan server dan diimplementasikan oleh penerima. Setelah menerima pesan dari server, penerima akan memanggil kembali metode Callback's Dowork () untuk membiarkan panggilan balik menangani logika bisnis tertentu. Oleh karena itu, penerima hanya bertanggung jawab untuk mendengarkan pesan di server, dan pemrosesan spesifik ditangani dengan panggilan balik. Harus disebutkan di sini bahwa ketika jenis pesan adalah jenis file, itu akan tidur untuk mengonfigurasi interval eksekusi, sehingga dowork di callback dapat membaca aliran file ke server alih -alih langsung memasukkan loop berikutnya. Desain di sini mirip dengan server. Kode implementasi spesifik penerima adalah sebagai berikut:
penerima kelas publik mengimplementasikan runnable {soket soket final privat; Panggilan balik balik final pribadi; public receivelistener (soket soket, panggilan balik panggilan balik) {this.socket = soket; this.callback = callback; } @Override public void run () {if (socket! = Null) {while (! Socket.isclosed ()) {coba {inputStream adalah = socket.getInputStream (); Garis string = null; StringBuffer SB = null; if (is.available ()> 0) {bufferedReader bufr = new buferedReader (inputStreamReader baru (IS)); SB = StringBuffer baru (); while (is.available ()> 0 && (line = bufr.readline ())! = null) {sb.append (line); } Loggerutil.trach ("terima [" + sb.tostring () + "] di" + tanggal baru ()); callback.dowork (soket, sb.tostring ()); Basemessage Message = json.parseObject (sb.tostring (), Basemessage.class); if (message.getType () == messageType.file) {// jeda untuk menerima file loggerutil.trach ("Klien: jeda untuk menerima file"); Thread.sleep (constantValue.Message_period); }} else {thread.sleep (constantValue.message_period); }} catch (Exception e) {loggerutil.Error ("Klien Kirim pesan gagal!" + e.getMessage (), e); }}}}}}}[Callback.java, defaultcallback.java]
Dari yang di atas, kita dapat melihat bahwa pemrosesan pesan klien adalah panggilan balik balik, dan panggilan baliknya hanyalah antarmuka. Semua implementasi panggilan balik mengimplementasikan antarmuka untuk memproses pesan sesuai dengan kebutuhan mereka. Di sini, implementasi default dari Callback adalah DefaultCallback. DefaultCallback hanya memproses tiga jenis pesan, yaitu pesan obrolan, pesan file, dan mengembalikan pesan. Untuk pesan obrolan, DefaultCallback akan mendapatkan antarmuka yang sesuai melalui rute router di UI (lihat desain UI di bawah ini untuk detailnya), dan kemudian menampilkan pesan di kotak obrolan yang sesuai; Untuk pesan file, DefaultCallback akan menulis file ke jalur yang ditentukan dalam konfigurasi (file diterima tanpa izin pengguna di sini. Desain ini tidak terlalu ramah, jadi untuk saat ini); Untuk pesan pengembalian, DefaultCallback akan dipanggil ke penangan yang berbeda sesuai dengan kunci dalam pesan pengembalian. Kode spesifiknya adalah sebagai berikut:
Callback antarmuka publik {public void dowork (server soket, data objek); } kelas publik DefaultCallback mengimplementasikan callback {@Override public void dowork (server soket, data objek) {if (data! = null) {Basemessage pesan = json.parseObject (data.toString (), Basemessage.class); switch (message.getType ()) {case messageType.chat: handleChatMessage (data); merusak; case messageType.file: handlefileMessage (server, data); merusak; case messagetype.return: handlereturnmessage (data); merusak; }}} private void handlechatmessage (data objek) {chatmessage m = json.parseObject (data.toString (), chatmessage.class); String tabkey = m.getFrom (); // dari 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 (StringBuffer baru () .Append (TextAreA.getText ()). Append (System.LinesEparator ()). Append (System.LinesEparator ()) .Append ("[") .Peramping (m.getOwner ()). Append ("]:") .Prepend (System.lineseparator (M.GetOwner ()). .toString ()); // Gulir ke TextArea Bawah.SetCaretPosition (TextArea.getText (). Length ()); }} private void handlefileMessage (server soket, data objek) {filemessage pesan = json.parseObject (data.toString (), filemessage.class); if (message.getSize ()> 0) {outputStream os = null; coba {if (server! = null) {inputStream is = server.getInputStream (); File dir = file baru (constantValue.client_receive_dir); if (! dir.exists ()) {dir.mkdirs (); } os = FileOutputStream baru (file baru (pathutil.combination (constantValue.client_receive_dir, tanggal baru (). gettime () + message.getName ())))); int total = 0; while (! server.isclosed ()) {if (is.available ()> 0) {byte [] buff = byte baru [constantValue.buff_size]; int len = -1; while (is.available ()> 0 && (len = is.read (buff))! = -1) {os.write (buff, 0, len); Total += len; Loggerutil.debug ("Terima buff [" + len + "]"); } os.flush (); if (total> = message.getSize ()) {loggerutil.info ("Terima buff [ok]"); merusak; }}}}}} catch (pengecualian e) {loggerutil.error ("Terima file gagal!" + e.getMessage (), e); } akhirnya {if (os! = null) {coba {os.close (); } catch (pengecualian abaikan) {} os = null; }}}} private void handlereTurnMessage (data objek) {returnMessage m = json.parseObject (data.toString (), returnMessage.class); if (stringutil.isnotempty (m.getKey ())) {switch (m.getKey ()) {case key.notify: // beri tahu klien untuk memperbarui daftar usr hf.getHandler (key.notify) .handle (data); merusak; case key.login: hf.getHandler (key.login) .handle (data); merusak; case key.register: hf.getHandler (key.register) .handle (data); merusak; case key.listuser: hf.getHandler (key.listuser) .handle (data); merusak; case key.tip: hf.getHandler (key.tip) .handle (data); merusak; }}}}[Handler.java, hf.java, listuserhdl.java ...]
Komponen Handler bertanggung jawab untuk memproses pesan dari jenis pesan pengembalian server. DefaultCallback mendistribusikan pesan ke penangan yang berbeda sesuai dengan kunci yang berbeda. Ini juga merupakan komponen pabrik sederhana. Ini mirip dengan data yang diterima oleh server. Diagram kelas lengkap adalah sebagai berikut:
Kode untuk bagian ini diberikan di bawah ini. Untuk mengurangi ruang, semua kode yang diterapkan oleh Handler dikumpulkan.
penangan antarmuka publik {pegangan objek publik (objek obj); } kelas publik hf {public static handler getHandler (tombol string) {switch (key) {case key.notify: return new notifyhdl (); case key.login: return new LoginHDl (); case key.register: kembalikan registerhdl baru (); case key.listuser: return new ListUserhdl (); case key.tip: kembalikan tiphdl baru (); } return null; }} kelas publik ListUsERHDL mengimplementasikan penangan {@override handle objek publik (objek obj) {if (obj! = null) {coba {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 instance dari jList) {@suppressWarnings ("uncecked") // jlist <string> listUsrlist = (jList <string>) comp; Daftar <String> listUser = new LinkedList <String> (); listuser.addall (dto.getListuser ()); Collections.sort (listuser); listuser.add (0, constantValue.to_all); listusrlist.setListData (listuser.toArray (string baru [] {})); }}} catch (Exception e) {LogGerutil.Error ("Handle listUsr gagal!" + e.getMessage (), e); }} return null; }} Kelas Publik LoginHDL mengimplementasikan pawang {@Override Handle objek publik (objek obj) {if (obj! = null) {coba {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) {// tunjukkan kesalahan joptionpane.showmessagealog (wadah, rm.getmessage ()); }}} catch (Exception e) {LogGerutil.Error ("Handle Login Gagal!" + E.GetMessage (), e); }} return null; }} kelas publik notifyhdl mengimplementasikan handler {@Override public handle (objek obj) {if (obj! = null) {coba {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 instance dari jList) {@suppressWarnings ("uncecked") // jlist <string> listUsrlist = (jList <string>) comp; Daftar <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 (string baru [] {})); }}} catch (Exception e) {LogGerutil.Error ("Tangani NoFity gagal!" + e.getMessage (), e); }} return null; } Daftar Privat <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)); }} daftar pengembalian; }} Registerhdl kelas publik mengimplementasikan pawang {@Override handle objek publik (objek obj) {if (obj! = null) {coba {returnMessage rm = json.parseObject (obj.toString (), returnMessage.class); Container container = router.getView (registerAndLogInview.class) .container (); if (container! = null) {if (rm.issuccess ()) {joptionpane.showmessagealog (wadah, rm.getContent ()); } else {joptionpane.showmessageDialog (wadah, rm.getMessage ()); }}} catch (Exception e) {loggerutil.error ("Tangani register gagal!" + e.getMessage (), e); }} return null; }} Kelas Publik Tiphdl mengimplementasikan pawang {@Override Handle objek publik (objek obj) {if (obj! = null) {coba {returnMessage m = json.parseObject (obj.toString (), returnMessage.class); if (m.issuccess () && m.getContent ()! = null) {string tabKey = m.getFrom (); Tip String = 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 (StringBuffer baru () .Append (TextAreA.getText ()). Append (System.LinesEparator ()). Append (System.LinesEparator ()) .Append ("[) .Peramping (m.getOwner ()). Append ("]: ") .Pperpendikan () .lineseparator (M.GetOwner ()). // Gulir ke TextArea Bawah.SetCaretPosition (TextArea.getText (). Length ()); }}} catch (Exception e) {loggerutil.error ("Tip pegangan gagal!" + e.getMessage (), e); }} return null; }} Ada kelas lain untuk modul komunikasi soket, yaitu, clientHolder, yang digunakan untuk menyimpan klien saat ini, yang mirip dengan socketHolder di server.
/** * @Author Yaolin */Kelas Publik ClientHolder {Public Static Client; klien public static getClient () {return client; } public static void setClient (klien klien) {clientHolder.client = klien; }}Implementasi spesifik modul UI:
Di atas mencatat desain modul komunikasi soket. Selanjutnya, saya merekam modul desain UI. Saya tidak berencana untuk menulis UI sendiri. Lagi pula, tulisannya terlalu jelek, jadi saya dapat meminta teman sekelas atau teman untuk membantu saya mengetuknya nanti. Jadi saya menyerahkan pemrosesan acara UI untuk bertindak untuk menanganinya, dan cukup pisahkan desain UI dan respons acara. Semua UIS mewarisi JFrame dan mengimplementasikan antarmuka tampilan. Kelas implementasi penangan di atas diperoleh melalui router (akan dikembalikan secara langsung jika ada, dan akan dibuat dan disimpan jika tidak ada). Tampilan menyediakan kreasi UI (), dapatkan wadah (), dapatkan komponen dalam UI getComponent (), display display (), dan mendaur ulang sampah (); Hasil dan resultholder hanya untuk membuat dan menyimpan tab obrolan. Desainnya adalah sebagai berikut:
[Router.java, view.java]
Semua UIS mewarisi JFrame dan mengimplementasikan antarmuka tampilan. Kelas implementasi pawang memperoleh UI yang ditentukan melalui router (kembali secara langsung jika ada, dan itu menciptakan dan menyimpan jika tidak ada). Tampilan menyediakan kreasi UI (), memperoleh wadah (), dan memperoleh komponen di UI getComponent (), display display (), dan mendaur ulang sampah (). Implementasi spesifik adalah sebagai berikut:
/*** Lihat rute* @author yaolin*/router kelas publik {peta statis privat <string, view> listroute = hashmap baru <string, view> (); public static view getView (class <?> clazz) {view v = listroute.get (clazz.getName ()); if (v == null) {coba {v = (view) class.forname (clazz.getName ()). newInstance (); listroute.put (clazz.getName (), v); } catch (exception e) {loggerutil.error ("Buat tampilan gagal!" + e.getMessage (), e); }} return v; }} /** * Antarmuka kanonik untuk semua antarmuka * @author yaolin * */tampilan antarmuka publik {/** * */tampilan publik create (); / ** * */ wadah wadah publik (); / ** * Kunci @param */ jComponent publik getComponent (tombol string); / ** * */ public void display (); / ** * */ public void trash (); }[RegisterAndLoginView.java, chatroomview.java]
Karena saya tidak ingin menulis UI sendiri, saya hanya menulis dua antarmuka UI di sini, yaitu antarmuka pendaftaran dan login dan antarmuka obrolan. Berikut adalah dua antarmuka jelek:
Daftar antarmuka login
Antarmuka obrolan
Berikut ini adalah kode spesifik untuk dua antarmuka ini:
/*** Daftar dan login* @Author Yaolin*/Kelas Publik RegisteranDloginView Memperluas JFrame Implement 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; } kembalikan ini; } wadah wadah publik () {create (); return getContentPane (); } @Override public jComponent getComponent (tombol string) {return null; } @Override public void display () {setVisible (true); } @Override public void trash () {Dispose (); } private void init () {// Atribut setSize (500, 300); setResizable (false); setLocationRelativeto (null); // Panel JPanel Container = JPanel baru (); Panel.setLayout (null); // komponen // nama pengguna 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); // kata sandi jlabel lbpassword = jlabel baru (i18n.text_password); lbpassword.setbounds (100, 120, 200, 30); final jpasswordfield pfpassword = jpasswordfield baru (); pfpassword.setbounds (150, 120, 230, 30); panel.add (lbpassword); Panel.Add (PFPassword); // btnregister jbutton btnregister = jbutton baru (i18n.btn_register); btnregister.setbounds (100, 175, 80, 30); // btnlogin final jbutton btnlogin = jbutton baru (i18n.btn_login); btnlogin.setbounds (200, 175, 80, 30); // btncancel jbutton btnexit = jbutton baru (i18n.btn_exit); btnexit.setbounds (300, 175, 80, 30); panel.add (btnregister); panel.add (btnlogin); panel.add (btnexit); // acara pfpassword.addkeyListener (KeyAdapter baru () {public void keypressed (keyevent final e) {if (e.getKeyCode () == keyevent.vk_enter) btnlogin.doclick ();}); // end addKeylistener btnreger. ActionPerformed (Final ActionEvent e) {if (stringutil.isempty (tfusername.getText ()) || stringutil.isempty (string baru (pfpassword.getPassword ())) {joptionpane.showmessage action.handleregister (tfusername.getText (), string baru (pfpassword.getPassword ())); Stringutil.isempty (string baru (pfpassword.getPassword ())) {joptionpane.showmessagedialog (getContentPane (), i18n.info_login_empty_data); }); // akhir addactionListener Btnexit.addactionListener (actionListener baru () {public void actionPerformed (final actionEvent e) {system.exit (0); }}); // akhir addactionListener getContentPane (). Tambah (panel); setDefaultCloseOperation (jframe.exit_on_close); }} /** * Jendela Obrolan Klien * * @Author Yaolin */Kelas Publik ChatRoomView Memperluas JFrame Menerapkan tampilan {private static final long serialversionuid = -45158311728999054818l; string final public static listUsrlist = "listUsrlist"; string final statis publik chattabbed = "chattabbed"; private static boolean create = false; aksi chatroomaction pribadi = chatroomAction baru (); Private JList <String> listUsrlist = null; private jtabbedpane chattabbed = null; @Override public view create () {if (! Create) {init (); Create = true; } kembalikan ini; } wadah wadah publik () {create (); return getContentPane (); } @Override public jComponent getComponent (tombol string) {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 (borderlayout baru ()); add (createChatPanel (), borderlayout.center); add (createUsrlistView (), borderlayout.east); setDefaultCloseOperation (jframe.exit_on_close); } private jComponent createChatPanel () {// File pemilih final jfilechooser filechooser = new jfilechooser (); Jpanel panel = jpanel baru (borderlayout baru ()); // center chattabbed = new jtabbedpane (); chattabbed.addtab (constantValue.to_all, resultholder.get (constantValue.to_all) .getScrollPane ()); panel.add (chattabbed, borderlayout.center); // jpanel selatan selatan = jpanel baru (borderlayout baru ()); // South - File JPanel Middle = JPanel baru (BorderLayout baru ()); Middle.Add (baru jlabel (), borderlayout.center); // hanya untuk padding jbutton btnupload = jbutton baru (i18n.btn_send_file); Middle.Add (btnupload, borderlayout.east); South.add (tengah, borderlayout.north); // South - TextArea Final JTextArea Tasend = new jTextArea (); Tasend.setCaretColor (Color.Blue); tasend.setMargin (inset baru (10, 10, 10, 10)); Tasend.Setrows (10); South.Add (Tasend, BorderLayout.Center); // selatan - btn jpanel bottom = jpanel baru (borderlayout baru ()); Bottom.Add (baru jlabel (), borderlayout.center); // hanya untuk padding jbutton btnsend = jbutton baru (i18n.btn_send); Bottom.add (btnsend, borderlayout.east); South.Add (Bawah, Borderlayout.South); btnupload.addactionListener (ActionListener baru () {public void actionPerformed (final actionEvent e) {if (! constantValue.to_all.equals (chattabbed.gettitleat (chattabbed.getSelectedIndex ()))) {int returnval = filechooser. Jfilechooser.approve_option) {file file = filechooser.getSelectedFile (); I18n.info_file_to_all_error);}}}); btnsend.addactionListener (ActionListener baru () {public void actionPerformed (final actionEvent e) {if (stringutil.isnotempty (tasend.gettext ())) {action.send (chattabbed.gettitleat (chattabbed.getSelecectedindex (), null); hingga () (tasend.) (TasEptExt.) (TasEptExt.); }}); Panel.Add (selatan, Borderlayout.south); Panel Kembali; } private jComponent createUsrlistView () {listUsrlist = new jList <string> (); ListUsRlist.SetBorder (Lineborder baru (Color.Blue)); listUsrlist.setListData (string baru [] {constantValue.to_all}); listUsrlist.setFixedCellWidth (200); listUsrlist.setFixedCellHeight (30); listUsrlist.addlistSelectionListener (new ListSelectionListener () {@Override public void valueChanged (listSelectionEvent e) {// obrolan ke if (chattabbed.indExoftab (listusrlist.getSelectedValue ()) == -1 && listusRlist.getList.getSelected ()) == -1 && listUsRlist.getelSelSelSelEcted () == & n -1 && listusrlist.getelSelSelSelSelEctED () == - ! ListUsrlist.getSelectedValue (). Equals (clientHolder.getClient (). getFrom ())) {chattabbed.addtab (listUsrlist.getSelectedValue (), getscroltholder () ();); chattabbed.setselectedIndex (chattabbed.indexoftab (listusrlist.getSelectedValue ())); return listUsrlist; }}[Registerandloginaction.java, chatroomaction.java]
Di sini, pemrosesan acara UI ditangani oleh tindakan, yang hanya memisahkan desain UI dan respons acara. Acara RegisterAndLoginView ditangani oleh RegisterAndLoginaction, dan acara -acara ChatroomView ditangani oleh Chatroomaction. Implementasi spesifik adalah sebagai berikut:
Public Class RegisterAndLoginAction {public void handleReGister (string username, string password) {if (stringutil.isempty (username) || stringutil.isempty (kata sandi)) {return; } RegisterMessage message = new registerMessage () .setUserName (nama pengguna) .setPassword (kata sandi); message.setFrom (nama pengguna); Sendhelper.send (clientholder.getClient (). GetSocket (), pesan); } public void handlelogin (string username, string password) {if (stringutil.isempty (username) || stringutil.isempty (kata sandi)) {return; } Pesan loginMessage = LoginMessage baru () .setUserName (nama pengguna) .setPassword (kata sandi); message.setFrom (nama pengguna); Sendhelper.send (clientholder.getClient (). GetSocket (), pesan); }} Ada dua kelas lain untuk desain UI, yaitu resultholder dan resultWrapper. Hasil dan resultholder hanya untuk membuat dan menyimpan tab obrolan. Implementasi spesifik adalah sebagai berikut:
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 (tombol string, wrapper resultWrapper) {listresultwrapper.put (tombol, wrapper); } public static resultWrapper get (tombol string) {wrapper wrapper = listresultwrapper.get (key); if (wrapper == null) {wrapper = create (); put (kunci, pembungkus); } return wrapper; } private static resultWrapper create () {jTextArea resultTextArea = new jTextArea (); resulttextArea.seteditable (false); resulttextArea.setBorder (Lineborder baru (Color.Blue)); Jscrollpane scrollpane = jscrollpane baru (resultTextArea); scrollpane.sethorizontalScrollBarpolicy (scrollpaneconstants.horizontal_scrollbar_never); scrollpane.setVerticalScrollBarpolicy (scrollpaneconstants.vertical_scrollbar_as_needed); Wrapperswrapper wrapper = new resultWrapper (scrollpane, resultTextArea); return wrapper; }} Yang terakhir diberikan, entri yang dijalankan oleh klien:
/** * * @author yaolin * */kelas publik niloaychat {public static void main (string [] args) {view v = router.getView (registerAndLogInview.class) .create (); coba {v.display (); Klien klien = klien baru (DefaultCallback baru ()); client.start (); Clientholder.setClient (klien); } catch (ioException e) {joptionpane.showmessagealog (v.container (), e.getMessage ()); }}} Demo Download Address: Demo
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.