Scroller类中的Focus Manager错误

时间:2012-07-17 04:45:11

标签: flex flex4 flash-builder

在Scroller.as类第2139行中,我收到以下错误:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at spark.components::Scroller/focusInHandler()[E:\dev\4.y\frameworks\projects\spark\src\spark\components\Scroller.as:2139]
    at flash.display::Stage/set focus()

来自Scroller.as

/**
 *  @private 
 *  Listens for any focusIn events from descendants 
 */ 
override protected function focusInHandler(event:FocusEvent):void
{
    super.focusInHandler(event);

    // When we gain focus, make sure the focused element is visible
    if (viewport && ensureElementIsVisibleForSoftKeyboard)
    {
        var elt:IVisualElement = focusManager.getFocus() as IVisualElement; 
        lastFocusedElement = elt;
    }
}

由于这是框架代码,我有什么选择可以阻止它?

上下文
我创建了一个弹出的TitleWindow,在其中添加了一个Module并显示它。该模块有几个国家,每个州都有一个组,一个组有一个List,该List有一个ItemRenderer,该ItemRenderer有一个Checkbox。

该模块还有一个菜单。打开菜单时,弹出的菜单列出了Module可用的状态。当从弹出的菜单中选择一个项目时,我会切换到另一个状态。

当状态改变并且最后一项是复选框时,则生成错误。至少这就是我认为正在发生的事情。我推断出这是因为在Scroller类中,处理程序正在处理事件。在那个事件上是当前的目标。当前目标是复选框。

更新 - 重现的步骤

// inside the Application.mxml
// define variables
public var popup:Group;
public var titleWindow:TitleWindow;


// shows pop up
public function showInspector():void {

    // inside show inspector method
    // create new inspector container
    popup = new InspectorContainer(); // a group implements="mx.managers.IFocusManagerContainer"
    titleWindow = new TitleWindow();
    titleWindow.addElement(popup);

    // display pop up title window
    PopUpManager.addPopUp(titleWindow, this, false);
}

<fx:Declarations>
    <modules:InspectorContainer/>
</fx:Declarations>
  1. 显示弹出窗口。弹出窗口是一个标题窗口,其中InspectorContainer(一个组)作为第一个元素。

  2. 弹出从主页状态(默认状态)更改为在线状态(当用户单击按钮时会发生这种情况)。在线状态有一个列表。 List有一个itemrenderer。 itemrenderer有一个复选框。选中该复选框。到目前为止一切顺利。

  3. 弹出窗口(InspectorContainer)有一个mx:MenuBar实例。当您单击菜单栏中的项目时,菜单栏会显示菜单项。

  4. 单击菜单列表中的项目。调用itemClick menuHandler。在此功能中,弹出窗口会更改状态。

  5. 这是发生错误的时间。

3 个答案:

答案 0 :(得分:4)

错误的主要原因是UIComponent中的get函数“focusManager”:

public function get focusManager():IFocusManager
{
    if (_focusManager)
        return _focusManager;

    var o:DisplayObject = parent;

    while (o)
    {
        if (o is IFocusManagerContainer)
            return IFocusManagerContainer(o).focusManager;

        o = o.parent;
    }

    return null;
}

如下面的代码所示,你可以看到uicomponent第一次通过遍历来自其父,parent.parent和parent.parent.parent ...来获得它的focusmanager,但不幸的是,当PopUpManager.addPopUp(popUp)添加了uicomponent时这,FALSE);它的父级是SystemManager,它的parent.parent是Stage,它们要么不是IFocusManagerContainer,所以它们要么没有focusManager属性,所以结果为null。

最后,我的解决方案将覆盖直接使用组件,如果您需要修复2层组件,请将focusManager传递给它。

package cn.easymenu.view.components{
 import mx.core.FlexGlobals;
 import mx.managers.IFocusManager;

 import spark.components.TextArea;

 public class TextArea extends spark.components.TextArea{
  override protected function partAdded(partName:String, instance:Object):void{
   super.partAdded(partName, instance);
   if(instance == scroller){
    scroller.focusManager=this.focusManager;
   }
 }

 override public function get focusManager():IFocusManager{
  var ifm:IFocusManager=super.focusManager;
  if(!ifm){
   ifm = FlexGlobals.topLevelApplication.focusManager;
  }
  return ifm;
  }
 }
}

答案 1 :(得分:1)

使用PopUpManager时遇到了这个问题。

  1. 以下是Adobe bug report
  2. 这是另一个SO post about it
  3. 这是another post,可能有也可能没有关系(你没有提到导致这个问题的原因)。
  4. 在我的例子中,我在第一个和第二个链接上跟随了示例,并创建了一个Scroller的子类,它在适当的位置进行了空检查。这对我的特殊情况很好,并且应用程序没有经历任何不良影响。这就是说这个“修复”对我来说总是让人觉得有点黑客。

答案 2 :(得分:1)

这是完整的代码:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
            xmlns:s="library://ns.adobe.com/flex/spark" 
            xmlns:mx="library://ns.adobe.com/flex/mx" 
            autoDrawBackground="true"
            width="100%"
            implements="mx.managers.IFocusManagerContainer">

    <fx:Script>
    <![CDATA[
        public function get defaultButton():IFlexDisplayObject {
            return null;
        }

        public function set defaultButton(value:IFlexDisplayObject):void {
            // do nothing
        }

        override public function get systemManager():ISystemManager {
            return null;
        }
    ]]>
    </fx:Script>

    <!-- other stuff -->

    <s:CheckBox id="enabledCheckbox" />

</s:ItemRenderer>

解决方案是实现IFocusManagerContainer或基本上它所需的功能。在我的情况下,我不得不在Scroller focusInHandler处理程序中覆盖焦点事件的目标对象(组件)的容器中的systemManager getter。

我这样想出来了。 Scroller focusInHandler中发生错误。焦点事件(event.target)的目标属性是复选框(具有焦点的最后一个项目)。 Checkbox的容器是我的ItemRenderer。这是我实现IFocusManagerContainer接口的地方。我提到这个是因为沿着这条路径有5个或更多父级。

在ItemRenderer中我添加了:

override public function get systemManager():ISystemManager {
    return null;
}

此方法是在实现mx.managers.IFocusManagerContainer接口时必须实现的方法。

该接口还要求我定义defaultButton getter和setter,但没有必要解决这个错误。在测试中,我删除了接口,删除了defaultButton成员并离开了systemManager getter,只是它仍然解决了问题。

另外,感谢noobsarepeople2的帮助。