2009-05-18 9 views
7

ho un personalizzato itemRenderer che visualizza 5 ingressi testo in ciascuno dei 3 pannelli:Flex itemRenderer impedisce l'uso di tabulazione tra ingressi testo

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

Purtroppo, se usati come itemRenderer, tabulazione tra gli ingressi di testo non funziona, anche con i valori tabIndex sopra. Se copio questo codice in un'applicazione MXML, la tabulazione tra gli input di testo funziona come previsto.

Qualcuno sa come ripristinare la tabulazione in questo scenario? Sarebbe un peccato se dovessi rilasciare questa app senza un elemento di usabilità così semplice.

Suppongo che potrebbe essere necessario implementare mx.managers.IFocusManagerComponent, ma non riesco a trovare alcun esempio su come farlo, e il FocusManager docs non aiuta neanche.

+0

Hai provato a impostare tabEnabled sul textInputs? – quoo

+0

L'ho provato proprio ora, non sembra essere d'aiuto. –

+0

Quando dici "l'indicizzazione delle schede non funziona" quale comportamento stai vedendo? Passa al controllo successivo dopo il componente basato su List o si trova all'interno del browser chrome? –

risposta

3

Stavo usando un mx: VBox come oggetto personalizzatoRenderer con rendererIsEditor = "true" per il mio datagrid, e stavo correndo anche nel problema dell'ordine di tabulazioni.

Quello che ho capito è che itemRenderer deve implementare IFocusManagerComponent in modo che FocusManager() dell'applicazione principale sia in grado di collegarsi correttamente ad esso. Ho provato ad implementare quell'interfaccia:

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

... e risulta essere piuttosto complesso da fare ... molte funzioni di interfaccia da implementare.

Tuttavia nel mio caso sono stato fortunato; Ho avuto solo un elemento TextInput nel mio itemRenderer (il resto era tutto il codice appena personalizzato, validatori & formattatori), così ho trasformato il mio itemRenderer da mx: VBox a mx: TextInput (che implementa già l'IFocusManagerComponent):

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

Ecco! Il problema con l'ordine di tabulazione è stato risolto.

Suppongo che la conclusione per quelli di voi con itemRenderers più complessi è che sarà necessario implementare completamente l'interfaccia IFocusManagerComponent nella classe ... che è probabilmente buona perché sembra che direbbe a flex come personalizzare passare attraverso i campi itemRenderer. O forse si potrebbe cambiare il tag di livello superiore a qualcosa che implementa l'interfaccia già, ad esempio: potresti nido mx: VBox dentro qualcosa di simile:

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

... e farlo funzionare, forse? Qualcuno con un codice più complesso di me dovrebbe fare un tentativo e vedere cosa succede.

+1

Ciao. Per quello che vale, UIComponent implementa tutti i metodi richiesti di IFocusManagerComponent, ma non solo l'interfaccia (perché alcuni UIComponents non dovrebbero focalizzarsi). Quindi, per rendere un componente attivo, basta aggiungere la dichiarazione dell'interfaccia (non è necessario implementare nessuno dei metodi - UIComponent lo fa per te. Http://www.adobe.com/livedocs/flex/201/langref/ mx/manager/IFocusManagerComponent.html –

+2

Sinc l'URL fornito nel commento precedente è rotto, ecco un URL alternativo/aggiornato: [IFocusManagerComponent] (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/ MX/manager/IFocusManagerComponent.html). – jweyrich

0

Penso che mi stia muovendo nella giusta direzione, ma non sono ancora del tutto arrivato.

ho il mio principale, con il HorizontalList utilizzando un itemRenderer personalizzato:

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

Ho aggiornato il codice di esempio nella domanda iniziale per contenere l'intero itemRenderer MXML (parti applicabili); ed ecco il code-behind 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(); 
    } 
} 

In sostanza, quello che sto cercando di fare qui è re-implementare tabulazione controllando il tasto TAB sulla manifestazione KEY_DOWN di ogni campo di testo, e l'utilizzo che per spostare l'attivazione al successivo TextInput (avvolgimento del primo TextInput dall'ultimo). Tuttavia, questo non ha l'effetto desiderato e l'attenzione continua a passare al controllo successivo al di fuori di questa HorizontalList.

Qualche idea da dove partire?

0

Perché stai facendo tabChildren = "false"? Non vuoi dare un'occhiata ai bambini della HorziontalList? dato che itemRenderer è figlio della lista ....

+0

Non ero originariamente. E 'stato qualcosa che ho provato per disperazione, e non sembrava avere alcun effetto, quindi è stato lasciato in. Ho abbandonato l'intero HorizontalList con un approccio itemRenderer personalizzato, però, e appena andato per un componente personalizzato, che ha tabulazioni abilitate. Lascio la domanda in modo che altri che si imbattono nello stesso problema riescano almeno a trovare quello che ho trovato finora. –

0

Questo non sembra essere possibile con il metodo che stavo usando. Invece di usare una lista con un Renderizzatore di oggetti personalizzato, passai a un componente di visualizzazione a singola voce e a un elenco separato per mostrare un riepilogo di tutti gli elementi, e questo mi permise di risolvere il mio problema.

0

Ho avuto lo stesso problema, l'ho risolto cercando di reimplementare il comportamento del pulsante di tabulazione. L'indizio del successo sta semplicemente usando il metodo event.preventdefault(). Il codice utilizzato è mostrato più avanti.

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

Ho riscontrato lo stesso problema con un oggettoRender utilizzato in un componente "derivato da ListBase". Ho trovato che tutti i componenti "derivati ​​da ListBase" contengono tutti i renderer degli elementi in ListBaseContentHolder.

Dalla fonte 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; 

I tabChildren e tabEnabled proprietà di questa classe sono impostati su false per impostazione predefinita. L'unica soluzione che ho trovato è stato quello di creare un componente MyList derivante dalla lista e l'override del metodo createChildren (dove il listContent viene inizializzato) in questo modo:

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

Quindi, utilizzando "< MyList tabChildren =" true "/ > "invece del componente" < mx: List/> "mi ha restituito la funzionalità di tabulazione nell'oggetto ItemRender.

Speranza che aiuta,

0

ho avuto gli stessi problemi di tabulazione uno dei itemRenderer nel mio AdvancedDataGrid (tra l'altro , Sto usando Flex SDK 3.5) ma questo post è stato estremamente utile per permettermi di creare il mio item -ender per la tabulazione, quindi vorrei dare un contributo :)

Per far funzionare tutto questo, voi o è necessario modificare alcune proprietà sulla griglia e gridColumn.

Iniziamo parlando della griglia e della griglia della colonna.

Come tutti sapete quando si imposta la proprietà "modificabile" della griglia su "true", è possibile passare da una cella di colonna all'altra (presupponendo che non sia stata impostata la proprietà "modificabile" della colonna su "falso").

Passo # 1, rendono proprietà "modificabile" del vostro griglia imposta su "true"

Passo # 2, rendere la colonna di griglia "modificabile" proprietà imposta anche su "true" e "rendererIsEditor" "true"

E 'importante impostare il dataField a un campo fasullo perché da quando abbiamo impostato il renderer come editore che significa tutto ciò che si aggiorna in itemRenderer verrà assegnato al dataField cioè si imposta la dataField di "Pippo", che ha un tipo int e tu hai oggetti non primitivi che popolano un oggetto itemRenderer. Quando si effettua una selezione, quell'oggetto sarà assegnato a "Pippo"

Passo # 3, rendono colonna di proprietà di griglia "dataField" imposta anche a un campo fasullo.

Ora facciamo la cosa che consentono di tabulazione di lavorare su itemRenderer

So che questa non è una versione ottimizzata, ma questo farà per il 1 ° passaggio.

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

esempio su come utilizzare sul vostro 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> 

Poi appena messo che nel gridColumn e il gioco è fatto.

Divertiti.

0

In pratica si desidera rimuovere il comportamento predefinito dell'evento di modifica della messa a fuoco. Penso che devi fare questo:

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.