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

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

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

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

      先来看布局,一个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):地图级别控制器

上一篇:仿win8磁贴界面以及功能


下一篇:windows server2003+iis6 网页打开,报无法显示xml页