Focus Manager bug in Scroller class

雨燕双飞 提交于 2020-01-16 08:27:06

问题


In the Scroller.as class line 2139 I'm getting the following error:

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()

From 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;
    }
}

Since this is framework code what option do I have to prevent it?

Context
I have created a pop up TitleWindow, added a Module in it and displayed it. The Module has a few States, in each State is a Group, one Group has a List, that List has an ItemRenderer, that ItemRenderer has a Checkbox.

The Module also has a Menu. When the Menu is opened the menu pop up lists the states the Module has available. When an item is selected from the menu pop up I change to another state.

When the state is changed and the last item is the checkbox then the error is generated. At least that's what I think is happening. I deduced this because in the Scroller class the handler is handling an event. On that event is the current target. That current target is the checkbox.

Update - Steps to reproduce

// 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. Show pop up. The pop up is a Title Window with InspectorContainer (a group) as the first element.

  2. In pop up change from home state (default state) to online state (this happens when the user clicks a button). The online state has a List. The List has an itemrenderer. The itemrenderer has a checkbox. Select the checkbox. So far so good.

  3. The pop up (InspectorContainer) has a mx:MenuBar instance. When you click on an item in the menubar the menubar displays a menu items.

  4. Click an item in the menu list. The itemClick menuHandler is called. In this function the pop up changes state.

This is when the error occurs.


回答1:


The main reson of the error is the get function "focusManager" in UIComponent:

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;
}

As the code below, you can see the uicomponent get its focusmanager first time by traverse from its parent, parent.parent and parent.parent.parent...,but unfortunately when an uicomponent was added by PopUpManager.addPopUp(popUp,this,false); It's parent is SystemManager, and it's parent.parent is Stage, and they are either not IFocusManagerContainer, so they have either not focusManager property, therefore the result is null.

Finally my Solution is override the direct usage component, and if you need to fix a 2 layer component, pass the focusManager into it.

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;
  }
 }
}



回答2:


I ran into this issue when using PopUpManager.

  1. Here is the Adobe bug report on it.
  2. Here's another SO post about it.
  3. This is another post that may or may not be related (you didn't mention what led to this problem).

In my case I followed the example at the first and second links and made a subclass of Scroller that does a null check in the appropriate place. This was fine for my particular situation and the app hasn't experienced any ill effects as a result. That said this "fix" has always felt a tad hacky to me.




回答3:


Here's the full code:

<?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>

The solution was to implement the IFocusManagerContainer or basically the function it needed. In my case I had to override the systemManager getter in the container of the target object (component) of the focus event in the Scroller focusInHandler handler.

I figured it out this way. The error occurs in the Scroller focusInHandler. The target property of the focus event (event.target) is the Checkbox (the last item to have focus). The container of the Checkbox is my ItemRenderer. This is where I implemented the IFocusManagerContainer interface. I mention this because there are 5 or more parent levels along this path.

In the ItemRenderer I added:

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

This method is a method you have to implement when you implement the mx.managers.IFocusManagerContainer interface.

That interface also required me to define the defaultButton getter and setter but it was not necessary to solve this error. In tests I removed the interface, removed the defaultButton members and left the systemManager getter and with just that it still solved the problem.

Also, thanks to noobsarepeople2 for help.



来源:https://stackoverflow.com/questions/11516121/focus-manager-bug-in-scroller-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!