Шаблон 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 - то вы можете посмотреть и другие мои заметки, которые собрались у меня на сайте в прошлом году:
- 2012 год: Векторное рисование в полярных координатах и арктангенс.
- 2011 год: Simple Freeware OpenSource Flex Player for Web (one sound).
- 2011 год: Парсинг AJAX-сайтов в среде AIR (путем выполнения jQuery-запросов из ActionScript).
- 2011 год: Флекс диаграммы.
- 2011 год: Видео-камеры, видео-чаты и Flash-медиасервера (работающие по RTMP и самописным протоколам).
- 2011 год: Сокеты во Flash.
- 2011 год: Freeware OpenSource панорамный фотослайдер.
- 2011 год: AIR приложения для платформ Android, Macintosh и Linux.
- 2011 год: Реклама в видеоплеере (возможности объектного программирования ActionScript).
- 2011 год: Трехмерное вдохновение PaperVision3D.
- 2011 год: TextBannerRotator - простой ротатор текстовых баннеров с эффектом BLUR.
- 2011 год: OpenSource Freeware FotoSlider on Flex 3 and jQuery.
- 2011 год: Как сделать SOAP/WSDL-вебсервис на ASP.NET/MONO для вызова его из FLEX.
- 2011 год: Как с помощью jQuery сделать флеш-ролик резиновым.
- 2011 год: Мой первый фото-слайдер на Flex 4.
<SITEMAP> <MVC> <ASP> <NET> <DATA> <KIOSK> <FLEX> <SQL> <NOTES> <LINUX> <MONO> <FREEWARE> <DOCS> <ENG> <CHAT ME> <ABOUT ME> < THANKS ME> |