前文已完成了基本的地图工具,现在要开始一个GIS项目的核心内容——查询。按这个项目的设计,查询要分为政区查询和关键字查询两类,其实都大同小异,这里就只按关键字查询来写了。
查询在一个系统中一般有几个要素,一是要有条件输入,这里就是一个文本框和一个搜索按钮;二是要有结果列表,项目设计的左侧区域就承担了这个任务,计划用一个list来实现;三是要有地图展示,地图展示主要是把查询结果在地图上用符号标注出来,并且能提供一个infowindow来展示详细信息。本节内容不包括第三点,与地图的一些联动在下节单独介绍。
首先我要调整一下框架的布局,主要是左侧栏,先看mxml。
AHTour.mxml:
<!—将左侧扩充为300的宽度--> <s:BorderContainer left="0" top="80" bottom="0" width="300" backgroundColor="#FFFFFF" borderWeight="2"> <mx:TabNavigator id="b_left" left="5" right="5" top="5" bottom="5"> <s:NavigatorContent width="100%" height="100%" label="景点查询"> <!--提供一个group用于界面的切换--> <s:Group id="mainContent" left="10" right="10" top="5" bottom="10"> <component:LeftSearchPage left="0" right="0" top="0" bottom="0" QueryStart="QueryStartHandler(event)"> </component:LeftSearchPage> </s:Group> </s:NavigatorContent> </mx:TabNavigator> </s:BorderContainer> <s:BorderContainer id="b_map" left="300" right="0" top="80" bottom="0"> </s:BorderContainer>
代码中我略去了政区查询的布局,核心就是左侧栏扩充为300的宽度,这是为了显示的需要,map的border相应的left也置为300。而后就是我把Tab中的内容用一个group框了起来,并把之前的textInput和搜索按钮放到一个组件LeftSearchPage中去,这是为了方便控制界面得切换,LeftSearchPage代码在后面给出。
先来看查询,因为是要查询数据库,要用到后台代码。我在spring mvc当中做了一个查询,spring mvc不是本文重点,我只贴部分核心代码:
//dao层中的查询 public List<?> getDataByCol(String tabName, String colName,String colValue) { String sqlStr = "select * from "+tabName+" where "+ colName + " like ‘%"+ colValue+"%‘"; return jdbcTemplate.queryForList(sqlStr); } //service层中调用dao public List<?> getDataByCol(String tabName,String colName,String colValue) { return baseDao.getDataByCol(tabName, colName, colValue); } //数据获取controller @SuppressWarnings({ "rawtypes", "unchecked" }) @RequestMapping("/query") public @ResponseBody HashMap Query(HttpServletRequest request, @RequestParam("keyword")String keyword) throws Exception { HashMap map = new HashMap(); byte bb[]; bb = keyword.getBytes("ISO-8859-1"); //以"ISO-8859-1"方式解析字符串 keyword= new String(bb, "UTF-8"); //再用"utf-8"格式表示字符串 map.put("result", baseService.getDataByCol("sp_tour", "name", keyword)); map.put("success", true); return map; }
写完服务后,形成一个http接口提供数据:http://localhost:8080/ahlv/base/query.html?keyword=XXXX,用合肥野生动物园做关键字可以获得这样的结果:
{ "result":[ { "NAME":"合肥野生动物园", "URL":"http://tour.ahta.com.cn/Scenic---view2011---1475.shtml", "REGION":"合肥市", "LON":117.16, "LAT":31.83 } ], "success":true }
下面回到前台,我们先设计一个用于查询的类,先上代码:
QueryTool.as
//要注册两个事件,在完成时传递结果,失败时传递错误信息 [Event(name="QueryCompleted", type="tool.CustomEvent")] [Event(name="QueryFailed", type="tool.CustomEvent")] public class QueryTool extends EventDispatcher { public function QueryTool() { } public function Query(keyword:String):void { var httpRequest:HTTPService = new HTTPService(); httpRequest.url = "http://localhost:8080/ahlv/base/query.html"; httpRequest.method = "GET"; httpRequest.addEventListener(ResultEvent.RESULT,resultHandler); httpRequest.addEventListener(FaultEvent.FAULT,faultHandler); httpRequest.send({keyword:keyword}); function resultHandler(event:ResultEvent):void { var resultObj:Object = JSON.parse(event.result.toString()); //判断是否失败 if(!Boolean(resultObj.success)) { dispatchEvent(new CustomEvent(CustomEvent.QueryFailed,"查询失败!")); return; } //将结果转换为graphic var resultArray:ArrayCollection = new ArrayCollection(); for each(var gobj:Object in resultObj.result) { var picSymbol:PictureMarkerSymbol = new PictureMarkerSymbol("image/locator.png",22,28,0,14,0); var graphic:Graphic = new Graphic(new MapPoint(Number(gobj.LON),Number(gobj.LAT)),picSymbol,gobj); resultArray.addItem(graphic); } dispatchEvent(new CustomEvent(CustomEvent.QueryCompleted,resultArray)); } } protected function faultHandler(event:FaultEvent):void { dispatchEvent(new CustomEvent(CustomEvent.QueryFailed,event.fault.faultString)); } }
这里用到了CustomEvent这个事件类,这也是会被项目中反复用到的一个类,来看一下它的定义。这个自定义事件类是标准写法,上面定义了四种事件类型,现在要用的是QueryCompleted和QueryFailed,其他的后面很快会用到。
CustomEvent.as
public class CustomEvent extends Event { public static const QueryCompleted:String = "QueryCompleted"; public static const QueryStart:String = "QueryStart"; public static const BacktoFront:String = "BacktoFront"; public static const QueryFailed:String = "QueryFailed"; public var data:Object; public function CustomEvent(type:String,data:Object = null) { super(type,bubbles,cancelable); this.data = data; } override public function clone():Event{ return new CustomEvent(type,data); } }
当然光能拿到数据还不够,还需要配置好数据显示的列表,这就需要一个LeftResultPage,这个Page就是在查询成功后替换LeftSearchPage,用于显示结果列表。
LeftSearchPage.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 tool.CustomEvent; [Bindable] private var result:ArrayCollection; protected function back_clickHandler(event:MouseEvent):void { dispatchEvent(new CustomEvent(CustomEvent.BacktoFront)); } ]]> </fx:Script> <fx:Declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:Declarations> <s:Group width="100%" height="30"> <s:Label left="0" text="{‘共查到‘+ result.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="{result}"></s:List> </s:VGroup>
这里用到了一个listRenderer,这是要实现一个比较复杂的list项必须的,好吧,不要怕烦,写一下这个renderer。注意这里的data是一个graphic,要照着graphic取属性的格式来写。
listRenderer.mxml
<?xml version="1.0" encoding="utf-8"?> <s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" autoDrawBackground="true" width="250" height="70"> <s:Image left="10" source="image/locator.png" verticalCenter="0"/> <s:VGroup left="42" right="10" top="10" bottom="10"> <s:Label color="#19AF01" fontSize="14" text="{data.attributes.NAME}"/> <s:Label color="#8D8D8D" text="{data.attributes.REGION}"/> </s:VGroup> </s:ItemRenderer>
再来看看如何使用这个查询类来进行查询,首先看一看LeftSearchPage是怎么写的。
LeftSearchPage.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="400" height="300"> <fx:Metadata> [Event(name="QueryStart", type="tool.CustomEvent")] </fx:Metadata> <fx:Script> <![CDATA[ import mx.controls.Alert; import tool.CustomEvent; protected function query_clickHandler(event:MouseEvent):void { if(txt_Keyword.text.length > 0) dispatchEvent(new CustomEvent(CustomEvent.QueryStart,txt_Keyword.text)); else Alert.show("请输入关键字!"); } ]]> </fx:Script> <fx:Declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:Declarations> <s:TextInput id="txt_Keyword" left="5" right="65" top="0" height="22" prompt="请输入关键字"/> <s:Button right="10" top="0" width="50" height="22" label="查询" click="query_clickHandler(event)"/> </s:Group>
这边定义了QueryStart事件,在搜索按钮点击时把关键字传递给AHTour.mxml,这是为了方便页面切换和处理过期数据,把查询类放到主页面中去执行,子页面只控制自己的界面元素。下面看AHTour里需要加的QueryStartHandler和backtofrontHandler两个事件监听:
AHTour.mxml
//主页Query private function QueryStartHandler(event:CustomEvent):void { // TODO Auto-generated method stub var query:QueryTool = new QueryTool(); query.Query(event.data.toString()); query.addEventListener(CustomEvent.QueryCompleted,queryCompletedHandler); query.addEventListener(CustomEvent.QueryFailed,queryFailedHandler); function queryCompletedHandler(event:CustomEvent):void { var queryResult:LeftResultPage = new LeftResultPage(); queryResult.map = map; queryResult.result = event.data as ArrayCollection; //返回主页的事件监听 queryResult.addEventListener(CustomEvent.BacktoFront,backtofrontHandler); queryResult.bottom = 0; queryResult.top = 0; mainContent.removeAllElements(); mainContent.addElement(queryResult); } function queryFailedHandler(event:CustomEvent):void { Alert.show(event.data.toString()); } } private function backtofrontHandler(event:Event):void { var searchPage:LeftSearchPage = new LeftSearchPage(); searchPage.left = 0; searchPage.right = 0; searchPage.top = 0; searchPage.bottom = 0; //启动查询的事件监听 searchPage.addEventListener(CustomEvent.QueryStart,QueryStartHandler); mainContent.removeAllElements(); mainContent.addElement(searchPage); }
这样就可以了,查询结果可以显示于这个列表中,点击列表的返回按钮又可以返回到查询页,触发下一次查询。看一下结果:
这一节内容有点长,主要是因为布局和事件带来了附加的代码。现在还只是把结果显示了出来,下一节将介绍把这个结果与地图联动的一系列做法。