MVC
|
Modified: |
Downloads
Download then in Eclipse
File | Import | General | Existing Projects into Workspace | Open the download folder | Finish
Calculatorv0 Model-View-Controller command line example.
Calculatorv1 Android Big Blob example.
Calculatorv2 Android Model-View-Controller example.
Overview
MVC is considered by some an architecture and others a programming pattern. Either defines clear responsibilities, communication and loose coupling between the three primary components of most user systems.
The figure above illustrates a communication relationship where the Controller communicates to the View and Model.
The three components of MVC have the following, typical responsibilities:
Controller: receives then sends user input to Model, sends Model state to View.
Model: maintains state of the application.
View: display of Model state seen by user.
Model-View-Controller command line javac
The following example implements a calculator using MVC.
Some advantages of MVC as implemented here:
- Interaction between the MVC components is managed by MyController.
- MyController, MyView and MyModel are completely uncoupled from each other.
- MyView and MyModel do not communicate directly.
- MyController myController = new MyController(myModel, myView); instantiates the Controller with a Model and View.
Other implementations of a Model and View could be substituted that implemented the same interface methods.
|
|
Android Calculator - Big Blob Approach
Trivial problems have trivial solutions.
The Big Blob pattern can often be applied successfully to small problems but fails to scale.
The Calculator is first implemented for Android using the Big Blob approach, all functionality is lumped together and parts tightly coupled.
The Calculator has two parts: the graphical user interface defined in XML and the logic defined in Java.
The user interface is implemented as 3 XML files:
- main.xml is the default project file and serves to include the display and keyboard graphical user interface definition files.
- display.xml defines the display for the calculator results.
- keyboard.xml defines the keyboard for user to click.
XML Key Points
main.xml is the view loaded by: setContentView( R.layout.main );
<include layout="@layout/display" /> includes the contents of file layout/display.xml at that point in the current XML file.
android:id="@+id/editString" defines an id in display.xml for the EditText object.
android:onClick="onClick0" defines an event handler for android:id="@+id/button0" defined in keyboard.xml file.
main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:weightSum="1"> <include layout="@layout/display" /> <include layout="@layout/keyboard" /> </LinearLayout>
display.xml <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editString" android:text="0.0"> <requestFocus/> </EditText> </TableRow> </TableLayout>
keyboard.xml <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"> <TableRow android:gravity="center_horizontal">
<Button android:text="0" android:id="@+id/button0"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick0">
</Button><Button android:text="1" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick1"> </Button> <Button android:text="2" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick2"> </Button> <Button android:text="+" android:id="@+id/buttonPlus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickPlus"> </Button> </TableRow> <TableRow android:gravity="center_horizontal"> <Button android:text="3" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick3"> </Button> <Button android:text="4" android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick4"> </Button> <Button android:text="5" android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick5"> </Button> <Button android:text="-" android:id="@+id/buttonMinus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickMinus"> </Button> </TableRow> <TableRow android:gravity="center_horizontal"> <Button android:text="6" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick6"> </Button> <Button android:text="7" android:id="@+id/button7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick7"> </Button> <Button android:text="8" android:id="@+id/button8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick8"> </Button> <Button android:text="*" android:id="@+id/buttonTimes" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickTimes"> </Button> </TableRow> <TableRow android:gravity="center_horizontal"> <Button android:text="9" android:id="@+id/button9" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick9"> </Button> <Button android:text="C" android:id="@+id/buttonC" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickC"> </Button> <Button android:text="=" android:id="@+id/buttonEqual" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickEqual"> </Button> <Button android:text="/" android:id="@+id/buttonDivide" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickDivide"> </Button> </TableRow> </TableLayout>
Java Key Points
Activity is the application execution, often what the user sees; there is only CalculatorActivity, which is responsible for events and display of visual objects.
Button onClick events, defined in keyboard.xml, are handled by methods of the defined name in:
<Button android:text="0" android:id="@+id/button0"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick0">
</Button>The "0" button click calls the below method which calls the setModel('0') method.
public void onClick0( View v ) {
setModel('0');
display.setText(accumulator+"");
}Assigns the EditText view object defined in display.xml file.
display = (EditText) findViewById(R.id.editString);
CalculatorActivity.java package edu.ius.rwisman.Calculator; import edu.ius.rwisman.Calculator.R; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.EditText; public class CalculatorActivity extends Activity { private double accumulator, opr; private char op; private EditText display; @Override public void onCreate( Bundle savedInstanceState ) { try { super.onCreate(savedInstanceState); setContentView(R.layout.main); display = (EditText) findViewById(R.id.editString); } catch (Exception e) { Log.e("ERROR", "ERROR IN CODE: " + e.toString()); e.printStackTrace(); } } public void setModel(char c) { switch (c) { case 'C': opr = 0.0; accumulator= 0.0; break; case '+':; case '-':; case '*':; case '/' : op = c; opr = accumulator; accumulator = 0.0; break; case '=' : switch (op) { case '+': accumulator = opr + accumulator; break; case '-': accumulator = opr - accumulator; break; case '*': accumulator = opr * accumulator; break; case '/': accumulator = opr / accumulator; break; default: break; } break; default: // Assume '0'..'9' digit accumulator = accumulator * 10.0 + (c-'0'); break; } }
public void onClick0(View v) {
setModel('0');
display.setText(accumulator+"");
}public void onClick1(View v) { setModel('1'); display.setText(accumulator+""); } public void onClick2(View v) { setModel('2'); display.setText(accumulator+""); } public void onClick3(View v) { setModel('3'); display.setText(accumulator+""); } public void onClick4(View v) { setModel('4'); display.setText(accumulator+""); } public void onClick5(View v) { setModel('5'); display.setText(accumulator+""); } public void onClick6(View v) { setModel('6'); display.setText(accumulator+""); } public void onClick7(View v) { setModel('7'); display.setText(accumulator+""); } public void onClick8(View v) { setModel('8'); display.setText(accumulator+""); } public void onClick9(View v) { setModel('9'); display.setText(accumulator+""); } public void onClickPlus(View v) { setModel('+'); display.setText(accumulator+""); } public void onClickMinus(View v) { setModel('-'); display.setText(accumulator+""); } public void onClickTimes(View v) { setModel('*'); display.setText(accumulator+""); } public void onClickDivide(View v) { setModel('/'); display.setText(accumulator+""); } public void onClickC(View v) { setModel('C'); display.setText(accumulator+""); } public void onClickEqual(View v) { setModel('='); display.setText(accumulator+""); } }R.java
/* AUTO-GENERATED FILE. DO NOT MODIFY. * * This class was automatically generated by the * aapt tool from the resource data it found. It * should not be modified by hand. */ package edu.ius.rwisman.Calculator; public final class R { public static final class attr { } public static final class drawable { public static final int icon=0x7f020000; } public static final class id { public static final int button0=0x7f050001; public static final int button1=0x7f050002; public static final int button2=0x7f050003; public static final int button3=0x7f050005; public static final int button4=0x7f050006; public static final int button5=0x7f050007; public static final int button6=0x7f050009; public static final int button7=0x7f05000a; public static final int button8=0x7f05000b; public static final int button9=0x7f05000d; public static final int buttonC=0x7f05000e; public static final int buttonDivide=0x7f050010; public static final int buttonEqual=0x7f05000f; public static final int buttonMinus=0x7f050008; public static final int buttonPlus=0x7f050004; public static final int buttonTimes=0x7f05000c; public static final int editString=0x7f050000; } public static final class layout { public static final int display=0x7f030000; public static final int keyboard=0x7f030001; public static final int main=0x7f030002; } public static final class string { public static final int app_name=0x7f040001; public static final int hello=0x7f040000; } }
Android Calculator - MVC
Android and Eclipse development tools can be used to implement a Model-View-Controller (MVC) reasonably directly.
MVC promotes modularity but loose-coupling of the modules is another goal so three XML files will define the visual elements (keyboard and display), unlike the previous example, these files are not all lumped together by including in one XML file main.xml.
Graphical and functional modules (i.e. in Android, XML and Java) combined make up a complete component.
keyboard.xml and MyController are one module,
display.xml and MyView another.
Ideally, each component is loosely coupled, communicating only through method interfaces. The payoff being able to reuse the component without change in another application or substitute a different component with same functionality.
Key Points in XML
No onClick event handlers are defined in keyboard.xml file.
The events are handled in MyController rather than the Activity class which is expected to handle XML defined onClick events. See the code on how these events are handled.
main.xml provides a base View to which the Views defined in display.xml and keyboard.xml are added programmatically.
Note that in the Big Blob, the files were combined in main.xml by include.
main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:weightSum="1" android:id="@+id/myLayout"> </LinearLayout>
display.xml <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editString" android:text="0.0"> <requestFocus/> </EditText> </TableRow> </TableLayout>
keyboard.xml <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"> <TableRow android:gravity="center_horizontal"> <Button android:text="0" android:id="@+id/button0" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="1" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="2" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="+" android:id="@+id/buttonPlus" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </TableRow> <TableRow android:gravity="center_horizontal"> <Button android:text="3" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="4" android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="5" android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="-" android:id="@+id/buttonMinus" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </TableRow> <TableRow android:gravity="center_horizontal"> <Button android:text="6" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="7" android:id="@+id/button7" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="8" android:id="@+id/button8" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="*" android:id="@+id/buttonTimes" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </TableRow> <TableRow android:gravity="center_horizontal"> <Button android:text="9" android:layout_width="wrap_content" android:id="@+id/button9" android:layout_height="wrap_content"></Button> <Button android:text="C" android:id="@+id/buttonC" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="=" android:id="@+id/buttonEqual" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="/" android:id="@+id/buttonDivide" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </TableRow> </TableLayout>The three components of MVC generally have the following responsibilities:
MyController: receives user input via button onClick events, sends user input to MyModel, sends MyModel state to MyView.
MyModel: maintains state of the calculator.
MyView: display of MyModel state, the state of the calculator.
Activity is the application execution, there is only CalculatorActivity, which is responsible for the GUI, event handling and display of visual objects.
MyModel reused from implementation of our first MVC Calculator.
MyView programmatically creates (inflates) the layout defined in layout/display.xml and assigns the editString to display object.
Note that R.layout.display is the XML and display is a Java object.
LayoutInflater layoutInflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate( R.layout.display, this);
display = (EditText) findViewById( R.id.editString );Finds android:id="@+id/editString" in layout file and assigns the EditText object
MyController programmatically creates (inflates) the layout defined in keyboard.xml and defines the onClick handlers.
final Button button1 = (Button) view.findViewById( R.id.button1 );
button1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
myModel.setModel( '1' );
myView.setView( myModel.getModel() );
}
});
final Button button1 = (Button) view.findViewById( R.id.button1 ) assigns button1 the object defined in keyboard.xml by android:id="@+id/button1".
<Button android:text="1" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> The remainder of the above defines the event handler for clicks.
Setting the model with a '1' which changes the model state.
Getting the model state and setting the view to that state.
CalculatorActivity adds the layouts (subclasses of View) to its View.
ViewGroup container = (ViewGroup) findViewById( R.id.myLayout );
container.addView( myView );
container.addView( myController );android:id="@+id/myLayout" defined in main.xml file.
Using default main.xml file, instantiate a ViewGroup object.
myView and myController are View objects, added to a ViewGroup so are displayed together in parent view.
|
|
||||
|
|