(Flex) Flex (2012 год)

Шаблон Flex/Air приложений со вкладками - ViewStack.

To view this page ensure that Adobe Flash Player version 10.0.0 or greater is installed.

На этой страничке я покажу шаблон Flex-Air приложений достаточно общего вида - со вкладками, ПрогрессБаром, с таблицами, с Canvas для графики, с фильтром c привязкой и с тестовыми данными. Во многих-многих моих приложениях этот шаблон повторяется и поэтому я хочу помочь начинающим программистам и опубликовать его.


Итак, FLEX-проект состоит из нескольких компонентов, главного MXML-файла и отдельных фрагментов кода (с классами, пакетами или просто модулями). Главный файл FLEX-проекта - это собственно MXML-файл с XML-тегами, содержащими разметку формы и в котором перечислены файлы с кодом проекта (в данном случае в строках 19-22). Компоненты (контролы в терммнах MS) перечислять не надо - они линкуются в процессе компиляции самой средой. Еще в этом главном файле перечисляются включенные в SWF отдельные файлы, как ресурсы - вы это можете увидеть в строке 9,10. Я включил сюда тестовые данные, которые читает парсер и заполняет этими данными табличку. Часто в качестве ресурсов включаются шрифты и рисунки.

Теперь рассмотрим подробнее главный файл проекта - MXML. В данном случае два верхних элемента формы - это TabBar с кнопками и ViewStack (набор панелей). Соответственно тыканиями в кнопки таббара панели переключаются. Логика переключения не совсем простая, вынесена в отдельный файл PanelLogic.as - мы ее рассмотрим позже.

Как вы видите, тут свободно можно перемешивать как строки разметки формы, так и строки кода. Например, чтобы не загромождать основной код, я включил вызов прогресс-бара непосредственно в разметку в строке 111. Хотя в данному случае это можно было сделать и проще - прямо указав реакцию на событие click в XML-декларации компонента. Но большие фрагменты кода, конечно, так включить нельзя. В коде, расположенном в теле MXML есть пару ньюансов - строки 50-60 закоментированы. Почему? Потому что это функционал доступа к локальному диску - из FLEX этого сделать конечно нельзя, это можно делать только из AIR - именно поэтому в варианте для FLEX эти строки закомментированы и данные считываются из ресурсов.

Здесь есть блок глобальных деклараций - эти обьекты видны в контролах и могут быть использованы для привязки. Пример привязки вы видите в строках 100-106, а инициализацию объекта привязки в строке 12. В строках 31,32 вы видите эффект вспышки, в строках 25-29 - объявления переменных, нужные мне для импортера данных. В строках 75-79 данные из структур, заполненных импортером с помощью привязки попадают на отображение в табличку на первой вкладке.

Итак, на первой вкладке вызывается импортер, которые читает файл ресурсов и с помощью привязки данные попадают в табличку, на второй вкладке расположена форма, на которой можно задать некие фильтры к данным, а третья вкладка особенная - у нее есть событие EnterFrame - она предназначена для отрисовки графики.


   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
   3:                 xmlns:s="library://ns.adobe.com/flex/spark" 
   4:                 xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"   backgroundColor="#FDF5E6">
   5:      
   6:      
   7:      <fx:Script>
   8:          <![CDATA [
   9:              [Embed(source="NfaPlus.dump",mimeType="application/octet-stream")]
  10:              private var NfaPlus_dump : Class;    
  11:              
  12:              private var Filter:Object = {From:0,To:0,Symbol:""};
  13:              
  14:              
  15:   
  16:          ]]>
  17:      </fx:Script>
  18:      
  19:      <fx:Script source="graphics.as" />
  20:      <fx:Script source="loadinput.as" />
  21:      <fx:Script source="logic.as" />
  22:      <fx:Script source="PanelLogic.as" />
  23:      
  24:      <fx:Declarations>
  25:          <fx:String id="InputString" />
  26:          <s:ArrayCollection id="JUMP"/>
  27:          <s:ArrayCollection id="STATE"/>
  28:          <s:ArrayCollection id="PLACE"/>
  29:          <s:ArrayCollection id="LINK"/>
  30:          <mx:UIComponent id="GRAF" />
  31:          <mx:Glow id="glow" duration="1000" alphaFrom="1.0" alphaTo="0.3" blurXFrom="0.0" blurXTo="10.0" blurYFrom="0.0" blurYTo="510.0" color="0x22A050"/>
  32:          <mx:Glow id="unglow" duration="1000" alphaFrom="0.3" alphaTo="1.0" blurXFrom="10.0" blurXTo="0.0" blurYFrom="10.0" blurYTo="0.0" color="0x3380DD"/>
  33:      </fx:Declarations>
  34:      
  35:      
  36:      <s:TabBar id="tabs" left="8" y="2" dataProvider="{ViewStack1}" changing="tabs_changingHandler(event)"  />
  37:      <mx:ViewStack id="ViewStack1" width="98%" height="92%" left="8" y="23" change="ViewStack1_changeHandler(event)">
  38:          <s:NavigatorContent label="Данные"  width="100%" height="100%">
  39:              <s:BorderContainer width="100%" height="100%" borderWeight="2" cornerRadius="3" dropShadowVisible="true">
  40:                  <s:backgroundFill>
  41:                      <s:LinearGradient rotation="90">
  42:                          <s:GradientEntry color="0xF4F4F4" />
  43:                          <s:GradientEntry color="0xB9B9B9" />
  44:                      </s:LinearGradient>
  45:                  </s:backgroundFill>
  46:                  <s:VGroup x="0" y="0" width="100%" height="100%">
  47:                      <mx:VRule height="5"/>
  48:                      <s:Button id="OpenFile" label="Load data" left="10"  >
  49:                          <s:click>
  50:                          /*     var f:File = File.desktopDirectory;
  51:                              f.browseForOpen (" Select dump file", [textFilter]);
  52:                              f.addEventListener ( Event.SELECT, function (event:Event):void {
  53:                              var fs:FileStream = new FileStream();
  54:                              fs.open(event.target as File, FileMode.READ);
  55:                              InputString = fs.readUTFBytes (fs.bytesAvailable);
  56:                              fs.close();
  57:                              if (ParseInput()){
  58:                              ViewStack1.selectedIndex = 1;
  59:                              }
  60:                              }); */
  61:   
  62:                              import mx.core.ByteArrayAsset;
  63:                              
  64:                              var TEST_DATA_ByteArray:ByteArrayAsset = ByteArrayAsset(new NfaPlus_dump());
  65:                              InputString = TEST_DATA_ByteArray.readUTFBytes(TEST_DATA_ByteArray.length);
  66:                              if (ParseInput()){
  67:                                  ViewStack1.selectedIndex = 0;
  68:                              }
  69:                              
  70:                              ShowTimeProgress();
  71:                              
  72:                          </s:click>
  73:                      </s:Button>
  74:                      <s:Label x="15" id="Err1" color="#FF0000"/>
  75:                      <mx:DataGrid id="rawtab1" dataProvider="{JUMP}"  width="100%"  x="0"  height="100%">
  76:                          <mx:columns>
  77:                              <mx:DataGridColumn headerText="From" dataField="From"/>
  78:                              <mx:DataGridColumn headerText="Symbol" dataField="Symbol"/>
  79:                              <mx:DataGridColumn headerText="To" dataField="To"/>
  80:                          </mx:columns>
  81:                      </mx:DataGrid>
  82:                  </s:VGroup>
  83:                  
  84:                  
  85:              </s:BorderContainer>
  86:          </s:NavigatorContent>
  87:          
  88:          
  89:          <s:NavigatorContent label="Фильтр" width="100%" height="100%" >
  90:              <s:BorderContainer width="100%" height="100%" borderWeight="2" 
  91:                                 cornerRadius="3" dropShadowVisible="true">
  92:                  <s:backgroundFill>
  93:                      <s:LinearGradient rotation="90">
  94:                          <s:GradientEntry color="0xF4F4F4" />
  95:                          <s:GradientEntry color="0xB9B9B9" />
  96:                      </s:LinearGradient>
  97:                  </s:backgroundFill>
  98:                  <mx:Form id="contactForm" defaultButton="{SetFilter}" width="100%" height="100%">
  99:                      <mx:FormItem label="От номера :">
 100:                          <s:TextInput text="{Filter.From}"/>
 101:                      </mx:FormItem>
 102:                      <mx:FormItem label="До номера:">
 103:                          <s:TextInput  text="{Filter.To}"/>
 104:                      </mx:FormItem>
 105:                      <mx:FormItem label="Symbol:">
 106:                          <s:TextInput  text="{Filter.Symbol}"/>
 107:                      </mx:FormItem>
 108:                      <mx:FormItem>
 109:                          <s:Button id="SetFilter" label="Set" >
 110:                              <s:click><![CDATA[
 111:                                  ShowTimeProgress();
 112:                              ]]></s:click>
 113:                          </s:Button>
 114:                          
 115:                      </mx:FormItem>
 116:                  </mx:Form>
 117:              </s:BorderContainer>
 118:          </s:NavigatorContent>
 119:          
 120:          
 121:          <s:NavigatorContent label="Рисунок" width="100%" height="100%"  enterFrame="navigatorcontent1_enterFrameHandler(event)">
 122:              <s:BorderContainer  width="100%" height="100%" borderWeight="2" 
 123:                                 cornerRadius="3" dropShadowVisible="true">
 124:                  <s:backgroundFill>
 125:                      <s:LinearGradient rotation="90">
 126:                          <s:GradientEntry color="0xF4F4F4" />
 127:                          <s:GradientEntry color="0xB9B9B9" />
 128:                      </s:LinearGradient>
 129:                  </s:backgroundFill>
 130:   
 131:                  <s:VSlider id="num1" x="10" y="80" height="320" minimum="7" maximum="100" value="100" stepSize="1"  mouseDownEffect="{glow}" mouseUpEffect="{unglow}"  click="num1_clickHandler(event)"/>
 132:                  <mx:Canvas id="Stage1" left="30" top="10" bottom="10" right="10"   contentBackgroundColor="#F99999"   />
 133:                  
 134:              </s:BorderContainer>
 135:          </s:NavigatorContent>
 136:   
 137:      </mx:ViewStack>
 138:      
 139:  </s:Application>

Теперь рассмотрим второй компонент проекта - контрол прогресс бара. Это достаточно повторяемый у меня из проекта в проект компонент - причем он умеет рабоать и сам (по таймеру), и его моэно вызывать вручную в EnterFrame - когда что-то рисуется долго-долго.


   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" 
   3:           xmlns:s="library://ns.adobe.com/flex/spark" 
   4:           xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="20">
   5:      
   6:      <fx:Script>
   7:      <![CDATA[
   8:          
   9:          //этот прогресс-бар может вызыватся из FLEX и AIR
  10:          //в режиме по таймеру
  11:          //
  12:          //        protected function ShowTimeProgress(){
  13:          //            var ProgressWindow:Progress = new Progress();
  14:          //            ProgressWindow.ProgressOnTimer=true;
  15:          //            ProgressWindow.ProgressBarStep=5;
  16:          //            ProgressWindow.ProgressTimerMillesecondDelay=10;
  17:          //            PopUpManager.addPopUp(ProgressWindow,this,false);
  18:          //            PopUpManager.centerPopUp(ProgressWindow);
  19:          //        }
  20:          //
  21:          //или вызываться по шагам в коде
  22:          //
  23:          //        private var ProgressWindow:Progress;
  24:          //        protected function StartProgress(){
  25:          //            ProgressWindow = new Progress();
  26:          //            ProgressWindow.ProgressOnTimer=false;
  27:          //            ProgressWindow.resetProgressBar();
  28:          //            PopUpManager.addPopUp(ProgressWindow,this,false);
  29:          //            PopUpManager.centerPopUp(ProgressWindow);
  30:          //        }    
  31:          //        ProgressWindow.timer_progress(null);
  32:          //
  33:          //        ProgressWindow.timer_progress(null);
  34:          
  35:          import  mx.events.FlexEvent;
  36:          import  mx.managers.PopUpManager;
  37:          private var timer:Timer;
  38:          
  39:          private var _progressOnTimer:Boolean = true;
  40:          
  41:          public function get ProgressOnTimer():Boolean{
  42:              return _progressOnTimer;
  43:          }
  44:          
  45:          public function set ProgressOnTimer(value:Boolean):void{
  46:              _progressOnTimer = value;
  47:          }
  48:          
  49:          private var _progressTimerMillesecondDelay:int = 10;
  50:          
  51:          public function get ProgressTimerMillesecondDelay():int{
  52:              return  _progressTimerMillesecondDelay;
  53:          }
  54:          
  55:          public function set ProgressTimerMillesecondDelay(value:int):void{
  56:              _progressTimerMillesecondDelay = value;
  57:          }
  58:          
  59:          private var _progressBarStep:int = 1;
  60:          
  61:          public function get ProgressBarStep():int{
  62:              return  _progressBarStep;
  63:          }
  64:          
  65:          public function set ProgressBarStep(value:int):void{
  66:              _progressBarStep = value;
  67:          }
  68:          
  69:          protected function progressBar_creationCompleteHandler(event:FlexEvent):void
  70:          {
  71:              if (_progressOnTimer){
  72:                  timer = new Timer(_progressTimerMillesecondDelay);
  73:                  timer.addEventListener(TimerEvent.TIMER, timer_progress);
  74:                  playProgressBar();
  75:              }    
  76:              else{
  77:                  resetProgressBar();
  78:              }
  79:          }
  80:   
  81:          public function timer_progress(evt:TimerEvent):void {
  82:              progressBar.setProgress( progressBar.value + _progressBarStep,  100);
  83:          }
  84:          
  85:          private function playProgressBar():void {
  86:              resetProgressBar();
  87:              timer.start();
  88:          }
  89:   
  90:          
  91:          public function resetProgressBar():void {
  92:              progressBar.setProgress(0, 100);
  93:              progressBar.scaleX = 1.0; // 100%
  94:              progressBar.scaleY = 1.0; // 100%
  95:              progressBar.alpha = 1.0;  // 100%
  96:              
  97:          }
  98:          
  99:          public function progressBar_complete(evt:Event):void {
 100:              if (timer!==null){
 101:                  if (timer.running){
 102:                      timer.stop();
 103:                  }
 104:              }
 105:              PopUpManager.removePopUp(this);
 106:          }
 107:          
 108:      ]]>
 109:      </fx:Script>
 110:      
 111:      
 112:      <fx:Declarations>
 113:          <mx:Parallel id="progressBar_completeEffect">
 114:              <mx:Fade alphaTo="0.0" />
 115:              <mx:Zoom zoomHeightTo="0" />
 116:          </mx:Parallel>
 117:      </fx:Declarations>
 118:      
 119:      <mx:ProgressBar id="progressBar"
 120:                      complete="progressBar_complete(event);"
 121:                      completeEffect="{progressBar_completeEffect}"
 122:                      mode="manual"
 123:                      labelPlacement="center"
 124:                      width="100%"
 125:                      height="20"  creationComplete="progressBar_creationCompleteHandler(event)" label=" "/>
 126:  </s:Group>

В файл PanelLogic.as зашита логика переключения между панелями. Основная фишка здесь в том, чтобы выдать Alert о необходимости уничтожения рисунка (который может строится часами). Ну и остановить EnterFrame на панели для графики. Сюда же я включил враппер для вызова прогресс-бара.


   1:  import mx.controls.Alert;
   2:  import mx.core.UIComponent;
   3:  import mx.events.CloseEvent;
   4:  import mx.managers.PopUpManager;
   5:  import spark.events.IndexChangeEvent;
   6:   
   7:  protected function tabs_changingHandler(event:spark.events.IndexChangeEvent):void
   8:  {
   9:   
  10:      if (event.oldIndex==ImagePanel && event.newIndex!==ImagePanel) {
  11:          RotateGRAF=false;
  12:          Alert.show("Уничтожить рисунок?", "Выход", Alert.YES|Alert.NO, this, DestroyClickHandler);
  13:      } 
  14:  }    
  15:   
  16:  //переключилась панель
  17:  protected function ViewStack1_changeHandler(event:mx.events.IndexChangedEvent):void
  18:  {
  19:      if (event.newIndex==ImagePanel){
  20:          Refresh(RefreshNew);
  21:      }
  22:  }
  23:   
  24:  //пришел ответ на Alert
  25:  var RefreshNew:Boolean=false;
  26:  private function DestroyClickHandler(evt:CloseEvent):void {
  27:      if (evt.detail == Alert.YES) {
  28:          RefreshNew=true;
  29:          RotateGRAF=false;
  30:      } else {
  31:          RefreshNew=false;
  32:          RotateGRAF=true;
  33:      }
  34:  }
  35:   
  36:  protected function Refresh(DrawNew:Boolean):void {
  37:      try{            
  38:          if (Stage1.numChildren==0){
  39:              //на сцене пока ничего (canvas is clear)
  40:              GRAF = new UIComponent();
  41:              Stage1.addChild(GRAF);  
  42:              DrawKoleso();
  43:              RotateGRAF=true;
  44:          }
  45:          else if (DrawNew) {
  46:              //нужна новая перерисовка (need redraw) 
  47:              Stage1.removeAllChildren();
  48:              GRAF = new UIComponent();
  49:              Stage1.addChild(GRAF);  
  50:              DrawKoleso();
  51:              RotateGRAF=true;
  52:          }
  53:          else {
  54:              GRAF = Stage1.getChildAt(0) as UIComponent;
  55:              //не нужна перерисовка (no need redraw)
  56:              RotateGRAF=true;
  57:          }
  58:      }                
  59:      catch (e){
  60:          Alert.show(e.toString());
  61:      }
  62:  }
  63:   
  64:   
  65:  protected function ShowTimeProgress(){
  66:      var ProgressWindow:Progress = new Progress();
  67:      ProgressWindow.ProgressOnTimer=true;
  68:      ProgressWindow.ProgressBarStep=5;
  69:      ProgressWindow.ProgressTimerMillesecondDelay=10;
  70:      PopUpManager.addPopUp(ProgressWindow,this,false);
  71:      PopUpManager.centerPopUp(ProgressWindow);
  72:  }

Файл grafics.as - в приниципе для этого примера я взял отрисовку элументарных графических примитивов из топика - Векторное рисование в полярных координатах и арктангенс.

И наконец, последний элемент этого приложения - это собственно векторная графика на выщеуказанных графических примитивах. Все сделано намеренно просто - чтобы было понятно.


   1:  import flash.display.Graphics;
   2:   
   3:  private var Center:Point= new Point(400,400); //стартовая точка
   4:  private var KolesoRadius:Number ;             //радиус колеса
   5:  private var CurveLen:Number ;                 //длина дуги = KolesoRadius*2*Math.PI
   6:  private var KolStep:Number;                   //количество спиц = CurveLen/(2*Circle_Radius)
   7:  private var AngleRadian:Number;               //угол в радианах = 2*Math.PI/KolStep;
   8:   
   9:  private function DrawKoleso():void{
  10:      
  11:      KolStep = num1.value;
  12:      CurveLen = num1.value * (2*Circle_Radius);
  13:      AngleRadian = 2*Math.PI/KolStep;
  14:      KolesoRadius =     CurveLen / (2*Math.PI) ;
  15:   
  16:      DrawGradientCircle(GRAF.graphics,Center.x,Center.y,0,0);
  17:      
  18:      for (var i:Number=0;i<KolStep ;i++){
  19:          var P1:Point = Point.polar(KolesoRadius,i*AngleRadian)
  20:          DrawGradientCircle(GRAF.graphics,P1.x+Center.x,P1.y+Center.y,0.6,i*AngleRadian);
  21:          DrawLine3(i,GRAF.graphics,Center.x,Center.y,P1.x+Center.x,P1.y+Center.y);
  22:      }
  23:  }
  24:   
  25:  private function radiansToDegrees(radians:Number):Number {
  26:      var degrees:Number = radians * (180 / Math.PI);
  27:      return degrees;
  28:  }
  29:   
  30:  private function degreesToRadians(degrees:Number):Number {
  31:      var radians:Number = degrees * (Math.PI / 180);
  32:      return radians;
  33:  }
  34:   
  35:   
  36:  protected function num1_clickHandler(event:MouseEvent):void
  37:  {
  38:      DrawKoleso();
  39:  }
  40:   
  41:  private var RotateGRAF:Boolean=false;
  42:  protected function navigatorcontent1_enterFrameHandler(event:Event):void
  43:  {
  44:      if (RotateGRAF){
  45:          var offsetWidth:Number = 400;
  46:          var offsetHeight:Number = 400;
  47:          var radians:Number = degreesToRadians(0.5);
  48:          var RotateMatrix:Matrix = GRAF.transform.matrix;
  49:          RotateMatrix.translate(-offsetWidth, -offsetHeight);
  50:          RotateMatrix.rotate(radians);
  51:          RotateMatrix.translate(+offsetWidth, +offsetHeight);
  52:          GRAF.transform.matrix = RotateMatrix;
  53:      }
  54:   
  55:  }


Если вас интересует FLASH, AIR и FLEX - то вы можете посмотреть и другие мои заметки, которые собрались у меня на сайте в прошлом году:




Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/Flex_StackViewTemplate/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>