ArcGIS FlexViewer

来源:互联网 发布:软件摊销年限 税法 编辑:程序博客网 时间:2024/06/05 03:45

1、   ArcGIS API for Flex-Widget开发部署流程

开发Widget开发工具:Flex Builder
开发语言:ActionScript & mxml

1)开发环境配置:

a)首先确保FlexBuilder(Flex SDK 4.6)安装成功,并下载Flex viewer(libs目录中包含ArcGIS API for Flexv1.0)。
b)解压下载下来的Flex viewer压缩包。
c)打开Flex Builder,将刚解压出来的源代码导入到flex workspace中。在导入向导中选择源代码的目录,点击finish。
d)编译Flex Viewer。选中当前工程,在Project 菜单下选择Build Project。
2)自定义widget开发
a)新建一个mxml组件(右键单击flex Viewer ,选择New->MXML Component)。选择组件目录,输入组件名称,并确保该组件继承BasWidget类。
b)将创建好的widget添加Flex Builder ProjectModule list。右键单击Flex Viewer 工程,选择属性(properties),打开属性配置框,然后在左边列表择FlexModules。单击Add按钮,选择MyFirstWidget.mxml。这一步的目的是确保MyFirstWidget能够被编译成单独的swf文件。
c)编译widget。重新编译整个Flex Viewer 工程即可。编译完成之后在bin-debug目录下就会有MyFirstWidget.swf。
3)使用WidgetTemplate,打开MyFirstWidget.mxml文件,内置的WidgetTemplate提供了丰富的界面元素。下面介绍如何部署Widget。
a)把Widget加入到config.xml文件中。
打开config.xml文件并在<widgets>标签中添加代码:
b)保存config.xml文件并重新编译Flex Viewer。
c)运行Flex Viewer。
d)从Tool菜单中选择My First Widget。

至此,通过代码就完成了个基本的Widget UI 设计,已经有了最大化,最小化,关闭等功能。下一步,就是实现业务逻辑。

4) 访问地图 (Accessing a Map)
Flex Viewer是一个以地图为中心的应用程序,所有的操作都是围绕地图来进行。FlexViewer初始化之后,Widget就可以访问地图了(来自ArcGISServer 的地图服务)。map是BaseWidget类中定义的一个public 属性,只要是继承BaseWidget的类都可以访问改属性,该属性是com.esri.ags.Map(ArcGIS API for Flex)类型,因此可以使用map对象访问任何在ArcGIS API for Flex中的对象。当widget 被加载之后,widget manager会动态的把当前活动的地图对象关联到widget。

5) 在地图上显示业务数据
a)添加GraphicsLayer
建议为每个Widget都创建一个Graphics Layer。
b)Widget打开/最小化的时候打开/关闭Graphics Layer,WidgetTemplate提供了2个窗口状态的事件:widgetOpened 和 widgetClosed,可以使用这两个事件来同步WidgetTemplate窗口状态的变化和Graphics Layer显示。当widget打开的时候显示Graphics Layer,当widget关闭的时候隐藏Graphics Layer。例子如下:首先侦听widgetOpened 和 widgetClosed事件。在widgetOpened 和 widgetClosed事件的响应函数中控制Graphics Layer的显示。
6)Widget中使用绘制功能
在 Flex Viewer 中通过调用setMapAction方法,可以方便的进行地图上的绘制。setMapAction是在BaseWidget类中定义的public方法,因此只要继承于BaseWidget类的Widget都可以使用该方法。
setMapAction方法的定义如下:
参数:
action: String 类型,定义为当前绘制操作的要素类型。对应ArcGIS Flex API 中绘制工具的绘制要素类型。对应列表如表3-2:
表 3-2:flexViewrer与ArcGIS API for Flex绘制消息对应表

Flex Viewer

ArcGIS API for Flex

extent

Draw.EXTENT

point

Draw.MAPPOINT

line

Draw.LINE

polyline

Draw.POLYLINE

polygon

Draw.POLYGON

multipoint

Draw.MULTIPOINT

freehandpolyline

Draw.FREEHAND_POLYLINE

freehandpolygon

Draw.FREEHAND_POLYGON

status: String 类型,在控制栏上显示的状态文本。
callback: Function类型,绘制结束后调用的回调函数。回调函数的定义可以参考下面代码:

2、   FlexViewer源码包结构

Flex Viewer源代码是Flash Builder中标准的Flex工程,可直接导入到Flash Builder。下面分别介绍一下源代码包结构中的各个部分,包结构如下图所示。

1) src根目录:包含index.mxml、defaults.css和config.xml。index.mxml是系统入口点,也就是创建Flex Application实例的地方;defaults.css是Flex Viewer中用来定义组件样式的文件,所有对组件样式进行定义的css脚本都可以放到这个文件中,defaults.css在index.mxml中被引用;config.xml是缺省配置文件。

2) apps:Flex Viewer的良好设计使其具有很高的可配置性,包括底图、业务图层、各种服务器端资源、甚至是用户体验都是可配置的。apps包中的内容是不同应用系统的配置文件及各种所需资源。Flex Viewer可以在url参数中设置所需加载的配置文件,比如:在Flex Viewer的url后加上配置文件的信息“?config=apps/zh_CN/config.xml”,Flex Viewer就会加载apps/zh_CN下的config.xml文件,根据该配置文件中的信息来配置整个Flex Viewer系统。也就是说,Flex Viewer根据不同的配置文件可以展现完全不同的应用系统。

3) assets.images:Flex Viewer使用的各种图片、flash资源所在的目录。

4) com.esri.viewer:该包及其子包的内容是Flex Viewer的主体程序,这里所说的“主体程序”是相对于Widget而言的。主体程序中实现了Widget的基础、对Widget的管理、以及除自定义Widget之外的所有功能,这部分内容将在后边的小节介绍。

5) widgets:Flex Viewer中所有的Widget都在此包中。Widget基于Module开发,Module是Adobe为解决Flex系统体积过大而提出的一种解决方案,较大的Flex系统可以通过Module将系统进行分割,从而减小系统初始化所需加载的体积。Flex Viewer通过Widget将业务功能进行划分,每个Widget都是一个功能相对完善和独立的组件,每个Widget可以独立完成一个或者一组相关操作。

6) libs:这是Flash Builder中Flex工程存放库文件的目录,agslib-2.3-2011-03-01.swc是AGS Flex API 2.3的库文件。根据不同的业务需求,可能会用到更多的库文件,那么这些库文件都将放到这个目录下。

7) locale:Flex Viewer支持国际化,国际化所需的属性文件全部放在该目录下。在Flash Builder中,可通过指定编译参数来决定使用哪种语言,如下图所示:

 

3、   FlexViewer架构分析

 

在com.esri.viewer.managers包中,汇集了Flex Viewer中所有的Manager。这些Manager各个肩负重任,他们虽隐身幕后,却是Flex Viewer良好运转的关键,我们不妨称之为主席团成员。

1)     ConfigManager

Flex Viewer通过配置文件来组织数据、功能和UI。ConfigManager的责任是适时读取配置文件,对配置文件进行解析,然后将解析结果分发出去,由其他需要使用配置文件数据的模块接收。

2)     DataManager

Flex Viewer各个部分之间需要共享数据,比如说Widget与Widget之间数据共享。DataManager提供了一种数据共享的方案,任何模块都可以通过DataManager把数据贡献出来,供其它模块使用。 DataManager的关键职责是把共享数据保存在内存中,任何时候都可以从DataManager获取需要的共享数据。

3)     MapManager

Map是GIS应用的基础。MapManager解决了Map的问题。MapManager不是对Map的简单封装,而是提供了所有与Map相关的操作,比如根据配置文件加载地图,放大、缩小这些基本操作,画图,在地图上显示信息框,图层控制等等。如果在某个自定义Widget中,想要画一个多边形,不必new一个DrawTool,发个消息告诉MapManager你想画个多边形即可;如果你想在地图上某个点上显示一些信息,同样发个消息告诉MapManager就行了。MapManager会很友好地帮我们完很多工作,我们只需发个消息知会一声。

4)     ScriptingManager

预留,尚未使用。

5)     SecurityManager

预留,尚未使用。

6)     UIManager

Flex Viewer用户体验之所以风格统一,是因为UIManager做了大量的工作。在配置文件中,有如下脚本,UIManager会根据这一信息对UI的样式进行配置。

<style>

              <colors>

0xFFFFFF,0x333333,0x101010,0x000000,0xFFD700

</colors>

              <alpha>0.8</alpha>

</style>

7)     WidgetManager

顾名思义,WidgetManager是对Widget进行管理的组件。WidgetManager对Widget的管理包括根据配置文件创建Widget信息列表、加载Widget、布局Widget、关闭Widget等。WidgetManager提供四种Widget布局方式:自由布局(float)、水平布局(horizontal)、垂直布局(vertical)和固定布局(fix)。自由布局Widget可以拖动,水平布局、垂直布局和固定布局Widget完全有WidgetManager管理,不可拖动;四种布局方式中,固定布局Widget不可改变窗口大小。

3.2 Flex Viewer松耦合的关键

系统耦合度是决定系统灵活性与可维护性的关键。Flex Viewer的松耦合设计是其健壮的关键因素之一。那么,是什么保证了FlexViewer的松耦合呢?

“事件!事件!还是事件!”

事件是Flex Viewer松耦合的关键。在3.1中多次提到了“消息”,物化到Flex Viewer中就是事件。不同的模块通过事件彼此交互、传递数据,保证了各模块之间的松耦合,彼此不必相知,却能紧密合作。ViewerContainer、EventBus、AppEvent组成了Flex Viewer事件机制的基础。当然,Flex Viewer事件机制的基础是Flex的事件机制。

1)     EventBus

继承自EventDispatcher,使用了单例模式。EventBus是全局的事件派发器,为Flex Viewer中的不同模块之间的交互提供便利。有了EventBus,不同模块之间的交互无需彼此调用对方的方法,只需派发/监听消息即可。

2)     AppEvent

继承自Event,在Flex Viewer中被用来当做消息和数据的载体,在不同的模块之间传递数据。AppEvent定义了Flex Viewer中需要的所有事件类型,也就是那些公共的字符串常量。通常,系统层面需要添加的事件也都定义在AppEvent中。

3)     ViewerContainer

继承自Group,使用了单例模式,是Flex Viewer中各个模块的容器。Flex Viewer中调用ViewerContainer最多的三个方法是:addEventListener()、dispatchEvent()、showError()。我们可以在任何需要对某类AppEvent事件进行监听的地方通过addEventListener()方法添加监听,可以在任何需要派发某类AppEvent事件的地方通过dispatchEvent()方法派发事件,可以在任何需要显示Error信息的地方通过showError()方法显示Error信息。如果通读Flex Viewer代码,会发现,AppEvent的监听与派发随处可见。

Flex Viewer松耦合的关键因素还有Widget的设计和实现,这部分内容将在后面的小节涉及,在此按下不表。

至此,我们的讨论涉及到了Flex Viewer中的绝大部分模块,先来看一下Flex Viewer的整体结构,如图3.1所示。图中最下方的Control Widgets和Business Widgets尚未提及,我们将在3.4中涉及这部分内容。

 

         图 3.1 Flex Viewer整体结构

3.3 初始化那些事儿

在浏览器地址栏输入Flex Viewer的地址,经过短暂等待,当她华丽丽地展现在我们眼前,你是否想过在这短暂的等待中,Flex Viewer都做了哪些事情呢?本小节我们来探讨Flex Viewer初始化那些事儿。注意,我们这里所说的Flex Viewer初始化,不是Flex概念中组件生命周期的初始化部分,而是指Flex Viewer在可以与用户交互之前,所做的准备工作。

 

图 3.2 Flex Viewer初始化过程

3.2小节中,我们强调通过使用事件,Flex Viewer将各模块之间充分解耦。实际上,事件也伴随着Flex Viewer初始化的整个过程。Flex Viewer的初始化过程如图3.2所示。

1)     首先观察一下ConfigManager的构造方法,其中有这样一句代码:

ViewerContainer.addEventListener(ViewerContainer.CONTAINER_INITIALIZED,init);

也就是说,ConfigManager实例在构造期间就通过ViewerContainer在EventBus的单例添加了对CONTAINER_INITIALIZED事件的监听,一旦ViewerContainer在别的地方派发了CONTAINER_INITIALIZED事件,ConfigManager将响应该事件,开始读取配置文件、解析配置文件,构造ConfigData数据,最后将ConfigData连同CONFIG_LOADED事件派发出去,见下面代码:

ViewerContainer.dispatchEvent(newAppEvent(AppEvent.CONFIG_LOADED, configData));

2)     我们再观察一下UIManager、WidgetManager和MapManager的代码,在各自的初始化代码中,都可以找到下面一句代码,此处不再解释:

ViewerContainer.addEventListener(AppEvent.CONFIG_LOADED,config);

3)     最后,问题的关键在于CONTAINER_INITIALIZED事件何时何地被派发?我们观察一下ViewerContainer的init()方法,其中有下面这句代码:

ViewerContainer.dispatch(ViewerContainer.CONTAINER_INITIALIZED);

而init()方法是ViewerContainer的creationComplete事件的响应方法。也就是说ViewerContainer初始化结束才是Flex Viewer初始化的开始。

Flex Viewer初始化的脉络已经相当清晰,ConfigManager、UIManager、WidgetManager、MapManager在各自的初始化过程中对相应的事件进行监听,一旦ViewerContainer初始化完成,派发出CONTAINER_INITIALIZED事件,其它的准备工作将顺其自然,水到渠成。

我们注意到,初始化过程所涉及的各个模块都是相互透明的,彼此并不知道有对方的存在,而这些模块之所以能够亲密协作,关键就在于它们都只关心事件,事件机制使得这些模块之间实现松耦合。实际上,事件充斥着Flex Viewer的各个角落,随着对Flex Viewer理解的逐渐深入,对这一点的体会将更加明显。

3.4 Widget设计及实现

 一个Widget是对一组相关操作的封装,这些相关操作完成某项特定业务功能。Widget不仅可以调用地图资源,也可以访问服务器端资源。Widget基于Flex Module,编译后是独立的swf文件,这样一来,将具体的业务逻辑封装在Widget中,减小了系统主体的体积,在业务功能较多时,可显著缩短系统加载时间。加之面向接口编程和依赖注入思想的应用,Flex Viewer中的Widget可以独立开发,通过配置文件决定提供哪些Widget,从而决定系统提供哪些业务功能。可以把Widget理解为“插件”。这种灵活的“插件”机制是如何实现的呢?下面我们了解一下Widget编程模型,如图3.3所示。

 

 

图 3.3 Widget编程模型

Widget分为两种,Control Widget和Business Widget。Control Widget是指控制组件,比如NavigationWidget、HeaderControllerWidget、OverviewMapWidget等,这些Widget提供系统级别的功能,不针对具体业务功能;Business Widget是指业务组件,比如SearchWidget、BookmarWidget、GeoRSSWidget等,这些Widget提供具体业务功能。两种Widget都继承自BaseWidget,都由WidgetManager来管理,不同的是,在WidgetManager中有一个WidgetContainer来专门管理Business Widget。

两种Widget有共同的父类BaseWidget,BaseWidget实现了接口IBaseWidget,两者也就有了共同的接口。WidgetManager通过IBaseWidget来管理Widget,与具体的Widget解耦。FlexViewer此处使用面向接口编程和依赖注入,实现了主体系统与Widget的松耦合。

IBaseWidget

该接口定义了WidgetManager与Widget进行交互的方法,BaseWidget实现了这个接口。

BaseWidget

该类是所有Widget的基类。只要某一组件继承自BaseWidget,WidgetManager即可对其进行管理。由于BaseWidget继承自Module,每一个Widget都将编译成独立的swf文件。

但两种Widget在具体实现上有所不同。Flex Viewer为Business Widget提供了统一的UI基类和接口,也就是WidgetTemplate和IWidgetTemplate。通常情况下,Control Widget都会使用自定义UI,但是这不是必须的。

IWidgetTemplate

该接口定义了BaseWidget与WidgetTemplate交互的方法。WidgetTemplate实现了这一接口。

WidgetTemplate

该类是IWidgetTemplate的一种默认实现,在具体的Widget实现中,同样可以使用自定义的WidgetTemplate,只要实现IWidgetTemplate接口即可。WidgetTemplate为Widget提供基本UI控件,包括窗口面板、带有图片按钮的标题栏等。使用Flex Viewer实现的WidgetTemplate,开发人员可以将更多的精力和时间放在实现业务逻辑上。

需要说明的是,Widget的设计在Flex Viewer 1.x版本和2.x版本中有所区别。在1.x中不存在ControlWidget,比如菜单组件,菜单项是可配置的,但是菜单组件本身是不可配置的。2.x版本以后,Widget的概念扩大了,Flex Viewer中,凡是能看到的组件都是Widget,这样一来,控制组件可以像业务组件一样可配置,比如HeaderControllerWidget,灵活性大大提高。

 

4、   FlexViewer2.3自定义Widget

1)  安装Flash Builder,下载Flex Viewer源码,我们使用最新的2.3;

2)  打开Flash Builder,导入Flex Viewer 2.3的源代码;

3)  鼠标放在widgets包上,单击右键,在弹出的菜单中选择New,然后单击MXML Component;

 

4)  在New MXML Component对话框中,输入包名“widgets.HelloWorld”,填写Widget名称“HelloWorldWidget”,并选择基类BaseWidget,单击Finish; 

5)  此时,HelloWorldWidget已经创建完毕,按照Flex Viewer提倡的做法,在其包下新建一个同名xml配置文件,即“HelloWorldWidget.xml”;

6)  此时,HelloWorldWidget不会被编译,因为还未把它加入到Module列表。打开工程的属性窗口,点击Flex Modules,点击Add键,将HelloWorldWidget加入到Module列表中。点击OK,会发现HelloWorldWidget的图标已经和其他的Widget一样;

 

7)  启动编译,编译后会发现在bin-debug目录下,HelloWorldWidget已经被编译成swf文件;

8)  下面开始实现HelloWorldWidget的业务逻辑,在Widget面板中显示出配置文件中的“Hello,Flex Viewer!”字符串。配置文件内容:

<?xml version="1.0" ?>

<configuration>

<hellocontent>Hello, Flex Viewer!</hellocontent>

</configuration>

HelloWorldWidget代码:

<?xml version="1.0" encoding="utf-8"?>

<viewer:BaseWidgetxmlns:fx="http://ns.adobe.com/mxml/2009"

                     xmlns:s="library://ns.adobe.com/flex/spark"

                     xmlns:mx="library://ns.adobe.com/flex/mx"

                      xmlns:viewer="com.esri.viewer.*"

                      layout="absolute"

                      width="400"

                      height="300"

                      widgetConfigLoaded="init()">

     <fx:Script>

         <![CDATA[

              [Bindable]

              private varhelloContent:String;

              privatefunction init():void{

                   if(configXML){

                       helloContent=String(configXML.hellocontent);

                   }

              }

         ]]>

     </fx:Script>

     <viewer:WidgetTemplate>

         <s:HGroupwidth="100%"

                     height="100%"

                     horizontalAlign="center"

                     verticalAlign="middle">

              <s:Labeltext="{helloContent}"/>

         </s:HGroup>

     </viewer:WidgetTemplate>

</viewer:BaseWidget>

9)  在config.xml对HelloWorldWidget进行配置,如下:

<widgetcontainer>

<widget label="HelloWorld"icon="assets/images/i_solar.png"

          config="widgets/HelloWorld/HelloWorldWidget.xml"

          url="widgets/HelloWorld/HelloWorldWidget.swf"/>

</widgetcontainer>

10)编译,运行!Hello,Flex Viewer!

 

 

5、   FlexViewer2.3Widget之间的通信

虽然每个Widget都是封装良好的一个组件,提供一组针对特定业务功能的操作,但是有时候需要Widget之间的彼此协作来完成一个粒度更大的业务逻辑。此时就需要Widget之间的交互,或者说通信。经常看到这样的问题“一个Widget如何调用另外一个Widget的方法?”。Widget之间彼此相互独立,互不知晓,“一个Widget调用另一个Widget的方法”意味着两个Widget紧密地耦合在了一起,这不符合“松耦合”的要求。那么Widget之间该如何交互呢?答案还是事件!还记得ViewerContainer的addEventListener()和dispatchEvent()方法么?ViewerContainer通过EventBus添加监听和派发事件,使各模块默契协作。

5.1  直接通信

直接通信是指Widget A监听某一事件E,Widget B在任何时间派发了事件E,WidgetA都能够捕捉到这个事件,并进行响应。当然,这里对事件的监听和捕捉都是通过ViewerContainer代理的EventBus实现的,参见图5.1。我们注意到,Widget A与Widget B除了通过全局的EventBus监听、派发、捕捉事件外,没有发生任何直接的联系。

图 5.1 Widget之间直接通信

①     Widget A通过EventBus监听事件E

②     Widget B通过EventBus派发事件E

③     Widget A通过EventBus捕捉事件E,并进行响应。

5.2  间接通信

直接通信有一个前提条件,那就是必须交互的两个Widget都已经被加载。Widget采用lazy-load的加载方式,也就是只有第一次打开的时候才会加载。如果Widget B已经加载并派发了事件E,而此时Widget A还并未加载,Widget A是无法捕捉并响应事件E的,因为Widget A在内存中还不存在。

此时的解决方法是通过DataManager间接通信。DataManager响应Widget B派发的事件,并数据存储起来,Widget A在第一次加载时通过DataManager获取Widget B共享的数据,以此可以达到通信的目的,参见图5.2。同样的,Widget A与WidgetB,以及两者与DataManager之间并未直接交互,所有的协作都通过事件来完成。

 

①     DataManager通过EventBus监听共享数据事件获取数据事件

②     Widget B派发共享数据事件

③     DataManager响应共享数据事件,将数据以key-value的形式保存;

④     Widget A监听分发数据事件,并且派发获取数据事件

⑤     DataManager响应获取数据事件,查询Widget A所需数据,派发分发数据事件

⑥     Widget A响应分发数据事件,并做相应的响应动作。

DataManager是Flex Viewer为模块之间共享数据和保存共享数据设计的一种解决方案。DataManager将各个模块需要共享出来的数据存储在内存中,一旦某个模块需要获取共享数据,派发一个消息即可,DataManager会响应这个消息,并共享数据作为消息的载体派发出去。

DataManager响应的事件

u  DATA_FETCH_ALL:获取数据事件(全部数据)

u  DATA_PUBLISH:共享数据事件

u  DATA_FETCH:获取数据事件(根据key获取数据)

DataManager派发的事件

u  DATA_SENT:分发数据事件

u  DATA_NEW_PUBLISHED:当有新的数据被共享时,将新数据分发出去

 

 

6、   FlexViewer2.3中拉帘Widget下载

由于最新的ArcGIS API for flex2.x使用了Flex SDK4,因此造成了对之前ArcGIS API for flex1.x制 作的拉帘Widget无法使用,因此重新制作了拉帘工具供大家使用。

下载地址:

Swipe

使用方法:将下载后的swipe.zip解压,将swipe目录拷贝到flexViewer工程的src\widgets目录下,并 将Widget加入到flexViewer工程的模块(Flex Module)中,编译。最后在congfig.xml中增加为 swipeWidget的引用。

截图如下:

 

原创粉丝点击