【问题标题】:Handling complex navigation in Flex在 Flex 中处理复杂的导航
【发布时间】:2010-02-04 13:00:50
【问题描述】:

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

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

当我加载现有页面时,它需要一些初始化(取决于加载它的上下文)。 In addition, when a company is chosen, I need to load the status from the backend, and depending on this status a specific page might be visible.

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

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

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

谢谢各位, 马丁

【问题讨论】:

    标签: apache-flex actionscript-3


    【解决方案1】:

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

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

    即使您不使用像 Mate 这样的框架,您也可以通过让组件分派自定义事件来避免 Application.application 引用,这些事件会冒泡到您的应用程序的顶层。应用程序的顶层可以侦听和捕获这些事件并对其采取行动。我发现这是一种更灵活的方法。我尽量避免 Application.application!

    如果您有一个复杂的菜单栏,需要根据许多不同的逻辑条件启用/禁用许多按钮或选项,那么状态模式是一种不错的处理方式。我构建了一个企业级应用程序,在顶部有一个“类似 Word”的按钮栏......并且有很多不同的条件会影响按钮的状态,我不得不将逻辑集中在一个地方。起初我没有使用状态模式,维护代码是一件很困难的事。有一天,我硬着头皮把所有的条件逻辑重构到一个 StateManager 类中。从那时起,它绝对让生活变得更轻松。

    【讨论】:

    • 嗨,布莱恩,感谢您的回答。 Mate 听起来很有趣——对于这个项目,尽管我可能会坚持使用 State 模式。但是,您是如何设法摆脱状态管理器中的 Application.application 引用(我创建了一个 Singleton 类)?我的 Menus 和 ViewStack 在 Application 文件中,状态管理器经常引用它们。谢谢,马丁
    【解决方案2】:

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

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

    这对你来说清楚了吗?如果没有,也许我可以花一两个小时来制作一个小样本。

    告诉我,

    =布莱恩=

    【讨论】:

      【解决方案3】:

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

      对于页面初始化,我遇到的问题是,一旦您导航到页面,是否应该显示某些元素并不总是知道,因为它不仅取决于整体用户权限,还取决于当前选择的权限数据。如果必须从服务器加载确定这一点所需的信息,则在加载信息时不能按原样显示页面。因此,我们创建了一个名为 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 并将用户“发送”到适当的区域。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-19
        • 2014-12-05
        • 2019-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多