I need a control in Flex 3 that is like NumericStepper, but that can display arbitrary strings. Does this control exist? If not, what are your suggestions for creating it, or references you would recommend?
For convenience, I'm calling this a TextStepper. I want this as a compact way to display a list of string choices that a user can cycle through by clicking the up/down buttons. Compact means no drop-down or pop-up aspects of the control: the only way to change the selected index is to click the up/down button (which updates the text input value). Value cycling means that I really want to treat the underlying dataProvider as a circular buffer. So up/down clicks modify selectedIndex in modulo fashion.
The idea is to use valueFormatFunction:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:local="*">
<local:StringStepper horizontalCenter="0" verticalCenter="0" width="200">
<local:dataProvider>
<s:ArrayCollection>
<fx:String>Hello!</fx:String>
<fx:String>I love you.</fx:String>
<fx:String>Won't you tell me your name?</fx:String>
</s:ArrayCollection>
</local:dataProvider>
</local:StringStepper>
</s:Application>
Source for StringStepper:
package
{
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
import spark.components.NumericStepper;
public class StringStepper extends NumericStepper
{
public function StringStepper()
{
enabled = false;
valueFormatFunction = defaultValueFormatFunction;
valueParseFunction = defaultValueParseFunction;
}
private var _dataProvider:ArrayCollection;
public function get dataProvider():ArrayCollection
{
return _dataProvider;
}
public function set dataProvider(value:ArrayCollection):void
{
if (_dataProvider == value)
return;
if (_dataProvider)
_dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,
dataProvider_collectionChangeHandler);
_dataProvider = value;
commitDataProvider();
if (_dataProvider)
_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,
dataProvider_collectionChangeHandler);
}
/**
* Same event as for <code>value</code>.
*/
[Bindable("valueCommit")]
public function get selectedItem():Object
{
return _dataProvider && value <= _dataProvider.length - 1 ? _dataProvider[value] : null;
}
public function set selectedItem(value:Object):void
{
if (!_dataProvider)
return;
value = _dataProvider.getItemIndex(value);
}
private function defaultValueFormatFunction(value:Number):String
{
return _dataProvider && value <= _dataProvider.length - 1 ? _dataProvider[value] : String(value);
}
private function defaultValueParseFunction(value:String):Number
{
if (!_dataProvider)
return 0;
var n:int = _dataProvider.length;
for (var i:int = 0; i < n; i++)
{
var string:String = _dataProvider[i];
if (string == value)
return i;
}
return 0;
}
private function commitDataProvider():void
{
if (!_dataProvider)
{
minimum = 0;
maximum = 0;
enabled = false;
return;
}
enabled = true;
minimum = 0;
maximum = _dataProvider.length - 1;
}
private function dataProvider_collectionChangeHandler(event:CollectionEvent):void
{
commitDataProvider();
}
}
}
I created one of these (as an MXML comoponent) by overlaying a TextInput over a NumericStepper (absolutely positioned) so that the TextInput covered the input portion of the NumericStepper.
The dataProvider was an ArrayCollection of strings, and the value of the NumericStepper was used to access an index in the ArrayCollection.
The change event of the NumericStepper changed the text of the TextInput to whatever was at index n of the dataProvider. I gave the component an editable property, which set the TextInput to editable and inserted the new string into the dataProvider at the current index.
Related
I have a Flex application that behaves as described below
I want to fix two things
I want to make clear to the user that more info can be shown. Current "Profundidad" is only a label.
The element that is being shown below is not aligned to the one above it, since this is actually another component that is being shown when the state changes
I have tried using Accordion, but when you have a Accordion with a single element, that element is always visible so it can not be collapsed and expanded
What i want is the final result to look as close as posible to this
You can easily achieve this using states. First define two states: 'normal' and 'expanded'
<s:states>
<s:State name="normal"/>
<s:State name="expanded"/>
</s:states>
Then use includeIn or excludeFrom to display components as needed:
<!-- always visible components -->
<s:Label text="FPBAF20F"/>
<s:Label text="N/A"/>
<s:Button label="Profundidad"
click="currentState = currentState == 'expanded' ? 'normal' : 'expanded'"/>
<!-- the compra/venta grid only shown in 'expanded' state -->
<s:DataGrid includeIn="expanded" />
The click handler isn't very pretty, but you get the idea: when you click the Button, the state is toggled from 'normal' to 'expanded' and back when you click again.
That's really all there is to it.
If you prefer an out-of-the-box solution, you're free to use the CollapsiblePanel component I created: https://github.com/RIAstar/CollapsiblePanelFx ;)
Maybe this code will be useful:
MXML file
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:classes="com.classes.*">
<classes:CollapsiblePanel id="cp" title="Profundidad (Click me)" open="false">
<mx:DataGrid>
<mx:ArrayList>
<fx:Object Cantidad="" Precio=""/>
</mx:ArrayList>
</mx:DataGrid>
</classes:CollapsiblePanel>
</s:Application>
CollapsiblePanel.as. Note that in my case, this class is in com.classes package, you can put this in the place as you want.
package com.classes {
import flash.events.*;
import mx.effects.AnimateProperty;
import mx.events.*;
import mx.containers.Panel;
import mx.core.ScrollPolicy;
[Style(name="closedIcon", property="closedIcon", type="Object")]
[Style(name="openIcon", property="openIcon", type="Object")]
public class CollapsiblePanel extends Panel {
private var _creationComplete:Boolean = false;
private var _open:Boolean = true;
private var _openAnim:AnimateProperty;
public function CollapsiblePanel(aOpen:Boolean = true):void
{
super();
open = aOpen;
this.addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
}
private function creationCompleteHandler(event:FlexEvent):void
{
this.horizontalScrollPolicy = ScrollPolicy.OFF;
this.verticalScrollPolicy = ScrollPolicy.OFF;
_openAnim = new AnimateProperty(this);
_openAnim.duration = 300;
_openAnim.property = "height";
titleBar.addEventListener(MouseEvent.CLICK, headerClickHandler);
_creationComplete = true;
}
private function headerClickHandler(event:MouseEvent):void { toggleOpen(); }
private function callUpdateOpenOnCreationComplete(event:FlexEvent):void { updateOpen(); }
private function updateOpen():void
{
if (!_open) height = closedHeight;
else height = openHeight;
setTitleIcon();
}
private function get openHeight():Number {
return measuredHeight;
}
private function get closedHeight():Number {
var hh:Number = getStyle("headerHeight");
if (hh <= 0 || isNaN(hh)) hh = titleBar.height;
return hh;
}
private function setTitleIcon():void
{
if (!_open) this.titleIcon = getStyle("closedIcon");
else this.titleIcon = getStyle("openIcon");
}
public function toggleOpen():void
{
if (_creationComplete && !_openAnim.isPlaying) {
_openAnim.fromValue = _openAnim.target.height;
if (!_open) {
_openAnim.toValue = openHeight;
_open = true;
dispatchEvent(new Event(Event.OPEN));
}else{
_openAnim.toValue = _openAnim.target.closedHeight;
_open = false;
dispatchEvent(new Event(Event.CLOSE));
}
setTitleIcon();
_openAnim.play();
}
}
public function get open():Boolean {
return _open;
}
public function set open(aValue:Boolean):void {
_open = aValue;
if (_creationComplete) updateOpen();
else this.addEventListener(FlexEvent.CREATION_COMPLETE, callUpdateOpenOnCreationComplete, false, 0, true);
}
override public function invalidateSize():void {
super.invalidateSize();
if (_creationComplete)
if (_open && !_openAnim.isPlaying) this.height = openHeight;
}
}
}
Yon can see this SWF in action here.
The source code of this collapsible panel is here.
I hope this will be help you.
I've created a simple Callout with a List in it.
Like this:
<s:Callout xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoLayout="true" >
<fx:Declarations>
<!-- Platzieren Sie nichtvisuelle Elemente (z. B. Dienste, Wertobjekte) hier -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.skill.flextensions.factories.StyledClassFactory;
import mx.collections.IList;
import spark.components.IconItemRenderer;
import spark.components.List;
import spark.events.IndexChangeEvent;
import spark.layouts.HorizontalAlign;
import spark.layouts.VerticalLayout;
private var _list:List;
//
// PUBLIC PROPERTIES
//
private var _dataProvider:IList;
private var _dataProviderChanged:Boolean = false;
public function get dataProvider():IList
{
return _dataProvider;
}
public function set dataProvider(value:IList):void
{
_dataProvider = value;
_dataProviderChanged = true;
this.invalidateProperties();
}
private var _itemRenderer:IFactory;
private var _itemRendererChanged:Boolean = false;
public function get itemRenderer():IFactory
{
return _itemRenderer;
}
public function set itemRenderer(value:IFactory):void
{
_itemRenderer = value;
_itemRendererChanged = true;
this.invalidateProperties();
}
//
// # SUPER
//
override protected function commitProperties():void
{
super.commitProperties();
if(_dataProviderChanged)
{
_dataProviderChanged = false;
_list.dataProvider = _dataProvider;
// TODO
// we have to remeasure, after dataprovider updated
// unfortunately, this doesn't work:
/*
_list.invalidateSize();
_list.invalidateDisplayList();
_list.validateNow();
invalidateSize();
invalidateDisplayList();
validateNow();
*/
// so we will have to find a workaround for this situation.
}
if(_itemRendererChanged)
{
_itemRendererChanged= false;
_list.itemRenderer = getItemRenderer();
}
}
override protected function createChildren():void
{
_list = new List;
_list.top = _list.bottom = 0;
_list.itemRenderer = getItemRenderer();
_list.addEventListener( IndexChangeEvent.CHANGE , onChange , false , 0 , true );
var l:VerticalLayout = new VerticalLayout;
l.gap = 0;
l.requestedMinRowCount = 0;
l.horizontalAlign = HorizontalAlign.CONTENT_JUSTIFY;
_list.layout = l;
this.addElement( _list );
}
//
// # LIST
//
protected function onChange(e:IndexChangeEvent):void
{
var obj:Object = _list.selectedItem;
this.removeAllElements();
_list = null;
this.close(true , obj);
}
private function getItemRenderer():IFactory
{
if( ! _itemRenderer )
{
var fac:StyledClassFactory = new StyledClassFactory( IconItemRenderer );
var props:Object = new Object;
props.messageField = "message";
props.labelField = "";
props.styleName = "itemName";
props.iconField = "icon";
var styles:Object = new Object;
styles.messageStyleName = "itemHeadline";
fac.properties = props;
fac.styles = styles;
return fac;
}
return _itemRenderer;
}
]]>
</fx:Script>
The problem here is, that my Callout does not measure correctly. When the dataProvider is added to the List, it always resizes the List to the first item. When some user-interaction happens with the List, it suddenly resizes correctly (adjusting to the largest item).
Unfortunaltely, the CallOut-Position does not change, leading to a misplaced Callout, sometimes it's even half off screen.
So I want to make sure, List has the right size, before I open the Callout.
How can I do this? Many thx for your input.
I had the same problem. I'm sure there's a more elegant way to do it but this was my solution.
Listen for Callout FlexEvent.CREATION_COMPLETE:
<s:Callout xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoLayout="true"
creationComplete="init()">
Force the callout to redo its layout/sizing:
function init():void
{
validateNow();
updatePopUpPosition();
}
In my app, i handle list data population slightly differently than you, so you may need to call init() after setting data instead.
I've been trying to get FXG to work in my Flex app, it works and renders fine but what I'm trying to accomplish is a sort of a gallery with data about the images in a database. I used to be able to use <s:Image source=/path/{variable_name}> but now I have to import the FXG files and can't use <s:Image> anymore. Here I can display a static FXG image:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fxg="assets.fxg.*"
tabBarVisible="false" title="{data.name}">
<fxg:megapicture001 x="118" y="27" width="338" height="519"/>
<s:Label x="78" y="43" text="{data.name}"/>
<s:navigationContent>
<s:Button icon="#Embed('assets/home.png')" click="navigator.popView()"/>
</s:navigationContent>
</s:View>
Trying to do <fxg:{data.picturename} /> blows up.
You can't import and use the FXG elements stand alone since they aren't display objects. My take was to wrap them in a UIComponent container. This class will probably end up as part of the Flextras Mobile Component set in our next update sometime early next year most likely:
package com.dotcomit.utils
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import mx.core.UIComponent;
public class FXGImage extends UIComponent
{
public function FXGImage(source:Class = null)
{
if(source){
this.source = source;
}
super();
}
// this will tell us the class we want to use for the display
// most likely an fxgClass
private var _source : Class;
protected var sourceChanged :Boolean = true;
public function get source():Class
{
return _source;
}
public function set source(value:Class):void
{
_source = value;
sourceChanged = true;
this.commitProperties();
}
public var imageInstance : DisplayObject;
// if you want to offset the position of the X and Y values in the
public var XOffset :int = 0;
public var YOffset :int = 0;
// if you want to offset the position of the X and Y values in the
public var heightOffset :int = 0;
public var widthOffset :int = 0;
override protected function createChildren():void{
super.createChildren();
if(this.sourceChanged){
if(this.imageInstance){
this.removeChild(this.imageInstance);
this.imageInstance = null;
}
if(this.source){
this.imageInstance = new source();
this.imageInstance.x = 0 + XOffset;
this.imageInstance.y = 0 + YOffset;
this.addChild(this.imageInstance);
}
this.sourceChanged = false;
}
}
override protected function commitProperties():void{
super.commitProperties();
if(this.sourceChanged){
// if the source changed re-created it; which is done in createChildren();
this.createChildren();
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(unscaledHeight != 0){
this.imageInstance.height = unscaledHeight + this.heightOffset;
}
if(unscaledWidth != 0){
this.imageInstance.width = unscaledWidth + this.widthOffset;
}
}
}
}
You can use it something like this:
<utils:FXGImage id="fxgImage" source="assets.images.mainMenu.MainMenuBackground" height="100%" width="100%" />
i want a dropdownlist that is wide enough to accommodate the larger item in the dropdown in display area of the main control.(.i.e. dropdownlist with the functionality of MX Combobox)
please guide me on this .
This will adjust the width of the drop down to the width of the data.
I forget where the example is where i got the code from. The height can be set by rowCount I believe.
package valueObjects.comboBox{
import flash.events.Event;
import mx.controls.ComboBox;
import mx.core.ClassFactory;
import mx.core.IFactory;
import mx.events.FlexEvent;
public class ExtendedComboBox extends ComboBox{
private var _ddFactory:IFactory = new ClassFactory(ExtendedList);
public function ExtendedComboBox(){
super();
}
override public function get dropdownFactory():IFactory{
return _ddFactory;
}
override public function set dropdownFactory(factory:IFactory):void{
_ddFactory = factory;
}
public function adjustDropDownWidth(event:Event=null):void{
this.removeEventListener(FlexEvent.VALUE_COMMIT,adjustDropDownWidth);
if (this.dropdown == null){
callLater(adjustDropDownWidth);
}else{
var ddWidth:int = this.dropdown.measureWidthOfItems(-1,this.dataProvider.length);
if (this.dropdown.maxVerticalScrollPosition > 0){
ddWidth += ExtendedList(dropdown).getScrollbarWidth();
}
this.dropdownWidth = Math.max(ddWidth,this.width);
}
}
override protected function collectionChangeHandler(event:Event):void{
super.collectionChangeHandler(event);
this.addEventListener(FlexEvent.VALUE_COMMIT,adjustDropDownWidth);
}
}
}
package valueObjects.comboBox{
import mx.controls.List;
public class ExtendedList extends List{
public function ExtendedList(){
super();
}
public function getScrollbarWidth():int{
var scrollbarWidth:int = 0;
if (this.verticalScrollBar != null){
scrollbarWidth = this.verticalScrollBar.width;
}
return scrollbarWidth;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" mlns:comboBox="valueObjects.comboBox.*" >
<comboBox:ExtendedComboBox labelField="data.totext()" itemRenderer="mx.controls.Label" />
</mx:Canvas>
You should create custom DropDownList (extending original) and override dataProvider setter and measure() method. In dataProvider setter you should invoke invalidateSize() and in measure you should iterate your data provider and find the largest label (you can assign new text to the label and compare their width).
To increase the height of a combo box's drop-down list, use "rowCount" property. This is for Flex 3.6.0
Check out this example from Flex Examples. You just have to create a skin for the DropDownList and set the popUpWidthMatchesAnchorWidth="false" property.
it is achieved by following code :
public override function set dataProvider(value:IList):void
{
var length:Number = 0;
var array:Array = value.toArray();
super.dataProvider = value;
var labelName:String = this.labelField;
var typicalObject:Object =new Object();
for each(var obj:Object in array)
{
var temp:Number = obj[labelName] .length;
if( length < temp )
{
length = temp;
Alert.show(" length "+length.toString());
typicalObject = obj;
}
//length = length < Number(obj[labelName].length) ? Number(obj[labelName].length) : length
//Alert.show(obj[labelName].length);
this.typicalItem = typicalObject;
}
}
Sorry, better take off both the width and height from the field.
The following is my codes. This is still work in progress; so, you will see some functions with empty contents. Plus, this is my first Flex application; please bear with me.
This is a quiz application that gets the questions and answers to each questions from a ColdFusion web service. There are three types of questions, True or False, Multiple Choice with single selection, and Multiple Choice with multiple selections. So, based upon the question type, the application would dynamically generate the appropriate amount of radio buttons or check boxes for the users to select. I got these working. The problem that I am having is, I am not sure how to check what the users have actually selected. In some other forums and posts on other web site, it said that I can use event.currentTarget.selectedValue to get the user selection. But when I actually do it, I got a run-time error saying, "Property selectedValue not found on mx.controls.FormItem and there is no default value." My question is, what do I need to do to capture the user selections?
Thanks in advance,
Monte
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="initVars()">
<fx:Declarations>
<s:RemoteObject id="CFCertService" destination="ColdFusion" source="CFCertExam.cfquiz">
<s:method name="returnQuestions" result="resultHandler(event)"/>
<s:method name="returnAnswers" result="answerHandler(event)"/>
</s:RemoteObject>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.containers.FormItem;
import mx.controls.Alert;
import mx.controls.CheckBox;
import mx.controls.RadioButton;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;
[Bindable]
private var questionArray:ArrayCollection;
private var questionType:String;
private var answerItem:FormItem;
[Bindable]
private var currentQuestionCounter:int;
[Bindable]
private var answerArray:ArrayCollection;
private var myOptionButton:RadioButton = new RadioButton();
private var myOptionButton2:RadioButton = new RadioButton();
private function initVars():void {
currentQuestionCounter = 0;
btnPrev.enabled = false;
btnNext.enabled = false;
}
private function answerHandler(event:ResultEvent):void {
answerArray = event.result as ArrayCollection;
var i:int;
answerForm.removeAllChildren();
answerItem = new FormItem();
answerForm.addChild(answerItem);
switch (questionType) {
case "True or False":
{
myOptionButton.label = "True";
if (answerArray.getItemAt(0).Answer_Choice == "True") {
myOptionButton.value = 1;
} else {
myOptionButton.value = 0;
}
answerItem.addChild(myOptionButton);
myOptionButton2.label = "False";
if (answerArray.getItemAt(0).Answer_Choice == "False") {
myOptionButton2.value = 1;
} else {
myOptionButton2.value = 0;
}
answerItem.addChild(myOptionButton2);
answerItem.addEventListener(MouseEvent.CLICK, selectionHandler);
break;
}
case "Multiple Choice (Single Selection)":
{
for (i=0; i<answerArray.length; i++) {
var myOptionButton1:RadioButton = new RadioButton();
myOptionButton1.label = answerArray.getItemAt(i).Answer_Choice;
if (answerArray.getItemAt(i).Corect_Flag == "1") {
myOptionButton1.value = 1;
} else {
myOptionButton1.value = 0;
}
answerItem.addChild(myOptionButton1);
}
break;
}
case "Multiple Choice (Multiple Selection)":
{
for (i=0; i<answerArray.length; i++) {
var myCheckBox:CheckBox = new CheckBox();
myCheckBox.label = answerArray.getItemAt(i).Answer_Choice;
answerItem.addChild(myCheckBox);
}
break;
}
}
answerForm.x = 380;
answerForm.y = 200;
}
private function selectionHandler(event:MouseEvent):void {
Alert.show(event.currentTarget.toString());
}
private function resultHandler(event:ResultEvent):void {
questionArray = event.result as ArrayCollection;
txt1Questions.htmlText = questionArray.getItemAt(currentQuestionCounter).Question_Text;
questionType = questionArray.getItemAt(currentQuestionCounter).Question_Type;
btnNext.enabled = true;
CFCertService.returnAnswers(questionArray.getItemAt(currentQuestionCounter).Question_ID);
}
private function buttonEventHandler():void {
CFCertService.returnQuestions();
btnStartExam.enabled = false;
}
private function btnPrevEventHandler():void {
currentQuestionCounter--;
if (currentQuestionCounter == 0) {
btnPrev.enabled = false;
}
if (currentQuestionCounter < questionArray.length) {
btnNext.enabled = true;
}
txt1Questions.htmlText = questionArray.getItemAt(currentQuestionCounter).Question_Text;
questionType = questionArray.getItemAt(currentQuestionCounter).Question_Type;
CFCertService.returnAnswers(questionArray.getItemAt(currentQuestionCounter).Question_ID);
}
private function answerReturnHandler(questionIndex:int):void {
}
private function btnNextEventHandler():void {
currentQuestionCounter++;
if (currentQuestionCounter > 0) {
btnPrev.enabled = true;
}
if (currentQuestionCounter >= (questionArray.length - 1)) {
btnNext.enabled = false;
}
txt1Questions.htmlText = questionArray.getItemAt(currentQuestionCounter).Question_Text;
questionType = questionArray.getItemAt(currentQuestionCounter).Question_Type;
CFCertService.returnAnswers(questionArray.getItemAt(currentQuestionCounter).Question_ID);
}
]]>
</fx:Script>
<mx:Text id="txt1Questions" x="129" y="124"/>
<s:Button id="btnStartExam" label="Start Exam" click="buttonEventHandler()" x="370" y="54"/>
<mx:Form id="answerForm"/>
<s:Button x="129" y="436" label="Previous" id="btnPrev" click="btnPrevEventHandler()" enabled="false"/>
<s:Button x="642" y="436" label="Next" id="btnNext" click="btnNextEventHandler()" enabled="false"/>
</s:Application>
The problem in your code is that currentTarget references the UIComponent that you add the event listener to, which you added to the FormItem and not the RadioButtons.
Two Options
If you want to continue to add the event listener to the FormItem, you should use target instead of currentTarget to obtain the reference to the actual item clicked, rather than the UIComponent with the listener on it. However, you should be aware that if you add anything else to the FormItem (e.g., Labels, RichText, etc), those items will also trigger the event listener when clicked.
The other option is to add event listeners (they can all use selectionHandler) to each of the RadioButtons and then currentTarget will work fine.
Also, you might want to use a RadioButtonGroup for those questions which only allow a single selection. Then you would only need to use the Event.CHANGE on the RadioButtonGroup to trigger your selectionHandler.
Additional Resource
Check out the video on event bubbling from the Flex in a Week series.