2009-05-18 1 views
7

У меня есть пользовательский ItemRenderer, который отображает 5 текстовых входов в каждой из 3-х панелей:Flex ItemRenderer предотвращает использование в обходе между текстовыми входами

<?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 docs не помогают.

+0

Вы пытались установить tabEnabled на textInputs? – quoo

+0

Я попробовал это сейчас, похоже, это не помогает. –

+0

Когда вы говорите, что индексирование вкладок не работает, какое поведение вы видите? Переходит ли он к следующему элементу управления после компонента на основе списка или он перемещается вокруг браузера хром? –

ответ

3

Я использовал mx: VBox как пользовательский itemRenderer с rendererIsEditor = "true" для моего datagrid, и я также столкнулся с проблемой порядка вкладок.

Что я понял, так это то, что itemRenderer должен реализовать IFocusManagerComponent для того, чтобы FocusManager() основного приложения мог правильно выполнить вкладку. Я пробовал реализовать этот интерфейс:

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

... и это оказалось довольно сложным ... множество функций интерфейса для реализации.

Однако в моем случае мне повезло; У меня был только один элемент TextInput в моей ItemRenderer (остальное было только пользовательский код, валидаторы & форматтеры), таким образом я преобразовал мою ItemRenderer из МХ VBox Мх: TextInput (который уже реализует IFocusManagerComponent):

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

Вуаля! Исправлена ​​проблема с заданием на вкладку.

Я полагаю, что заключение для тех из вас, у кого сложнее itemRenderers, вам нужно либо полностью реализовать интерфейс IFocusManagerComponent в вашем классе ... что, вероятно, хорошо, потому что похоже, что он подскажет, вкладку через поля itemRenderer. Или, возможно, вы могли бы изменить верхнюю метку уровня к чему-то уже реализует интерфейс, например: могли бы вы гнездятся на тх: VBox внутри что-то вроде:

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

... и это работает, возможно? Кто-то с более сложным кодом, чем я должен дать ему попробовать и посмотреть, что произойдет.

+1

Привет. Для чего это стоит, UIComponent реализует все необходимые методы IFocusManagerComponent, но просто не интерфейс (потому что некоторые UIComponents не должны получать фокус). Таким образом, чтобы сделать компонент сфокусированным, вы просто добавляете декларацию интерфейса (нет необходимости внедрять какие-либо методы - UIComponent делает это для вас. Http://www.adobe.com/livedocs/flex/201/langref/ mx/managers/IFocusManagerComponent.html –

+2

Sinc URL, указанный в предыдущем комментарии, сломан, вот альтернативный/обновленный URL: [IFocusManagerComponent] (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/ MX/менеджеров/IFocusManagerComponent.html). – jweyrich

0

Я думаю, что я могу двигаться в правильном направлении, но я еще не совсем там.

У меня есть основное приложение, с HorizontalList помощью настраиваемого 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(); 
    } 
} 

По сути, то, что я пытаюсь сделать здесь повторно реализовать табулируя путем проверки ключа TAB на Key_Down случае каждого текстового поля, и с помощью, чтобы переместить фокус к следующему TextInput (обертывание к первому TextInput от последнего). Однако это не имеет желаемого эффекта, и фокус все же переходит к следующему элементу управления вне этого HorizontalList.

Любые идеи, куда идти отсюда?

0

Почему вы делаете tabChildren = "false"? Разве вы не хотите вводить детей в список HorziontalList? так как itemRenderer является дочерним из списка.

+0

Я изначально не был. Это было то, что я попробовал из отчаяния, и, похоже, не имел никакого эффекта, так что он остался. Я отказался от всего HorizontalList с помощью настраиваемого подхода itemRenderer, и просто пошел за пользовательским компонентом, который Включено. Я оставляю вопрос таким образом, чтобы другие, сталкивающиеся с одной проблемой, могли найти хотя бы то, что я нашел до сих пор. –

0

Это, похоже, не представляется возможным с помощью метода, который я использовал. Вместо использования списка с настраиваемым элементом Item Renderer я переключился на компонент представления одного элемента и отдельный список, чтобы показать сводку всех элементов, и это позволило мне исправить мою проблему.

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

Я столкнулся с той же проблемой с itemRender, который использовался в компоненте, полученном в ListBase. Я обнаружил, что все компоненты, созданные «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; 

В tabChildren и tabEnabled свойства этого класса устанавливаются в ложь по умолчанию. Единственный способ решения проблемы я мог бы найти было создать компонент 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 =» истинный "/ > »вместо компонента« < mx: List/> »вернул функцию tabbing functionnality в ItemRender.

Надеется, что это помогает,

0

У меня был те же проблемы обхода одного из ItemRenderer в моей AdvancedDataGrid (кстати , Я использую Flex SDK 3.5), но этот пост был чрезвычайно полезен, позволяя мне сделать свой удобный для вклада элементRenderer, поэтому я тоже хотел бы внести свой вклад :)

Чтобы заставить вас работать, вы также o необходимо изменить несколько свойств на сетке и gridColumn.

Давайте поговорим о сетке и gridColumn.

Как вы все знаете, когда вы установите свойство «editable» сетки на «true», вы можете пропустить каждую ячейку столбца (если вы не задали свойство «editable» столбца «false»).

Шаг # 1, сделать свойство «редактируемым» сеткой устанавливает «истина»

Шаг № 2, сделать столбец сетки в «редактируемое» свойство также устанавливает «истина» и «rendererIsEditor» to «true»

Важно установить значение DataField в фиктивное поле, поскольку, поскольку мы устанавливаем рендерер как редактор, это означает, что все, что вы обновляете в itemRenderer, будет присвоено DataField, то есть вы установите для DataField значение «Foo», которое имеет тип int, и у вас есть непримитивные объекты, заполняющие comboBox itemRenderer. Когда вы сделаете выделение, этот объект будет присвоен «Foo»

Шаг № 3, чтобы свойство столбца «dataField» вашей сетки также устанавливалось в фиктивное поле.

Теперь давайте делать то, что дают возможность табулируя работать на ItemRenderer

Я знаю, что это не оптимизированная версия, но это будет делать за 1 проход.

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 и это все.

Наслаждайтесь.

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. 
Смежные вопросы