在Flex中处理复杂的导航

问题描述:

我正在构建一个复杂的Flex应用程序,现在我处于导航成为问题的地步。我使用Viewstack与菜单栏,但我不知道如何清晰地构造这个。在Flex中处理复杂的导航

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

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

是否有任何指导方针如何解决Flex中更复杂的导航/网站层次结构?

现在,我在应用程序的viewstack中拥有所有视图,并将其与Application.application.appViews.selectedChild - >引用,但这显然不是最佳实践,因为它违反了封装。

正在考虑实施某种状态机,它负责处理所有这些事情,但不太清楚这是否合理,或者是否有更好的方法。

谢谢你们, 马丁

如果它真的复杂,你可能要考虑将您的应用程序成模块。

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

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

如果你有一个复杂的菜单栏需要启用/禁用很多不同的逻辑条件的按钮或选项,状态模式是一个体面的方式来处理它。我构建了一个企业级应用程序,在顶部有一个“类似于Word的”按钮栏......并且有太多不同的条件影响了按钮的状态,我必须将逻辑集中在一个位置。起初我没有使用国家模式,维护代码是一件困难的事情。有一天,我咬紧牙关,重新将所有的条件逻辑分解成一个StateManager类。从那里开始,它绝对让生活更轻松。

+0

嗨布赖恩, 谢谢你的答案。伴侣听起来有趣 - 对于这个项目,尽管我可能会坚持国家模式。然而,你是如何设法摆脱状态管理器中的Application.application引用的(我创建了一个Singleton类)? 我的菜单和我的ViewStack位于应用程序文件中,状态管理器经常引用它们。 谢谢, Martin – martin 2010-02-05 08:55:42

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

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

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

让我知道,

=布莱恩=

我可以给你我用你的一些小问题的方法,在运行时初始化页面,如何封装导航的问题。

对于页面初始化,我遇到的问题是,一旦您导航到页面时,并不总是知道是否应该显示某些元素,因为它不仅取决于整体用户权限,还取决于当前用户的权限,选定的数据。如果需要确定这些信息必须从服务器加载,则在加载信息时无法显示该页面。所以我们创建了一个名为LoadPanel的控件,它是一个容器,它可以用一个加载指示符覆盖内容,直到收到其他信息。下面是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并将用户“发送”到适当的区域。

希望这会给你一些你面临的挑战的想法。