7

我有一个自定义ItemRenderer,它在 3 个面板中的每个面板中显示 5 个文本输入:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    height="300"
    width="800"
    creationComplete="onCreationComplete()"
>
    <!-- code-behind -->
    <mx:Script source="ChainListRenderer.mxml.as" />

    <mx:Label text="{data.title}" fontSize="25" fontWeight="bold" width="100%" textAlign="center" />
    <mx:HBox>
        <mx:Panel id="triggerPanel" title="Trigger" width="260">
            <mx:VBox id="tpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
                <mx:TextInput id="trigger1" width="100%" textAlign="left" tabIndex="0" tabEnabled="true" />
                <mx:TextInput id="trigger2" width="100%" textAlign="left" tabIndex="1" tabEnabled="true" />
                <mx:TextInput id="trigger3" width="100%" textAlign="left" tabIndex="2" tabEnabled="true" />
                <mx:TextInput id="trigger4" width="100%" textAlign="left" tabIndex="3" tabEnabled="true" />
                <mx:TextInput id="trigger5" width="100%" textAlign="left" tabIndex="4" tabEnabled="true" />
            </mx:VBox>
        </mx:Panel>
        <mx:Panel id="linkPanel" title="Link" width="260">
            <mx:VBox id="lpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
                <mx:TextInput id="link1" width="100%" textAlign="left" tabIndex="5" tabEnabled="true" />
                <mx:TextInput id="link2" width="100%" textAlign="left" tabIndex="6" tabEnabled="true" />
                <mx:TextInput id="link3" width="100%" textAlign="left" tabIndex="7" tabEnabled="true" />
                <mx:TextInput id="link4" width="100%" textAlign="left" tabIndex="8" tabEnabled="true" />
                <mx:TextInput id="link5" width="100%" textAlign="left" tabIndex="9" tabEnabled="true" />
            </mx:VBox>
        </mx:Panel>
        <mx:Panel id="answerPanel" title="Answer" width="260">
            <mx:VBox id="apBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
                <mx:TextInput id="answer1" width="100%" textAlign="left" tabIndex="10" tabEnabled="true" />
                <mx:TextInput id="answer2" width="100%" textAlign="left" tabIndex="11" tabEnabled="true" />
                <mx:TextInput id="answer3" width="100%" textAlign="left" tabIndex="12" tabEnabled="true" />
                <mx:TextInput id="answer4" width="100%" textAlign="left" tabIndex="13" tabEnabled="true" />
                <mx:TextInput id="answer5" width="100%" textAlign="left" tabIndex="14" tabEnabled="true" />
            </mx:VBox>
        </mx:Panel>
    </mx:HBox>
</mx:VBox>

不幸的是,当用作 ItemRenderer 时,文本输入之间的制表符不起作用,即使使用上面的 tabIndex 值也是如此。如果我将此代码复制到它自己的 MXML 应用程序中,则文本输入之间的制表符按预期工作。

有谁知道如何在这种情况下恢复制表符?如果我不得不在没有这么简单的可用性元素的情况下发布这个应用程序,那将是一种耻辱。

我想我可能需要实施mx.managers.IFocusManagerComponent,但我找不到任何关于如何做到这一点的例子,而且FocusManager 文档也没有帮助。

4

9 回答 9

3

我将 mx:VBox 用作我的数据网格的 rendererIsEditor="true" 的自定义 itemRenderer,并且我也遇到了选项卡顺序问题。

我发现 itemRenderer 需要实现 IFocusManagerComponent 才能使主应用程序的 FocusManager() 能够正确地对其进行制表符。我尝试实现该接口:

<?xml version="1.0"?>
<mx:VBox implements="mx.managers.IFocusManagerComponent" ...>
 [the rest of my itemRenderer code]
</mx:VBox>

...结果证明做起来相当复杂...很多接口函数要实现。

但是,就我而言,我很幸运;我的 itemRenderer 中只有一个 TextInput 元素(其余的都是自定义代码、验证器和格式化程序),所以我将 itemRenderer 从 mx:VBox 转换为 mx:TextInput(它已经实现了 IFocusManagerComponent):

<?xml version="1.0"?>
<mx:TextInput ...>
 [the rest of my itemRenderer code]
</mx:TextInput>

瞧!我的标签顺序问题已修复。

我想对于那些拥有更复杂 itemRenderers 的人的结论是,你需要在你的类中完全实现 IFocusManagerComponent 接口......这可能很好,因为它看起来会告诉 flex 如何通过你的 itemRenderer 自定义选项卡字段。或者,也许您可​​以将顶级标签更改为已经实现接口的东西,例如:您可以将 mx:VBox 嵌套在类似的东西中:

<mx:Container focusIn="FocusManager.setFocus(trigger1)">

......也许它可以工作?代码比我更复杂的人应该尝试一下,看看会发生什么。

于 2009-09-03T00:04:14.627 回答
3

我在“ListBase 派生”组件中使用的 itemRender 遇到了同样的问题。我发现所有“ListBase 派生”组件都将所有项目渲染器包装在 ListBaseContentHolder 中。

从 ListBase 来源:

/**
 *  An internal display object that parents all of the item renderers,
 *  selection and highlighting indicators and other supporting graphics.
 *  This is roughly equivalent to the <code>contentPane</code> in the 
 *  Container class, and is used for managing scrolling.
 */
protected var listContent:ListBaseContentHolder;

此类的tabChildrentabEnabled属性默认设置为 false。我能找到的唯一解决方法是创建一个从 List 派生的 MyList 组件,并以这种方式覆盖 createChildren 方法(在其中初始化 listContent):

import flash.display.DisplayObject;
import mx.controls.List;

public class MyList extends List {
    override protected function createChildren():void {
            super.createChildren();
            this.listContent.tabChildren = this.tabChildren
            this.listContent.tabEnabled = this.tabEnabled
        }
    }

然后使用“<MyList tabChildren="true"/>”而不是“<mx:List/>”组件让我恢复了ItemRender 中的选项卡功能。

希望能帮助到你,

于 2010-03-11T12:34:25.423 回答
0

我想我可能正朝着正确的方向前进,但我还没有完全做到。

我有我的主应用程序,其中 Horizo​​ntalList 使用自定义 ItemRenderer:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:com="ventures.view.component.*"
    layout="absolute"
    backgroundColor="#ffffff"
    preinitialize="onPreInitialize()"
    click="onClick(event)"
>
    <!-- code-behind -->
    <mx:Script source="ConsumptionChain.as" />

    <!-- show chain links -->
    <mx:HorizontalList
        id="ChainList"
        direction="horizontal"
        dataProvider="{chainData}"
        rollOverColor="#ffffff"
        selectionColor="#ffffff"
        horizontalScrollPolicy="off"
        left="20"
        right="20"
        top="20"
        height="300"
        minWidth="802"
        rowHeight="300"
        columnWidth="800"
        tabChildren="false"
        itemRenderer="ventures.view.ItemRenderer.ChainListRenderer"
    />

</mx:Application>

我已更新原始问题中的代码示例以包含整个 ItemRenderer MXML(适用部分);这是 ActionScript 代码隐藏:

/*
 * ChainListRenderer.mxml.as -- code-behind for ChainListRenderer.mxml
 */

import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;

//used to combine all textboxes in a single array to make looping through them with the TAB key easier
private var allBoxes:Array;

public function expandPanel(e:Event):void {
    trace(e.currentTarget);                
    var state : String = e.currentTarget.parent.parent.id;                
    if (state != this.currentState)
        this.currentState = state;
}

private function onCreationComplete():void{
    //this function will be run on each object via the map function
    function forEach(o:Object, index:int, ar:Array):void{
        o.addEventListener(FocusEvent.FOCUS_IN, expandPanel)
        o.addEventListener(MouseEvent.CLICK, stopBubble);           //don't propagate click events (which return to base state)
        o.addEventListener(KeyboardEvent.KEY_DOWN, customTabbing);  //fix tabbing between text fields
    }

    this.addEventListener(KeyboardEvent.KEY_DOWN, customTabbing);

    //loop over textboxes and add the focusIn event listener
    tpBoxes.getChildren().map(forEach);
    lpBoxes.getChildren().map(forEach);
    apBoxes.getChildren().map(forEach);

    //create an "allBoxes" array that is used by the customTabbing function
    allBoxes = tpBoxes.getChildren();
    function forEachTabbing(o:Object, index:int, ar:Array):void {
        allBoxes.splice(allBoxes.length, 0, o);
    }
    lpBoxes.getChildren().map(forEachTabbing);
    apBoxes.getChildren().map(forEachTabbing);
}

//this function is used to prevent event bubbling
private function stopBubble(e:Event):void {
    e.stopPropagation();
}

//this function re-implements tabbing between text fields, which is broken when inside an itemRenderer
public function customTabbing(e:KeyboardEvent):void {
    trace('keyCode: ' && e.keyCode.toString());
    if (e.keyCode == flash.ui.Keyboard.TAB){
        trace(focusManager.getFocus());
        //loop over array of all text-boxes
        for (var i:int = 0; i < allBoxes.length; i++){
            trace(i.toString());
            //if event (keypress) current target is the current TextInput from the array
            if (e.currentTarget == allBoxes[i]){
                //then focus the NEXT TextInput, and wrap if at last one
                allBoxes[((i+1) % allBoxes.length)].setFocus();
                //break out of the loop
                break;
            }
        }

        //prevent arrow keys from navigating the horizontal list
        e.stopPropagation();
    }
}

本质上,我在这里尝试做的是通过检查每个文本字段的 key_down 事件上的 TAB 键来重新实现制表符,并使用它将焦点移动到下一个 TextInput(从最后一个 TextInput 包装到第一个 TextInput)。但这并没有达到预期的效果,并且焦点仍然跳到此 Horizo​​ntalList 之外的下一个控件。

有什么想法可以从这里开始吗?

于 2009-05-19T20:01:08.760 回答
0

你为什么要做 tabChildren="false"?您不想标记 HorziontalList 的子项吗?因为 itemRenderer 是列表的子项....

于 2009-06-03T18:22:56.483 回答
0

这对于我使用的方法似乎是不可能的。我没有使用带有自定义项目渲染器的列表,而是切换到单项目视图组件和一个单独的列表来显示所有项目的摘要,这让我解决了我的问题。

于 2009-08-24T13:04:06.297 回答
0

我有同样的问题,通过尝试重新实现选项卡按钮的行为来解决它。成功的线索只是使用event.preventdefault()方法。使用的代码显示在前面。

private function initFocusMap():void {
    focusMap = {
        InNom:benefPersona.InApePat,
        InApePat:benefPersona.InApeMat,
        InApeMat:benefPersona.InFecNacimiento,
        InFecNacimiento:benefPersona.InRFC,
        InRFC:benefPersona.InCURP,
        InCURP:benefPersona.InIdSexo,
        InIdSexo:benefPersona.InIdParentesco,
        InIdParentesco:benefPersona.InPorc,
        InPorc:domBeneficiario.InCalle,
        InCalle:domBeneficiario.InNumExterior,
        InNumExterior:domBeneficiario.InNumInterior,
        InNumInterior:domBeneficiario.InCP,
        InCP:domBeneficiario.InColonia,
        InColonia:domBeneficiario.InIdPais,
        InIdPais:domBeneficiario.InIdEdo,
        InIdEdo:domBeneficiario.InIdCiudad,
        InIdCiudad:benefPersona.InNom                   
    }
}

private function kfcHandler(event:FocusEvent):void {
    var id:String = ""
    if (event.target is AperClsDateField || event.target is AperClsCombo) {
        id = event.target.id;
    } else {
        id = event.target.parent.id;
    }
    if (id != "InIdDelegacionMunic") {
        event.preventDefault();             
        focusManager.setFocus(focusMap[id]);
    }
}
于 2009-12-27T01:30:33.910 回答
0

这是使用 ComboBox itemEditor 的方式:

http://blog.flexmonkeypatches.com/2008/02/18/simple-datagrid-combobox-as-item-editor-example/

于 2010-05-28T18:05:52.613 回答
0

我在我的 AdvancedDataGrid 中选择一个 itemRenderer 时遇到了同样的问题(顺便说一句,我使用的是 Flex SDK 3.5),但是这篇文章非常有助于我制作标签友好的 itemRenderer,所以我想做也有贡献:)

要使其工作,您还需要更改网格和 gridColumn 上的一些属性。

我们先来谈谈grid和gridColumn。

众所周知,当您将网格的“可编辑”属性设置为“真”时,您可以在每个列单元格中使用标签(假设您没有将列的“可编辑”属性设置为“假”)。

第 1 步,将网格的“可编辑”属性设置为“真”

步骤 #2,使您的网格的列“可编辑”属性也设置为“真”和“rendererIsEditor”为“真”

将 dataField 设置为虚假字段很重要,因为由于我们将渲染器设置为编辑器,这意味着您在 itemRenderer 中更新的任何内容都将分配给 dataField,即您将 dataField 设置为具有 int 类型的“Foo”并且您没有- 填充组合框 itemRenderer 的原始对象。当您进行选择时,该对象将被分配给“Foo”

步骤 #3,使您的网格列“dataField”属性也设置为虚假字段。

现在让我们做一些让选项卡在 itemRenderer 上工作的事情

我知道这不是优化版本,但这将适用于第一次通过。

import mx.core.mx_internal;
import mx.managers.IFocusManagerComponent;
use namespace mx_internal;

public class FriendlyItemRendererContainer extends HBox implements IFocusManagerComponent
{

    public function FriendlyItemRendererContainer ()
    {
    super();
    addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);       
    }

    private var _listData:DataGridListData;
    //This is required to make it work as itemEditor
    public var text:String;

    private function keyFocusChangeHandler(event:FocusEvent):void
    {
            if (event.keyCode == Keyboard.TAB &&
                ! event.isDefaultPrevented() &&
                findNextChildToFocus(event.shiftKey))
            {

                event.preventDefault();

            }

    }

    private function findNextChildToFocus(shiftKey:Boolean):Boolean
    {
          var myChildrenAry:Array = getChildren();
      var incr:int = shiftKey ? -1 : 1;
      var index:int = shiftKey ? myChildrenAry.length : 0;
      var focusChildIndex:int = 0;
      var found:Boolean = false;

         for (focusChildIndex = 0; focusChildIndex < myChildrenAry.length; ++focusChildIndex)
     {
        if (!(myChildrenAry[focusChildIndex] as UIComponent).visible ||
            (myChildrenAry[focusChildIndex] is Container))
        {
            //If it's an invisible UIComponent or a container then just continue
            continue;
        }

            if (myChildrenAry[focusChildIndex] is TextInput)
        {
                    if (systemManager.stage.focus == (myChildrenAry[focusChildIndex] as TextInput).getTextField())
                    {
                        (myChildrenAry[focusChildIndex] as UIComponent).drawFocus(false);
                        found = true;
                        break;
                    }
        }
        else
        {
                    if (systemManager.stage.focus == myChildrenAry[focusChildIndex])
                    {
                        (myChildrenAry[focusChildIndex] as UIComponent).drawFocus(false);
                        found = true;
                        break;
                    }
       }
         }

         if (!found)
     {
        focusChildIndex = 0;
      }

      while (true)
      {
                focusChildIndex = focusChildIndex + incr;

                if ((focusChildIndex < 0) || (focusChildIndex >= myChildrenAry.length))
                {
                    UIComponentGlobals.nextFocusObject = null;
                    return false;
                }
                else
                if (myChildrenAry[focusChildIndex] is UIComponent)
                {
                (myChildrenAry[focusChildIndex] as UIComponent).setFocus();
                (myChildrenAry[focusChildIndex] as UIComponent).drawFocus(true);

                    break;
                }
        }


        return true;
    }

    override public function setFocus():void
    {
       var myChildrenAry:Array = getChildren();
       if (myChildrenAry.length > 0)
       {
        for (var i:int = 0; i < myChildrenAry.length; ++i)
        {
            if ((myChildrenAry[i] is UIComponent) && (myChildrenAry[i] as UIComponent).visible)
            {
               (myChildrenAry[i] as UIComponent).setFocus();
                   (myChildrenAry[i] as UIComponent).drawFocus(true);
               break;
            }
       }
    }

    }

    public function get listData():BaseListData
    {
            return _listData;
    }

    public function set listData(value:BaseListData):void
    {
            _listData = DataGridListData(value);
    }  
}

有关如何在 itemRenderer 上使用它的示例:

<FriendlyItemRendererContainer xmlns:mx="http://www.adobe.com/2006/mxml">

<mx:TextInput/>
<mx:Button label="Boo"/>
<mx:RadioButton/>
<<mx:TextInput/>
<mx:Button label="Baa"/>

</FriendlyItemRendererContainer>

然后把它放在 gridColumn 中就可以了。

享受。

于 2011-04-27T15:43:11.427 回答
0

基本上你想删除焦点更改事件的默认行为。我认为你需要这样做:

1. yourRenderer.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, onKeyFocusChange);
2. since you want to stop tab key, in the handler, do this: 
        if (event.keyCode == Keyboard.TAB)
            event.preventDefault()
3. in your keyDown handler, catch TAB, then you can manually move your focus.
于 2011-11-15T14:45:23.750 回答