处理Flex中的复杂导航

时间:2010-02-04 13:00:51

标签: flex actionscript-3

我正在构建一个复杂的Flex应用程序,现在我处于导航成为问题的地步。我使用带有菜单栏的Viewstacks,但我不确定如何清楚地构建它。

根据用户登录的用户和所选公司,他可以看到不同的页面。现在我限制了这个隐藏菜单栏中的相应按钮。但是,不仅菜单栏,而且应用程序内的按钮/链接应该能够导航到每个现有页面。

当我加载现有页面时,它需要一些初始化(取决于它从中加载的上下文)。此外,当选择公司时,我需要从后端加载状态,并根据此状态显示特定页面。

是否有任何指导如何在Flex中处理更复杂的导航/网站层次结构?

现在我在Application中的视图堆栈中拥有所有视图,并使用Application.application.appViews.selectedChild - >来引用它。但这显然不是最佳做法,因为它违反了封装。

正在考虑实施某种状态机,它负责所有这些,但不太确定这是否有意义,或者是否有更好的方法。

谢谢你们, 马丁

3 个答案:

答案 0 :(得分:1)

如果它非常复杂,您可能需要考虑将应用程序分解为模块。

此外,Mate是一个出色的Flex框架,用于处理复杂的通信和导航。 Mate的EventMaps可以帮助您集中组件,模块等之间的通信和逻辑。并且,它可以让您远离可怕的Application.application引用。

即使您不使用Mate这样的框架,也可以通过让组件调度自定义事件来避开Application.application引用,这些自定义事件会冒泡到应用程序的顶层。应用程序的顶层可以监听并捕获这些事件并对其进行操作。我发现这是一种更灵活的方法。我尽可能避免使用Application.application!

如果你有一个复杂的菜单栏需要根据许多不同的逻辑条件启用/禁用很多按钮或选项,那么状态模式是处理它的一种不错的方法。我构建了一个企业级应用程序,顶部有一个“类似Word”的按钮栏......有很多不同的条件会影响按钮的状态,我必须将逻辑集中在一个地方。起初我没有使用State模式,维护代码是一件困难的事。有一天,我咬紧牙关并将所有条件逻辑重新计算到StateManager类中。从那以后,它确实让生活更轻松。

答案 1 :(得分:0)

同样,您可能需要考虑使用自定义事件向您的应用程序广播重要事件。您可以将这些事件冒泡到应用程序级别。然后,通过在应用程序级别添加事件侦听器,您可以从应用程序级别捕获并响应这些事件和目标组件或模块。这为您提供了处理事件和“引导流量”的中心位置。它还可以防止Application.application方法的紧耦合。 (随着应用程序的增长和扩展,这很快就变成了一场噩梦!)

例如,您的StateManager可以包含case语句,用于决定应用程序需要处于哪种状态。一旦确定了有关当前状态的决定,您将调度自定义StateEvent。 (可能具有StateEvent.STATE_CHANGED和StateEvent.CURRRENT_STATE等属性)此事件可以冒泡到应用程序级别并被侦听器捕获。然后,侦听器调用一个方法来加载/更改状态。

这是否为您澄清?如果没有,也许我可以花一两个小时把一些样品放在一起。

让我知道,

=布赖恩=

答案 2 :(得分:0)

我可以为您提供我用于某些子问题的方法,在运行时初始化页面的问题以及如何封装导航。

对于页面初始化,我遇到的问题是,一旦导航到页面,是否应显示某些元素并不总是知道,因为它不仅取决于整体用户权限,还取决于当前所选的权限数据。如果必须从服务器加载确定此信息所需的信息,则在加载信息时无法按原样显示页面。因此,我们创建了一个名为LoadingPanel的控件,它是一个容器,可以覆盖带有加载指示符的内容,直到收到其他信息。这是ActionScript的缩短版本:

[DefaultProperty("children")]
public class LoadingPanel extends ViewStack
{
    public function LoadingPanel()
    {
        this.resizeToContent = false;
        super();
    }

    public function get children():Array { return _children }        
    public function set children(value:Array):void { _children = value; }

    public function get loadingImageStyle():String {
        return _loadingImgStyle; }
    public function set loadingImageStyle(value:String):void {
        _loadingImgStyle = value;
        if (_loadingIndic)
            _loadingIndic.loadingImageStyle = value; 
    }

    public function showLoadingIndicator():void 
    {
        if (_loadingIndic)
        {
            super.selectedChild = _loadingIndic;
        }
        else
        {
            _pendingLoadingIndic = true;
            var me:LoadingPanel = this;
            var listener:Function = function(event:Event):void
            {
                if (me._pendingLoadingIndic)
                    me.showLoadingIndicator();
            }
            addEventListener(FlexEvent.CREATION_COMPLETE, listener);
        }
    }

    public function hideLoadingIndicator():void 
    {
        _pendingLoadingIndic = false;
        if (_content)
        {
            super.selectedChild = _content;
        }
        else
        {
            var me:LoadingPanel = this;
            var listener:Function = function(event:Event):void
            {
                me.hideLoadingIndicator();
            }
            addEventListener(FlexEvent.CREATION_COMPLETE, listener);
        }
    }

    public function waitForEvent(target:EventDispatcher, event:String):void
    {
        _eventCount++;
        showLoadingIndicator();
        var me:LoadingPanel = this;
        target.addEventListener(
            event, 
            function(evt:Event):void 
            { 
                me._eventCount--; 
                if (!me._eventCount)
                {
                    me.hideLoadingIndicator();                        
                } 
            } 
        );
    }

    override public function addChild(child:DisplayObject):DisplayObject
    {
        var result:DisplayObject = child;
        if (_content)
        {
            result = _content.addChild(child);
            invalidateDisplayList();
        }
        else
        {
            if (!_children)
            {
                _children = [];
            }
            _children.push(child);
        }
        return result;
    }

    override protected function createChildren():void
    {
        super.createChildren();

        if (!_content)
        {
            _content = new Box();
            _content.percentWidth = 1.0; 
            _content.percentHeight = 1.0; 
            super.addChild(_content);
        }

        if (!_loadingIndic)
        {
            _loadingIndic = new LoadingIndicator();
            _loadingIndic.percentWidth = 1.0; 
            _loadingIndic.percentHeight = 1.0; 
            _loadingIndic.loadingImageStyle = _loadingImgStyle;
            super.addChild(_loadingIndic);
        }

        if (_children)
        {
            for each (var child:DisplayObject in _children)
            { 
                _content.addChild(child);
            }
        }
    }

    private var _loadingImgStyle:String = "loadingIndicatorDark";

    private var _loadingIndic:LoadingIndicator = null;
    private var _content:Box = null;
    private var _children:Array = null;
    private var _pendingLoadingIndic:Boolean = false;
    private var _eventCount:int = 0;
}

我们通常使用LoadingPanel包围内容然后调用面板的waitForEvent方法来使用它们。通常,我们等待的事件是进入Web服务响应。该类还允许您在显示其子项之前等待多个事件。

我要为您的项目提出的另一个建议是,您要考虑Flex中的深层链接。我们的用户赞赏能够在我们复杂的Flex应用程序中为资源/位置添加书签,以及能够在浏览器中点击刷新并返回到他们所在的同一“页面”。但实施深层链接也帮助我解决了你提到的问题之一;如何以封装方式将UI发送到特定页面?我们这样做的方法是引发一个包含目标“URL”的冒泡导航事件。然后,顶级导航“管理员”处理解释URL并将用户“发送”到适当的区域。

希望这能为您提供一些面对挑战的想法。