前文已经完成了从数据库查询旅游景点的工作,下面就是要把这些查询的结果显示到地图上,同时要实现查询列表和地图的联动。也就是点击列表中的某一项,地图能定位至该位置并弹出infowindow。
这个开发工作本身没有太大的难度,但是我认为的增加了一点障碍,因为我不太喜欢arcgis api自带的infowindow,我需要自己做一个,这一节内容就从这个DIY的infowindow开始。首先来设计一下这个窗体,为了表示不抄袭,这个infowindow改名PopupWindow。
PopupWindow.mxml
<?xml version="1.0" encoding="utf-8"?> <s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="280" height="120" xmlns:esri="http://www.esri.com/2008/ags"> <fx:Script> <![CDATA[ import com.esri.ags.Graphic; import flash.net.navigateToURL; [Bindable] public var graphic:Graphic; protected function image_Close_clickHandler(event:MouseEvent):void { this.parent.removeChild(this); } ]]> </fx:Script> <!--气泡形外框--> <s:Path data="M 1 1 L 278 1 L 278 90 L 175 90 L 130 118 L 150 90 L 1 90 Z" winding="nonZero"> <s:stroke> <s:SolidColorStroke caps="none" color="#696969" joints="miter" miterLimit="4" weight="1"/> </s:stroke> <s:fill> <s:SolidColor color="#FFFFFF"/> </s:fill> </s:Path> <!--主要内容--> <s:BorderContainer left="1" right="1" top="1" height="30" backgroundColor="#EAEAEA" dropShadowVisible="false"> <s:Image id="image_Close" right="5" click="image_Close_clickHandler(event)" source="image/close.png" verticalCenter="0"/> <s:Label id="label_Title" left="10" color="#1D2DC6" fontFamily="宋体" fontSize="16" fontWeight="bold" text="{graphic.attributes.NAME}" verticalCenter="0"/> </s:BorderContainer> <s:Label id="label_Address" left="10" top="45" color="#6B6B6B" text="{graphic.attributes.REGION}"/> <!--查看详情时跳转至数据自带的链接--> <s:Label right="10" top="45" color="#0000FF" text="查看详情" textDecoration="underline" buttonMode="true" click="navigateToURL(new URLRequest(graphic.attributes.URL.toString()),'_blank')"/> </s:Group>
这个组件只有两个特别的技巧,一是用path来绘制气泡形外框,二是在关闭按钮点击时的处理,也都不是很难理解的东西。下面就是核心问题了,如何把这个东西放地图上?用map控件继承的addChild()方法,移除就用removeChild()方法。还要利用map控件自带的地理坐标与屏幕坐标转换方法来计算这个窗体在应用程序中的放置的屏幕坐标。这还不算完,我们还要考虑map在平移、缩放的时候,这个窗体应该是平滑的随着map而动,这就需要同时监听map的EXTENT_CHANGE、PAN_UPDATE、ZOOM_UPDATE三个事件,这三个事件代表了map在extent发生变化的任何情况,保证窗体可以完全跟随map而动。主要思想就这些,后面的工作就都在前文已经做的LeftResultPage.mxml中完成,下面就是修改过后的这个Page的全部代码。
LeftResultPage.mxml
<?xml version="1.0" encoding="utf-8"?> <s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="270" gap="0"> <!--注册事件用于点击返回按钮回到首页--> <fx:Metadata> [Event(name="BacktoFront", type="tool.CustomEvent")] </fx:Metadata> <fx:Script> <![CDATA[ import com.esri.ags.Graphic; import com.esri.ags.Map; import com.esri.ags.events.ExtentEvent; import com.esri.ags.events.PanEvent; import com.esri.ags.events.ZoomEvent; import com.esri.ags.geometry.MapPoint; import com.esri.ags.layers.GraphicsLayer; import com.esri.ags.symbols.PictureMarkerSymbol; import com.esri.ags.symbols.SimpleMarkerSymbol; import com.esri.ags.utils.GeometryUtil; import com.esri.ags.utils.GraphicUtil; import mx.collections.ArrayCollection; import spark.events.IndexChangeEvent; import tool.CustomEvent; //绑定map用于显示要素、控制popupwindow //赋值map时就把三个监听注册 [Bindable] private var _map:Map; public function set map(value:Map):void { _map = value; _map.addEventListener(ExtentEvent.EXTENT_CHANGE,mapExtentChanger); _map.addEventListener(PanEvent.PAN_UPDATE,panHandler); _map.addEventListener(ZoomEvent.ZOOM_UPDATE,zoomHandler); } public function get map():Map { return _map; } //将result改为set函数,组件中的绑定要换为resultArray //使用时先赋值map,再赋值result,这时result会为map添加要素 [Bindable] private var resultArray:ArrayCollection; public function set result(value:ArrayCollection):void { resultArray = value; if(map) { var gLayer:GraphicsLayer = AddGraphicLayer("query"); for each(var graphic:Graphic in resultArray) { gLayer.add(graphic); } map.zoomTo(GraphicUtil.getGraphicsExtent(resultArray.toArray())); } } public function get result():ArrayCollection { return resultArray; } //返回按钮中增加了移除要素和popwindow private function back_clickHandler(event:MouseEvent):void { RemoveGraphicLayer("query"); RemovePopup(); dispatchEvent(new CustomEvent(CustomEvent.BacktoFront)); } private function RemoveGraphicLayer(layerid:String):void { if(map.getLayer(layerid)!=null) { map.removeLayer(map.getLayer(layerid)); } } private function RemovePopup():void { if(map.getChildByName("popup")) { map.removeChild(map.getChildByName("popup")); } } //这个是常用添加GraphicsLayer函数 private function AddGraphicLayer(layerid:String):GraphicsLayer { var glayer:GraphicsLayer; if(map.getLayer(layerid)!=null) { glayer = map.getLayer(layerid) as GraphicsLayer; } else { glayer = new GraphicsLayer(); glayer.id = layerid; map.addLayer(glayer); } return glayer; } private function list_Result_changeHandler(event:IndexChangeEvent):void { var graphic:Graphic = list_Result.selectedItem as Graphic; var mappoint:MapPoint = graphic.geometry as MapPoint; //定位至选定要素,同时控制一下级别 map.zoomTo(mappoint); if(map.level < 12) map.level = 12; var popupWindow:PopupWindow; //若map已有popupwindow,就把popupwindow移动到当前位置 if(map.getChildByName("popup")) { popupWindow = map.getChildByName("popup") as PopupWindow; popupWindow.graphic = graphic; //popup的x、y是左上角定位的,要计算一下把定位点移至气泡底部,同时要和符号匹配 popupWindow.x = map.toScreenX(mappoint.x) - 130; popupWindow.y = map.toScreenY(mappoint.y) - 148; } //若map没有popupwindow,新建一个并放置到对应的位置 else { popupWindow = new PopupWindow(); popupWindow.graphic = graphic; popupWindow.name = "popup"; popupWindow.x = map.toScreenX(mappoint.x) - 130; popupWindow.y = map.toScreenY(mappoint.y) - 148; map.addChild(popupWindow); } } private function panHandler(event:PanEvent):void { PopupMove(); } private function zoomHandler(event:ZoomEvent):void { PopupMove(); } private function mapExtentChanger(event:ExtentEvent):void { PopupMove(); } //move其实就是重新计算定位点的屏幕坐标,移动一下popupwindow private function PopupMove():void { if(map.getChildByName("popup")) { var popupWindow:PopupWindow = map.getChildByName("popup") as PopupWindow; var mappoint:MapPoint = popupWindow.graphic.geometry as MapPoint; popupWindow.x = map.toScreenX(mappoint.x) - 130; popupWindow.y = map.toScreenY(mappoint.y) - 148; } } ]]> </fx:Script> <fx:Declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:Declarations> <s:Group width="100%" height="30"> <s:Label left="0" text="{'共查到'+resultArray.length+'个结果'}" verticalCenter="0"/> <mx:LinkButton right="0" label="返回" click="back_clickHandler(event)" verticalCenter="0"/> </s:Group> <s:List id="list_Result" width="100%" height="100%" itemRenderer="renderer.listRenderer" dataProvider="{resultArray}" change="list_Result_changeHandler(event)"></s:List> </s:VGroup>
如此全部设计功能实现,照例来一张效果:
到此,项目设计的主要功能就完成了。当然目前项目的这个样子就交付不是很合适,后面要对项目做一系列界面调整与美化,这就要开启我们的美工部分了。