2009-05-18 7 views
7

tengo una costumbre itemRenderer que muestra 5 entradas de texto en cada una de 3 paneles:Flex itemRenderer impide el uso de tabulación entre las entradas de texto

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

Desafortunadamente, cuando se usan como un itemRenderer, tabulación entre las entradas de texto no funciona, incluso con los valores tabIndex anteriores. Si copio este código en una aplicación MXML propia, tabular entre las entradas de texto funciona como se esperaba.

¿Alguien sabe cómo restaurar tabbing en este escenario? Sería una pena si tuviera que lanzar esta aplicación sin un elemento de usabilidad tan simple.

Supongo que puedo necesitar implementar mx.managers.IFocusManagerComponent, pero no puedo encontrar ningún ejemplo sobre cómo hacerlo, y FocusManager docs tampoco me ayudan.

+0

¿Intentó establecer tabEnabled en el textInputs? – quoo

+0

Lo intenté justo ahora, no parece ayudar. –

+0

Cuando dices que la "indexación de pestañas no funciona", ¿qué comportamiento estás viendo? ¿Salta al próximo control después de su componente basado en listas o está tabulando alrededor del navegador Chrome? –

Respuesta

3

Estaba usando un mx: VBox como itemRenderer personalizado con rendererIsEditor = "true" para mi cuadrícula de datos, y también me estaba encontrando con el problema del orden de las pestañas.

Lo que descubrí es que el itemRenderer necesita implementar IFocusManagerComponent para que el FocusManager() de la aplicación principal pueda tabularlo correctamente. He intentado implementar la interfaz:

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

... y resulta ser bastante complejo para hacerlo ... un montón de funciones de interfaz para la aplicación.

Sin embargo, en mi caso tuve suerte; Yo sólo tenía un elemento TextInput en mi itemRenderer (el resto era todo el código simplemente costumbre, validadores & formateadores) así que convertido mi itemRenderer de mx: caja vertical a MX: TextInput (que ya implementa el IFocusManagerComponent):

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

Voila! Mi problema de orden de tabulación fue corregido.

Supongo que la conclusión para aquellos de ustedes con itemRenderers más complejos es que necesitarán implementar completamente la interfaz IFocusManagerComponent en su clase ... lo cual es probablemente bueno porque parece que le dirá a flex cómo personalizar pestaña a través de sus campos ItemRenderer. O tal vez usted podría cambiar su etiqueta de nivel superior a algo que ya implementa la interfaz, por ejemplo: ¿Podría el nido mx: caja vertical dentro de algo como:

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

... para que funcionen tal vez? Alguien con código más complejo que yo debería probarlo y ver qué pasa.

+1

Hola. Por lo que vale, UIComponent implementa todos los métodos requeridos de IFocusManagerComponent, pero simplemente no la interfaz (porque se supone que algunos UIComponents no tienen foco). Entonces, para hacer que un componente sea enfocable, simplemente agrega la declaración de la interfaz (no es necesario implementar ninguno de los métodos, UIComponent lo hace por usted. Http://www.adobe.com/livedocs/flex/201/langref/ mx/managers/IFocusManagerComponent.html –

+2

Sinc la URL proporcionada en el comentario anterior no funciona, aquí hay una URL alternativa/actualizada: [IFocusManagerComponent] (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/ mx/managers/IFocusManagerComponent.html). – jweyrich

0

Creo que me estoy moviendo en la dirección correcta, pero todavía no estoy del todo allí.

tengo mi aplicación principal, con el uso de un HorizontalList itemRenderer personalizado:

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

He actualizado el ejemplo de código en la pregunta original para contener toda la itemRenderer MXML (porciones aplicables); Y aquí está el código subyacente de 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(); 
    } 
} 

En esencia, lo que estoy tratando de hacer aquí es volver a implementar la tabulación mediante la comprobación de la tecla TAB en el evento KEY_DOWN de cada campo de texto y el uso de esta técnica para mover el foco al siguiente TextInput (envolviendo el primer TextInput desde el último). Sin embargo, esto no tiene el efecto deseado y el foco salta al siguiente control fuera de esta HorizontalList.

¿Alguna idea de dónde ir desde aquí?

0

¿Por qué haces tabChildren = "false"? ¿No quieres tabular a los hijos de HorziontalList? ya que el itemRenderer es un elemento secundario de la lista ....

+0

No era originalmente. Fue algo que probé por desesperación, y no pareció tener ningún efecto, así que lo dejé. Sin embargo, abandoné toda la HorizontalList con un enfoque personalizado de itemRenderer, y me fui por un componente personalizado, que tiene pestañas habilitadas Dejaré la pregunta para que otros que se encuentren con el mismo problema puedan al menos encontrar lo que he encontrado hasta ahora. –

0

Esto simplemente no parece posible con el método que estaba usando. En lugar de utilizar una lista con un Renderer de elementos personalizado, cambié a un componente de vista de un solo elemento y una lista separada para mostrar un resumen de todos los elementos, y esto me permite solucionar mi problema.

0

Tuve el mismo problema, lo resolví intentando volver a aplicar el comportamiento del botón de tabulación. La clave del éxito es simplemente usar el método event.preventdefault(). El código usado se muestra adelante.

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

me encontré con el mismo problema con un itemRender utilizado en un componente "ListBase deriva". Encontré que todos los componentes "ListBase derived" envuelven todos los rendereres de elementos en ListBaseContentHolder.

Desde la fuente 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; 

Los tabChildren y tabEnabled propiedades de esta clase se establecen en false por defecto. La única solución que pude encontrar fue la creación de un componente Mi lista derivada de la Lista y reemplazar el método createChildren (donde se inicializa el listContent) de esta manera:

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

Luego, utilizando "< Mi lista tabChildren =" true "/ > "en lugar del componente" < mx: List/> "me devolvió la funcionalidad de tabulación en el ItemRender.

creo que sirve,

0

que estaba teniendo los mismos problemas de tabulación uno de los itemRenderer en mi AdvancedDataGrid (por cierto , Estoy usando Flex SDK 3.5) pero esta publicación ha sido extremadamente útil al permitirme hacer mi itemRenderer amigable con las pestañas así que me gustaría hacer una contribución también :)

Para que esto funcione, también o necesita cambiar algunas propiedades en la grilla y gridColumn.

Hablemos primero sobre la cuadrícula y gridColumn.

Como todos saben cuando estableces la propiedad "editable" de la cuadrícula en "verdadero", puedes desplazarte por cada celda de columna (asumiendo que no estableciste la propiedad "editable" de la columna en "false").

Paso # 1, hacer de la propiedad "editable" de su rejilla fija a "true"

Paso # 2, hacer que la columna de la cuadrícula "editable" propiedad también se establece en "true" y "rendererIsEditor" a "verdadero"

Es importante establecer el campo de datos en un campo falso porque desde que configuramos el representador como editor significa que cualquier cosa que actualices en el elemento se asignará al campo de datos, es decir, establecerás el campo de datos en "Foo" que tiene un tipo int y tiene objetos no primitivos que completan un comboBox itemRenderer. Cuando realiza una selección, ese objeto se asignará a "Foo"

Paso # 3, la propiedad "dataField" de la columna de la grilla también se establece en un campo falso.

Ahora vamos a hacer lo que permiten la tabulación para trabajar en itemRenderer

Sé que esto no es una versión optimizada pero esto va a hacer por la primera pasada.

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

Ejemplo de cómo utilizarlo en su 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> 

A continuación, sólo hay que poner que en el gridColumn y eso es todo.

Disfrútalo.

0

Básicamente, desea eliminar el comportamiento predeterminado del evento de cambio de enfoque. Creo que necesita hacer esto:

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. 
Cuestiones relacionadas