PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/content/programming/as3/as3-madcomponents-tutorial-series-separation-of-concerns.md

https://gitlab.com/monkmartinez/blog
Markdown | 343 lines | 268 code | 75 blank | 0 comment | 0 complexity | 7d8047dc49ceef3f613e6a4b2a86c1fe MD5 | raw file
  1. Title: AS3 MadComponents Tutorial Series - Separation of Concerns
  2. Date: 2011-09-04
  3. Author: Michael
  4. Best practices for programming tell us separation of concerns,
  5. encapsulation and modularity are desirable in most, but not all,
  6. application contexts. A complex app with many views and/or items could
  7. quickly get out of control in a plethora of ways within
  8. the monolithic class context.
  9. The project I'm working on has nearly 50 separate views and consist of
  10. mostly textual data. So my first task was learning the method(s) to
  11. separate class files the [MadComponent][]way. This example will be quite
  12. simple but will illustrate how one would go about using different AS
  13. class files to separate concerns.
  14. <span style="text-decoration: underline;">**User Story:**</span> A list
  15. based component is chosen by the user. This item navigates to a
  16. "details" page or another component with more data.
  17. **Lets build the "landing page" or main application page;**
  18. First the XML layout:
  19. :::xml
  20. public class SeparateClassExample extends Sprite{
  21. protected static const DATA:XML = <data>
  22. <item label="Page 0"/>
  23. <item label="Page 1"/>
  24. </data>;
  25. protected static const LIST:XML =
  26. <list colour="#999999" background="#DADED4">
  27. <search colour="#DADED4" field="label"/>{DATA}</list>;
  28. //Important
  29. protected static const NAVIGATION:XML =
  30. <navigation id="nav" colour="#C100000" title="Home">
  31. {LIST}
  32. {Page0.LAYOUT}
  33. {Page1.LAYOUT}
  34. </navigation>;
  35. protected var _uiNavigation:UINavigation;
  36. The DATA constant simply defines the items that will populate the list.
  37. The LIST constant accepts the DATA constant and applies some basic
  38. formatting, Ie. colour (note the UK spelling). We also implement the
  39. search feature here which will filter the list based on the item's
  40. label. (I have experimented with different search methods, but have been
  41. unsuccessful thus far... we will explore this later).
  42. The NAVIGATION constant is actually the heart and soul of this simple
  43. app. You can think of the NAVIGATION constant as an Array of data that
  44. you define in XML and classes. As my astute readers will probably note
  45. based on a quick glance. We haven't defined Page0, Page1 and you can't
  46. spot LAYOUT anywhere. These are defined separate class files, although
  47. not completely necessary, it helps me keep things organized.
  48. We declare the \_uiNavigation instance or class level variable which
  49. we'll define in subsequent steps.
  50. **Lets move on to the constructor:**
  51. :::as3
  52. public function SeparateClassExample(screen:Sprite = null) {
  53. if (screen){
  54. screen.addChild(this);
  55. }
  56. // support autoOrients
  57. stage.align = StageAlign.TOP_LEFT;
  58. stage.scaleMode = StageScaleMode.NO_SCALE;
  59. // Create the main UI or "Landing Page"
  60. UI.create(this, NAVIGATION);
  61. // Initialize "views"
  62. Page0.initialize();
  63. Page1.initialize();
  64. // Navigation layout and behaviour
  65. _uiNavigation = UINavigation(UI.findViewById("nav"));
  66. _uiNavigation.autoForward = false;
  67. // Must set to false otherwise the app scrolls through all the pages in the "stack"
  68. _uiNavigation.autoBack = false;
  69. // Go to the "page" requested
  70. _uiNavigation.addEventListener(UIList.CLICKED, navigationChange);
  71. // Go Back using the Nav bar back button
  72. _uiNavigation.navigationBar.backButton.addEventListener(MouseEvent.MOUSE_UP, goBack);
  73. // Go back using the hardware back button
  74. _uiNavigation.addEventListener(KeyboardEvent.KEY_UP, goBackButton);
  75. _uiNavigation.navigationBar.backButton.colour = 999999;
  76. }
  77. The UI.create method instantiates the UI or "landing page" and all of
  78. the code relating to the use of MadComponents must be implemented after
  79. this method call. We initialize Page0 with a call to a public method
  80. inside the Page0 class. This method can be named anything you like.
  81. We then instantiate the \_uiNavigation variable to the NAVIGATION
  82. constant defined at the class level through the id we can name
  83. arbitrarily. We can now programmatically add formatting, assign event
  84. listeners and so forth to the \_uiNavigation variable.
  85. The first event listener UI.CLICKED will direct the navigation to the
  86. "page" that is assigned to the index of the NAVIGATION constant. In this
  87. case 0 is the list itself, while 1 and 2 will be Page0 and Page1
  88. respectively.
  89. The second event listener is assigned to the navigationBar.backButton
  90. and is a simple MouseEvent.MOUSE\_UP event. As you will see, this event
  91. will handle the back button that is added to the navigation bar once we
  92. have navigated away from the main list.
  93. The third event listener is added to handle the device back button,
  94. generally this is an Android device. I found through testing that if
  95. autoBack is set to false, you lose back button functionality. In some
  96. cases you may want the autoBack functionality, but its not appropriate
  97. in this context as it will scroll through the stack of items defined in
  98. the NAVIGATION constant. This event listener simply adds the devices
  99. back button into the app.
  100. Daniel is working on documentation as we speak, but a good starting
  101. place to learn about the framework are the PDF(s) included on the
  102. [download page][]. You can see what I've done to customize the
  103. \_uiNavigation variable based on the comments added into the code.
  104. **Now lets handle the events we declared in the constructor:**
  105. :::
  106. public function navigationChange(event:Event):void {
  107. var navIndex:int = _uiNavigation.index;
  108. // Check to see if current page is @ 0 to set correct title
  109. if(_uiNavigation.pages[0]){
  110. _uiNavigation.title = "Home";
  111. }
  112. // navigation Stuff
  113. if(navIndex == 0){
  114. _uiNavigation.goToPage(1, UIPages.SLIDE_LEFT);
  115. _uiNavigation.title = "Page 0";
  116. } else if (navIndex == 1){
  117. _uiNavigation.goToPage(2, UIPages.SLIDE_LEFT);
  118. _uiNavigation.title = "Page 1";
  119. }
  120. }
  121. // For the back button in the Navigation bar
  122. protected function goBack(event:Event):void {
  123. _uiNavigation.goToPage(0, UIPages.SLIDE_RIGHT);
  124. _uiNavigation.title = "Home";
  125. }
  126. // With autoBack set to false, you lose the device back button for some reason
  127. // You can use the Native App library to check for a device back button event
  128. protected function goBackButton(event:KeyboardEvent):void{
  129. if(event.keyCode == Keyboard.BACK){
  130. _uiNavigation.goToPage(0, UIPages.SLIDE_RIGHT);
  131. _uiNavigation.title = "Home";
  132. }
  133. }
  134. }
  135. }
  136. In this example, I want the navigationChange event to take the user to
  137. the page associated with the index they select. We let the gotoPage
  138. method do all the heavy lifting and add a nice visual transition. Beyond
  139. that the back button in the navigation bar and the device back button
  140. (if available) simply takes the user back to the list. If you've
  141. followed any of the tutorials mentioned in the starting out post, you
  142. will not see anything surprising here. The exception is maybe the event
  143. handler for the back button which is similar to the way one handles an
  144. event on the decktop or web based flash application.
  145. A little gotcha I found is when you don't explicitly set the title, the
  146. navigation component will leave the previous title in the navigation bar
  147. despite the change. Checking to make sure its set correctly just adds a
  148. bit of polish.
  149. **Now lets add the separate class "views" or Page0 and Page1:**
  150. :::as3
  151. package
  152. {
  153. import com.danielfreeman.madcomponents.*;
  154. import flash.display.Sprite;
  155. import flash.display.StageAlign;
  156. import flash.display.StageScaleMode;
  157. import flash.events.Event;
  158. import flash.system.Capabilities;
  159. import flash.text.TextField;
  160. public class Page0 extends Sprite
  161. {
  162. public static const LAYOUT:XML = <scrollVertical background="#DADED4">
  163. <label id="message0" alignH="fill"></label>
  164. </scrollVertical>;
  165. protected static var _message:UILabel;
  166. public function Patient()
  167. {
  168. super();
  169. }
  170. public static function initialize():void{
  171. _message = UILabel(UI.findViewById("message0"));
  172. _message.htmlText = "<b>App Considerations:</b><p><li>Is it AWESOME?</li><li>Are you going to BroGram? Sup?</li></p><p><li>More Stuff</li>";
  173. }
  174. }
  175. }
  176. This is a basic page layout using HTML text. LAYOUT is the constant we
  177. call from within the NAVIGATION constant in the Main class as you've
  178. seen. If I may direct your attention to the constructor, notice that
  179. UI.create is not used here. In this case, we "instantiate" the layout
  180. through the public initialize method mentioned earlier. Also note, ID's
  181. for the components for each page need to be different. Ie. Page0 label
  182. has an id of "message0". Page1 will need a label id of "message1" to
  183. work as one would expect. Flash requires string literals to be on the
  184. same line. In my working app this line is almost ridiculously long, no
  185. need to worry as the text will render as you've defined.
  186. This is the "MAIN" class in all its glory.
  187. :::as3
  188. package
  189. {
  190. import com.danielfreeman.madcomponents.*;
  191. import flash.desktop.NativeApplication;
  192. import flash.display.Sprite;
  193. import flash.display.StageAlign;
  194. import flash.display.StageScaleMode;
  195. import flash.events.Event;
  196. import flash.events.KeyboardEvent;
  197. import flash.events.MouseEvent;
  198. import flash.ui.Keyboard;
  199. public class SeparateClassExample extends Sprite{
  200. protected static const DATA:XML = <data>
  201. <item label="Page 0"/>
  202. <item label="Page 1"/>
  203. </data>;
  204. protected static const LIST:XML =
  205. <list colour="#999999" background="#DADED4">
  206. <search colour="#DADED4" field="label"/>{DATA}</list>;
  207. // Each additional "Page" needs to have different id's
  208. //for the components, even when they are separated into separate classes
  209. protected static const NAVIGATION:XML =
  210. <navigation id="nav" colour="#C100000" title="Home">
  211. {LIST}
  212. {Page0.LAYOUT}
  213. {Page1.LAYOUT}
  214. </navigation>;
  215. protected var _uiNavigation:UINavigation;
  216. public function SeparateClassExample(screen:Sprite = null) {
  217. if (screen){
  218. screen.addChild(this);
  219. }
  220. // support autoOrients
  221. stage.align = StageAlign.TOP_LEFT;
  222. stage.scaleMode = StageScaleMode.NO_SCALE;
  223. // Create the main UI or "Landing Page"
  224. UI.create(this, NAVIGATION);
  225. // Initialize "views"
  226. Page0.initialize();
  227. Page1.initialize();
  228. // Navigation layout and behaviour
  229. _uiNavigation = UINavigation(UI.findViewById("nav"));
  230. _uiNavigation.autoForward = false;
  231. // Must set to false otherwise the app scrolls through all the pages in the "stack"
  232. _uiNavigation.autoBack = false;
  233. // Go to the "page" requested
  234. _uiNavigation.addEventListener(UIList.CLICKED, navigationChange);
  235. // Go Back using the Nav bar back button
  236. _uiNavigation.navigationBar.backButton.addEventListener(MouseEvent.MOUSE_UP, goBack);
  237. // Go back using the hardware back button
  238. _uiNavigation.addEventListener(KeyboardEvent.KEY_UP, goBackButton);
  239. _uiNavigation.navigationBar.backButton.colour = 999999;
  240. }
  241. public function navigationChange(event:Event):void {
  242. var navIndex:int = _uiNavigation.index;
  243. // Check to see if current page is @ 0 to set correct title
  244. if(_uiNavigation.pages[0]){
  245. _uiNavigation.title = "Home";
  246. }
  247. // navigation Stuff
  248. if(navIndex == 0){
  249. _uiNavigation.goToPage(1, UIPages.SLIDE_LEFT);
  250. _uiNavigation.title = "Page 0";
  251. } else if (navIndex == 1){
  252. _uiNavigation.goToPage(2, UIPages.SLIDE_LEFT);
  253. _uiNavigation.title = "Page 1";
  254. }
  255. }
  256. // For the back button in the Navigation bar
  257. protected function goBack(event:Event):void {
  258. _uiNavigation.goToPage(0, UIPages.SLIDE_RIGHT);
  259. _uiNavigation.title = "Home";
  260. }
  261. // With autoBack set to false, you lose the device back button for some reason
  262. // You can use the Native App library to check for a device back button event
  263. protected function goBackButton(event:KeyboardEvent):void{
  264. if(event.keyCode == Keyboard.BACK){
  265. _uiNavigation.goToPage(0, UIPages.SLIDE_RIGHT);
  266. _uiNavigation.title = "Home";
  267. [MadComponent]: http://code.google.com/p/mad-components/
  268. [download page]: http://code.google.com/p/mad-components/downloads/list