java - JTextField input fails to update output in TextView in MVC -


i'm studying advanced java , trying write program utilizes mvc design pattern. program needs draw string can modified user's input in jtextfield. user can adjust color , font size of text through jcombobox , jspinner respectively.

here have far:

public class mvcdemo extends japplet {     private jbutton jbtncontroller = new jbutton("show controller");     private jbutton jbtnview = new jbutton("show view");     private textmodel model = new textmodel();  //constructor public mvcdemo(){      //set layout , add buttons     setlayout(new flowlayout());     add(jbtncontroller);     add(jbtnview);      jbtncontroller.addactionlistener(new actionlistener(){         @override         public void actionperformed(actionevent e){             jframe frame = new jframe("controllor");             textcontroller controller = new textcontroller();             controller.setmodel(model);             frame.add(controller);             frame.setsize(200, 100);             frame.setlocationrelativeto(null);             frame.setvisible(true);         }     });      jbtnview.addactionlistener(new actionlistener(){         @override         public void actionperformed(actionevent e){             jframe frame = new jframe("view");             textview view = new textview();             view.setmodel(model);             frame.add(view);             frame.setsize(500, 200);             frame.setlocation(200, 200);             frame.setvisible(true);         }     }); }      public static void main(string[] args){         mvcdemo applet = new mvcdemo();         jframe frame = new jframe();         frame.setdefaultcloseoperation(jframe.exit_on_close);         frame.settitle("mvcdemo");         frame.getcontentpane().add(applet, borderlayout.center);         frame.setsize(400, 90);         frame.setlocationrelativeto(null);         frame.setvisible(true);     } }  public class textmodel {      private string text = "your student id #";      //utility field used event firing mechanism     private arraylist<actionlistener> actionlistenerlist;      public void settext(string text){         this.text = text;          //notify listener change on text         processevent(new actionevent(this, actionevent.action_performed, "text"));     }      public string gettext(){         return text;     }      //register action event listener     public synchronized void addactionlistener(actionlistener l){         if (actionlistenerlist == null)             actionlistenerlist = new arraylist<actionlistener>();     }      //remove action event listener     public synchronized void removeactionlistener(actionlistener l){         if (actionlistenerlist != null && actionlistenerlist.contains(l))             actionlistenerlist.remove(l);     }      //fire tickevent     private void processevent(actionevent e){         arraylist<actionlistener> list;          synchronized (this){             if (actionlistenerlist == null)                 return;             list = (arraylist<actionlistener>)(actionlistenerlist.clone());         }     } }  public class textview extends jpanel{     private textmodel model;      //set model     public void setmodel(textmodel model){         this.model = model;          if (model != null)             //register view listener model             model.addactionlistener(new actionlistener(){                 @override                 public void actionperformed(actionevent e){                     repaint();                 }             });     }      public textmodel getmodel(){         return model;     }      @override     public void paintcomponent(graphics g){         if (model != null){             super.paintcomponent(g);             //g.setcolor(model.getcolor());              g.drawstring(model.gettext(), 190, 90);         }     } }  public class textcontroller extends jpanel {     string[] colorstrings = { "black", "blue", "red" };     private textmodel model;     private jtextfield jtftext = new jtextfield();     private jcombobox jcbocolorlist = new jcombobox(colorstrings);      //constructor     public textcontroller(){         //panel group labels         jpanel panel1 = new jpanel();         panel1.setlayout(new gridlayout(3, 1));         panel1.add(new jlabel("text"));         panel1.add(new jlabel("color"));         panel1.add(new jlabel("size"));          //panel group text field, combo box , spinner         jpanel panel2 = new jpanel();         panel2.setlayout(new gridlayout(3, 1));         panel2.add(jtftext);         panel2.add(jcbocolorlist);          setlayout(new borderlayout());         add(panel1, borderlayout.west);         add(panel2, borderlayout.center);          //register listeners         jtftext.addactionlistener(new actionlistener(){             @override             public void actionperformed(actionevent e){                 if (model != null)                     model.settext(jtftext.gettext());             }         });          /*jcbocolorlist.addactionlistener(new actionlistener(){             @override             public void actionperformed(actionevent e){                 if (model != null)                     model.set             }         });*/     }      public void setmodel(textmodel model){         this.model = model;     }      public textmodel getmodel(){         return model;     } } 

at moment i've implemented jtextfield component (yet figure out how jcombobox , jspinner properly), , hardly perfect.

when first launch program , turn on both view , controller panels, default string of "your student id #" shown correctly in view. when type other string jtextfield , hit enter, output string in textview not update unless close view panel , reopen it. point out causing behavior?

i suspect has event handling part of program. i'm still quite new gui programming , have basic understanding of how events triggered , handled. grateful if explain underlying cause of problem in beginner-friendly fashion.

your mixing layers, both model , controller non visual entities. i'm in phone i've not inspected code in depth, but, view should notify controller (directly or in directly) when values change, controller update model accordingly notify controller further notify view

in formal mvc, model , view should never know each other, controller used bridge them together. swing doesn't follow strict mvc (it's more of mv-c) , trying wrap strict mvc around can cause no end of headaches.

instead, do, wrap mvc around swing, means view doesn't need expose ui elements, instead, relies on contract between controller , view determine each party can do

let's start example.

start defining contracts. these should interfaces, allows decouple code in way allows physical implementation change without affecting other parts of api, maybe like...

public interface textmodel {     public void settext(string text);     public string gettext();     public void addchangelistener(changelistener listener);     public void removechangelistener(changelistener listener); }  public interface textcontroller {     public string gettext();     public void settext(string text); }  public interface textview {     public textcontroller getcontroller();     public void setcontroller(textcontroller controller);     public void settext(string text); } 

now, normally, i'd consider making abstract versions, wrap common functionality, sake of example, i've jumped straight default implementations...

public class defaulttextmodel implements textmodel {      private string text;     private set<changelistener> listeners;      public defaulttextmodel() {         listeners = new hashset<>(25);     }      @override     public string gettext() {         return text;     }      @override     public void settext(string value) {         if (text == null ? value != null : !text.equals(value)) {             this.text = value;             firestatechanged();         }     }      @override     public void addchangelistener(changelistener listener) {         listeners.add(listener);     }      @override     public void removechangelistener(changelistener listener) {         listeners.remove(listener);     }      protected void firestatechanged() {         changelistener[] changelisteners = listeners.toarray(new changelistener[0]);         if (changelisteners != null && changelisteners.length > 0) {             changeevent evt = new changeevent(this);             (changelistener listener : changelisteners) {                 listener.statechanged(evt);             }         }     }  }  public class defaulttextcontroller implements textcontroller {      private textmodel model;     private textview view;      public defaulttextcontroller(textmodel model, textview view) {         this.model = model;         this.view = view;          this.view.setcontroller(this);         this.model.addchangelistener(new changelistener() {             @override             public void statechanged(changeevent e) {                 // make "textwaschanged" method on view                 // , make view ask controller value, where's                 // fun in :p                 getview().settext(gettext());             }         });     }      public textmodel getmodel() {         return model;     }      public textview getview() {         return view;     }      @override     public string gettext() {         return getmodel().gettext();     }      @override     public void settext(string text) {         getmodel().settext(text);     }  } 

now, should asking yourself, how going work, have input , output view. reality is, work really, well, first, need 2 different views...

public class inputtextview extends jpanel implements textview {      private textcontroller controller;      public inputtextview() {         setlayout(new gridbaglayout());         jtextfield field = new jtextfield(10);         add(field);         field.addactionlistener(new actionlistener() {             @override             public void actionperformed(actionevent e) {                 getcontroller().settext(field.gettext());             }         });     }      @override     public textcontroller getcontroller() {         return controller;     }      @override     public void setcontroller(textcontroller controller) {         this.controller = controller;     }      @override     public void settext(string text) {         // kind of don't care, because we're responsible changing         // text anyway :p     }  }  public class outputtextview extends jpanel implements textview {      private textcontroller controller;      public outputtextview() {     }      @override     public textcontroller getcontroller() {         return controller;     }      @override     public void setcontroller(textcontroller controller) {         this.controller = controller;     }      @override     public void settext(string text) {         revalidate();         repaint();     }      @override     public dimension getpreferredsize() {         dimension size = new dimension(200, 40);         textcontroller controller = getcontroller();         if (controller != null) {             string text = controller.gettext();             fontmetrics fm = getfontmetrics(getfont());             if (text == null || text.trim().isempty()) {                 size.width = fm.stringwidth("m") * 10;             } else {                 size.width = fm.stringwidth(text);             }             size.height = fm.getheight();         }         return size;     }      @override     protected void paintcomponent(graphics g) {         super.paintcomponent(g);         textcontroller controller = getcontroller();         string text = "";         if (controller != null) {             text = controller.gettext();         }         if (text == null) {             text = "";         }         fontmetrics fm = g.getfontmetrics();         int x = (getwidth() - fm.stringwidth(text)) / 2;         int y = ((getheight() - fm.getheight()) / 2) + fm.getascent();         g.drawstring(text, x, y);     }  } 

these both implementations of textview, difference is, 1 view (the input) sets text , ignores changes text , 1 responds changes in text , never sets it...

brain still not coping? let me demonstrate....

inputtextview inputview = new inputtextview(); outputtextview outputview = new outputtextview();  textmodel model = new defaulttextmodel(); // shared model!! textcontroller inputcontroller = new defaulttextcontroller(model, inputview); textcontroller outputcontroller = new defaulttextcontroller(model, outputview); 

basically, here, 1 2 views, 2 controllers , one, shared, model. when input side of things changes text, model notifies output side of things , updated

and because know fun copy separate pieces of code , stich them together...

text

import java.awt.dimension; import java.awt.eventqueue; import java.awt.fontmetrics; import java.awt.graphics; import java.awt.gridbaglayout; import java.awt.gridlayout; import java.awt.event.actionevent; import java.awt.event.actionlistener; import java.util.hashset; import java.util.set; import javax.swing.jframe; import javax.swing.jpanel; import javax.swing.jtextfield; import javax.swing.uimanager; import javax.swing.unsupportedlookandfeelexception; import javax.swing.event.changeevent; import javax.swing.event.changelistener;  public class test {      public static void main(string[] args) {         new test();     }      public test() {         eventqueue.invokelater(new runnable() {             @override             public void run() {                 try {                     uimanager.setlookandfeel(uimanager.getsystemlookandfeelclassname());                 } catch (classnotfoundexception | instantiationexception | illegalaccessexception | unsupportedlookandfeelexception ex) {                     ex.printstacktrace();                 }                  inputtextview inputview = new inputtextview();                 outputtextview outputview = new outputtextview();                  textmodel model = new defaulttextmodel();                 // shared model!!                 textcontroller inputcontroller = new defaulttextcontroller(model, inputview);                 textcontroller outputcontroller = new defaulttextcontroller(model, outputview);                  jframe frame = new jframe("testing");                 frame.setdefaultcloseoperation(jframe.exit_on_close);                 frame.setlayout(new gridlayout(2, 0));                 frame.add(inputview);                 frame.add(outputview);                 frame.pack();                 frame.setlocationrelativeto(null);                 frame.setvisible(true);             }         });     }      public interface textmodel {         public void settext(string text);         public string gettext();         public void addchangelistener(changelistener listener);         public void removechangelistener(changelistener listener);     }      public interface textcontroller {         public string gettext();         public void settext(string text);     }      public interface textview {         public textcontroller getcontroller();         public void setcontroller(textcontroller controller);         public void settext(string text);     }      public class defaulttextmodel implements textmodel {          private string text;         private set<changelistener> listeners;          public defaulttextmodel() {             listeners = new hashset<>(25);         }          @override         public string gettext() {             return text;         }          @override         public void settext(string value) {             if (text == null ? value != null : !text.equals(value)) {                 this.text = value;                 firestatechanged();             }         }          @override         public void addchangelistener(changelistener listener) {             listeners.add(listener);         }          @override         public void removechangelistener(changelistener listener) {             listeners.remove(listener);         }          protected void firestatechanged() {             changelistener[] changelisteners = listeners.toarray(new changelistener[0]);             if (changelisteners != null && changelisteners.length > 0) {                 changeevent evt = new changeevent(this);                 (changelistener listener : changelisteners) {                     listener.statechanged(evt);                 }             }         }      }      public class defaulttextcontroller implements textcontroller {          private textmodel model;         private textview view;          public defaulttextcontroller(textmodel model, textview view) {             this.model = model;             this.view = view;              this.view.setcontroller(this);             this.model.addchangelistener(new changelistener() {                 @override                 public void statechanged(changeevent e) {                     // make "textwaschanged" method on view                     // , make view ask controller value, where's                     // fun in :p                     getview().settext(gettext());                 }             });         }          public textmodel getmodel() {             return model;         }          public textview getview() {             return view;         }          @override         public string gettext() {             return getmodel().gettext();         }          @override         public void settext(string text) {             getmodel().settext(text);         }      }      public class inputtextview extends jpanel implements textview {          private textcontroller controller;          public inputtextview() {             setlayout(new gridbaglayout());             jtextfield field = new jtextfield(10);             add(field);             field.addactionlistener(new actionlistener() {                 @override                 public void actionperformed(actionevent e) {                     getcontroller().settext(field.gettext());                 }             });         }          @override         public textcontroller getcontroller() {             return controller;         }          @override         public void setcontroller(textcontroller controller) {             this.controller = controller;         }          @override         public void settext(string text) {             // kind of don't care, because we're responsible changing             // text anyway :p         }      }      public class outputtextview extends jpanel implements textview {          private textcontroller controller;          public outputtextview() {         }          @override         public textcontroller getcontroller() {             return controller;         }          @override         public void setcontroller(textcontroller controller) {             this.controller = controller;         }          @override         public void settext(string text) {             revalidate();             repaint();         }          @override         public dimension getpreferredsize() {             dimension size = new dimension(200, 40);             textcontroller controller = getcontroller();             if (controller != null) {                 string text = controller.gettext();                 fontmetrics fm = getfontmetrics(getfont());                 if (text == null || text.trim().isempty()) {                     size.width = fm.stringwidth("m") * 10;                 } else {                     size.width = fm.stringwidth(text);                 }                 size.height = fm.getheight();             }             return size;         }          @override         protected void paintcomponent(graphics g) {             super.paintcomponent(g);             textcontroller controller = getcontroller();             string text = "";             if (controller != null) {                 text = controller.gettext();             }             if (text == null) {                 text = "";             }             fontmetrics fm = g.getfontmetrics();             int x = (getwidth() - fm.stringwidth(text)) / 2;             int y = ((getheight() - fm.getheight()) / 2) + fm.getascent();             g.drawstring(text, x, y);         }      } } 

Comments

Popular posts from this blog

php - Wordpress website dashboard page or post editor content is not showing but front end data is showing properly -

How to get the ip address of VM and use it to configure SSH connection dynamically in Ansible -

javascript - Get parameter of GET request -