Flex ItemRenator禁止在文本输入之间使用制表符

u3r8eeie  于 2022-09-21  发布在  Apache
关注(0)|答案(9)|浏览(117)

我有一个自定义的项目呈现器,它在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>

不幸的是,当用作ItemReneller时,文本输入之间的Tab键不起作用,即使使用上面的tabIndex值也是如此。如果我将这段代码复制到它自己的MXML应用程序中,文本输入之间的跳转就会正常工作。

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

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

hkmswyz6

hkmswyz61#

我使用mx:vbox作为定制的itemRender,并为我的数据网格设置rendererIsEditor=“true”,而且我还遇到了Tab键顺序的问题。

我得出的结论是,itemRender需要实现IFocusManagerComponent,以便主应用程序的FocusManager()能够正确地跳转到它。我尝试实现该接口:

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

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

然而,在我的例子中,我很幸运;我的itemRenender中只有一个TextInput元素(其余的都是定制代码、验证器和格式化程序),所以我将itemRender从mx:vbox转换为mx:TextInput(它已经实现了IFocusManager组件):

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

瞧啊!我的Tab键顺序问题已修复。

我想对于那些使用更复杂的itemRenderers的人来说,结论是您需要在类中完全实现IFocusManagerComponent接口...这可能很好,因为它看起来将告诉Flex如何通过您的itemRenderer域定制制表符。或者,您可以将顶级标记更改为已经实现该接口的内容,例如:您是否可以将mx:vbox嵌套在如下内容中:

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

...也许能让它起作用?代码比我复杂的人应该试一试,看看会发生什么。

v09wglhw

v09wglhw2#

我在“ListBase派生”组件中使用的itemRender遇到了同样的问题。我发现所有“ListBase派生的”组件都将所有项呈现器 Package 在一个ListBaseContent Holder中。

从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。我能找到的唯一解决办法是创建一个从列表派生的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
        }
    }

然后使用“”而不是“<mx:list/>”组件将ItemRender中的制表符功能带回给我。

希望能有所帮助,

vx6bjr1n

vx6bjr1n3#

我认为我可能正在朝着正确的方向前进,但我还没有完全做到这一点。

我有我的主应用程序,其中HorizontalList使用了一个定制的ItemRender:

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

我已经更新了原始问题中的代码示例,以包含整个ItemReneller 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)。但这并没有达到预期的效果,焦点仍然跳到此HorizontalList之外的下一个控件。

你知道接下来该怎么做吗?

ghg1uchk

ghg1uchk4#

您为什么要做tabChildren=“False”?您不想给Horzion List的子项添加标签吗?由于项目呈现器是列表的子级...

qij5mzcb

qij5mzcb5#

用我使用的方法,这似乎是不可能的。我没有使用带有定制条目呈现器的列表,而是切换到单个条目视图组件和单独的列表来显示所有条目的摘要,这让我修复了我的问题。

czfnxgou

czfnxgou6#

我也遇到了同样的问题,通过尝试重新实现选项卡按钮的行为来解决它。成功的秘诀就是简单地使用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]);
    }
}
nhn9ugyo

nhn9ugyo7#

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

要实现这一点,您还需要更改网格和gridColumn的一些属性。

让我们先来谈谈网格和网格列。

大家都知道,当您将网格的“edable”属性设置为“true”时,您可以在每个列单元格之间使用Tab键(假设没有将列的“edable”属性设置为“False”)。

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

步骤2,使网格的“可编辑”属性也设置为“True”,并将“rendererIsEditor”设置为“True”

将datafield设置为伪字段很重要,因为我们将呈现器设置为编辑器,这意味着您在itemRenator中更新的任何内容都将分配给datafield,即,您将datafield设置为“foo”,其类型为int,并且您有非基元对象填充comboBox itemRender。当您进行选择时,该对象将被分配给“foo”

步骤3,将网格的列“datafield”属性也设置为伪字段。

现在,让我们来做一些事情,使Tab键能够在itemReneller上工作

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

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

关于如何在您的itemReneller上使用它的示例:

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

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

</FriendlyItemRendererContainer>

然后只需将其放入网格列中,就是这样。

好好享受吧。

cunj1qz1

cunj1qz18#

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

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.

相关问题