首页 > 代码库 > Arcgis apis for flex项目实例—开发篇(3):地图级别控制器

Arcgis apis for flex项目实例—开发篇(3):地图级别控制器

      地图级别控制器俗称“鱼骨头”,这几乎是Web地图的标配了。ESRI的flex api自带了级别控制器叫做zoomSlider,我觉得是非常的难看,难看的和“鱼骨头”都不太沾边,给任何客户提供这样的一个组件都是非常不严肃的事情,我的选择是要么索性不提供这个工具,要么,就自己做一个。这是我做的组件的效果:

      这个组件可以整合很多的地图工具,本节只讨论中间的那个滑块部分,就是从“+”到“-”之间的部分,其他的工具留给下一节。

      先来看布局,一个Group把所有的部件框起来,放下两个按钮、两个Rect、两个BorderContainer,还有四个path。两个按钮很显然就是加减级别;一个BorderContainer作为级别条外框和刻度容器,两个个Rect分别是上部白色填充和下部蓝色填充;另一个BorderContainer作为滑块,滑块中放一个Rect既作为滑块的一个部分,又可作为级别刻度的模板;四个path就是右侧的级别标签。这些组件有一些相互压盖关系,顺序不要弄错。

    <!--mouseOver和mouseOut是用于控制右侧级别标签的显示,高度是动态的-->    <s:Group id="level_Contain" top="80" width="100%" height="{blockHeight + 40}" horizontalCenter="0"             mouseOver="mouseOverHandler(event)" mouseOut="mouseOutHandler(event)">        <commonitem:NavButton id="btn_plus" top="0" horizontalCenter="0" icon="image/plus.png" click="btn_plus_clickHandler(event)">        </commonitem:NavButton>        <!--上部填充Rect,只需填充为白色-->        <s:Rect id="upRect" top="20" width="7" height="{block.y-20}" horizontalCenter="0">            <s:fill>                <s:SolidColor color="#ffffff" />            </s:fill>        </s:Rect>        <!--下部填充Rect,只需填充为蓝色-->        <s:Rect id="downRect" top="{block.y+1}" width="7" height="{blockHeight - block.y + 20}" horizontalCenter="0">            <s:fill>                <s:SolidColor color="#269aea" />            </s:fill>        </s:Rect>        <!--作为级别条边框的BorderContainer,height加2让显示平滑-->        <s:BorderContainer id="slider" top="19" width="9" height="{blockHeight + 2}"                           backgroundAlpha="0.0" borderColor="#535a61" borderWeight="1"                           horizontalCenter="0">        </s:BorderContainer>        <!--滑块-->        <s:BorderContainer id="block" y="{initBlock}" width="15" height="9"                           borderColor="#535a61" buttonMode="true" horizontalCenter="0"                           mouseDown="block_mouseDownHandler(event)">                <s:Rect id="level_block" width="5" height="1" horizontalCenter="0" verticalCenter="0">                <s:fill>                    <s:SolidColor color="#535a61" />                </s:fill>            </s:Rect>        </s:BorderContainer>                <commonitem:NavButton id="btn_sub" top="{20+blockHeight}" horizontalCenter="0" icon="image/sub.png" click="btn_sub_clickHandler(event)">        </commonitem:NavButton>        <!--街道,节约篇幅,市、省、国三级类似不放出了-->        <s:Path horizontalCenter="20" y="{(mapLevels - countyLevel) * 8 + 15}" data="M 0 10 L 8 0 L 25 0 L 25 20 L 8 20 Z" visible="{isLabelShown}">            <s:fill>                <s:SolidColor color="#269aea" />            </s:fill>        </s:Path>        <s:Label horizontalCenter="22" y="{(mapLevels - countyLevel) * 8 + 19}" color="#FFFFFF" fontWeight="bold" text="街" visible="{isLabelShown}"/>        </s:Group>

      布局里的按钮是我自己做的,也可以直接用button组件、图片来代替,不是重点。布局里很多部件的height、top、y都用了绑定值,这是为了适应map的LODS的需要。因为组件有大量的绑定值,所以需要定义比较多的变量,具体涵义见注释。重点说一下传入主map的方式,这个和前文鹰眼图有所不同,用了一个set属性。回顾一下,在鹰眼图中,为了防止组件生命周期不一致,地图和组件分开进行初始化。这里没有自己的map,没办法分成两个层面初始化,我就用了一个isCompleted来标识组件初始化完毕,这样组件就可以在传入map属性和组件初始化完成这两个节点上择机进行初始化。这样做主要是因为要动态添加级别刻度,这个是没办法用绑定的方式完成,必须使用Group的addElement()方法,若是传入map时,Group还没有进入自己的生命周期会带来灾难性后果。同时,这样做,如果map的级别发生了变化,组件还会重新刷新刻度。

            //用set属性传入map,顺便做一些初始化            private var _map:Map;            public function set map(value:Map):void            {                _map=value;                _map.addEventListener(ExtentEvent.EXTENT_CHANGE,mapExtentChanged);                if(isCompleted)                    init();            }            public function get map():Map            {                return _map;            }                        //允许label显示            public var labelVisible:Boolean = true;                        //控制label显示            [Bindable]            private var isLabelShown:Boolean = false;                        //默认的国家级别            [Bindable]            public var nationLevel:int = 4;            //默认的省级别            [Bindable]            public var provinceLevel:int = 7;            //默认的市级别            [Bindable]            public var cityLevel:int = 12;            //默认的街道级别            [Bindable]            public var countyLevel:int = 15;                    //初始滑块位置                [Bindable]            private var initBlock:int;            //初始级别条高度            [Bindable]            private var blockHeight:int;            //地图的总级别数            [Bindable]            private var mapLevels:int;                        //初始的级别刻度top值            private var initTop:int = 25;            //标识组件初始化状态            private var isCompleted:Boolean = false;            //鼠标拖拽滑块时的初始点击位置            private var initY:Number;            //鼠标拖拽滑块时的初始滑块位置            private var initBlockY:Number;            //用isCompleted来避免组件生命周期不一致带来BUG            private function creationCompleteHandler(event:FlexEvent):void            {                isCompleted = true;                init();                                }            //初始化获取当前地图的级别数,滑块起始位置,级别条高度,以及放置刻度            private function init():void            {                if(_map)                {                    mapLevels = _map.lods.length;                                        initBlock = 21 + (mapLevels - map.level)*8;                    blockHeight = 5 + (mapLevels-1) * 8 + 5;                    slider.removeAllElements();                    for(var delta:int = 1;delta<=mapLevels;delta++)                    {                        var block:Rect = new Rect();                        block.width=5;                        block.height=1;                        block.horizontalCenter=0;                        block.fill = level_block.fill;                        block.top = initTop;                        slider.addElement(block);                        initTop += 8;                    }                }            }

      然后就是map与滑块之间协同的一些事儿要处理,这个和鹰眼图有点类似,就是map的extentChange和滑块的mousedown,不同的是,因为这个组件比较小,用户拖拽滑块很容易跑出组件范围,所以要在mousedown以后把move和up事件绑定到全局,这样操作就非常平滑了。

            //点击滑块条启用拖拽,注意要对整个this.parent绑定move和mouseup,这是因为组件本身有点小,鼠标很可能会在拖拽时移出组件,确保鼠标在任何位置都不会中断操作            private function block_mouseDownHandler(event:MouseEvent):void            {                initY = event.stageY;                initBlockY = block.y;                this.parent.addEventListener(MouseEvent.MOUSE_MOVE,block_mouseMoveHandler);                this.parent.addEventListener(MouseEvent.MOUSE_UP,block_mouseUpHandler);            }            //滑块只在y方向变化,所以只考虑y值,而且滑块不能移出最大和最小刻度            private function block_mouseMoveHandler(event:MouseEvent):void            {                var deltaY:Number = event.stageY - initY;                var buttonY:Number =  initBlockY + deltaY;                block.y = buttonY;                                if(buttonY < 21)                    block.y = 21;                else if(buttonY > 11+blockHeight)                    block.y = 11+blockHeight;                else                    block.y = buttonY;            }            //松开鼠标滑块必须落在某个刻度的位置上,地图也要相应变化            private function block_mouseUpHandler(event:MouseEvent):void            {                //level = 12 - Math.round((btn_Slider.y - 26)/10);                block.y = (Math.round((block.y - 21)/8)) * 8 + 21;                map.level = mapLevels - Math.round((block.y - 21)/8)-1;                                this.parent.removeEventListener(MouseEvent.MOUSE_MOVE,block_mouseMoveHandler);                this.parent.removeEventListener(MouseEvent.MOUSE_UP,block_mouseUpHandler);            }                                    //图动滑块也要动,这个事件让“+”、“-”按钮也变得很容易实现            private function mapExtentChanged(event:ExtentEvent):void            {                block.y = (mapLevels - map.level - 1) * 8 + 21;            }

      然后就是把“+”和“-”两个按钮的控制加上,有了前面的工作,这个很容易。

            private function btn_plus_clickHandler(event:MouseEvent):void            {                map.level+=1;            }                        private function btn_sub_clickHandler(event:MouseEvent):void            {                map.level-=1;            }

      处理一下级别标签。我这里做的比较简单,就是可以通过属性设置是否需要这个标签,如果需要的话就把国、省、市、街的级别设置一下,鼠标移入就可以出现在指定级别刻度旁,移出就看不见。

            //这个就是控制四个级别标签在鼠标移入时显示,移出时消失            private function mouseOverHandler(event:MouseEvent):void            {                if(labelVisible)                    isLabelShown = true;            }                        private function mouseOutHandler(event:MouseEvent):void            {                if(labelVisible)                    isLabelShown = false;            }

      最后是在主map的页面里面调用它:

        <component:MapNavigator left="10" top="10" map="{map}" labelVisible="true"                                 nationLevel="4" provinceLevel="7" cityLevel="12" countyLevel="15">        </component:MapNavigator>

      到此,这个“鱼骨头”就完成了,还有平移、放大、缩小、测距几个工具留给下文。惯例总结一下。这个“鱼骨头”的核心问题是要让滑块和主map的级别变化协同,使用的思想和鹰眼图十分类似。具体的程序中,需要通过一些绑定的变量来控制级别条、上下部填充、滑块的高度、位置等,这些都要能适应当前地图,并且动态变化。

Arcgis apis for flex项目实例—开发篇(3):地图级别控制器