Is there a way to listen for this? I can easily listen for click which selects a RadioButton, but I can't seem to find a way to listen for when the RadioButton has been deselected.
Any ideas?
Thanks!
Back in the Flex 2 days, the "change" event triggered when a radio button was deselected. However, this little convenience disappeared in Flex 3, for some reason, and I don't believe that we were provided with any sort of replacement event.
Handling your events at the RadioButtonGroup level is all fine and well, except that there are times when you really want to handle the events on the radio button level -- particularly if you were hoping to interact with a data provider entry via an itemRenderer that is drawing the radio button.
Conveniently, I have a little bit of boilerplate code you can use as drop-in replacements for RadioButton and RadioButtonGroup that provide a "unselect" event at the radio button level. Here is the SmartRadioButton, first of all:
<?xml version="1.0" encoding="utf-8"?>
<s:RadioButton xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import events.SmartRadioButtonEvent;
public function notifyDeselect():void {
dispatchEvent(new SmartRadioButtonEvent('deselect'));
}
]]>
</fx:Script>
<fx:Metadata>
[Event(name="deselect", type="events.SmartRadioButtonEvent")]
</fx:Metadata>
</s:RadioButton>
And here is the SmartRadioButtonGroup:
<?xml version="1.0" encoding="utf-8"?>
<s:RadioButtonGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
change="selectionChanged();"
>
<fx:Script>
<![CDATA[
import spark.components.RadioButton;
import components.SmartRadioButton;
protected var oldSelection:SmartRadioButton = null;
// Notify our old selection that it has been deselected, and update
// the reference to the new selection.
public function selectionChanged():void {
var newSelection:SmartRadioButton = this.selection as SmartRadioButton;
if (oldSelection == newSelection) return;
if (oldSelection != null) {
oldSelection.notifyDeselect();
}
oldSelection = newSelection;
}
// Override some properties to make sure that we update oldSelection correctly,
// in the event of a programmatic selection change.
override public function set selectedValue(value:Object):void {
super.selectedValue = value;
oldSelection = super.selection as SmartRadioButton;
}
override public function set selection(value:RadioButton):void {
super.selection = value;
oldSelection = super.selection as SmartRadioButton;
}
]]>
</fx:Script>
</s:RadioButtonGroup>
The two property overrides are in there to make sure that we correctly update the oldSelection, in the event of a programmatic change to the group's selection.
SmartRadioButtonEvent isn't anything fancy or important. It could probably just be a plain Event, since there isn't a special payload.
I have tested the above code, and it all works, but there are surely edge conditions, and other oddities that should be addressed, if it's used in a larger system.
Related
I have prepared a simplified test case for my question. It will run instantly in your Flash Builder if you put the 2 files below into a project.
I'm trying to display a List of strings and a confirmation checkbox in a popup:
In the real application I dispatch a custom event with the string selected in the list, but in the test code below I just call trace(str);
My problem: if I use click event, then the window closes, even if I click at a scrollbar (the !str check below doesn't help, when an item had been selected in previous use). And if I use change event, then the window doesn't close, when I click on the same item as the last time. And the itemClick event seems not to be present in spark.components.List anymore.
Any suggestions please on how to handle this probably frequent problem?
Writing a custom item renderer and having a click event handler for each item seems to be overkill for this case, because I have strings in the list.
Test.mxml: (please click myBtn few times - to see my problems with click and change)
<?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="400" minHeight="300">
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var _popup:Popup = new Popup();
private function showPopup(event:MouseEvent):void {
PopUpManager.addPopUp(_popup, this, true);
PopUpManager.centerPopUp(_popup);
}
]]>
</fx:Script>
<s:Button id="myBtn" right="5" bottom="5"
label="Open window" click="showPopup(event)" />
</s:Application>
Popup.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="240" height="240"
creationComplete="init(event)"
close="close()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.events.CloseEvent;
import mx.events.ItemClickEvent;
import mx.managers.PopUpManager;
private var myData:ArrayList = new ArrayList();
private function init(event:FlexEvent):void {
// XXX in the real app data is updated from server
myData.removeAll();
for (var i:uint = 1; i <= 10; i++)
myData.addItem('Item #' + i);
}
public function close(event:TimerEvent=null):void {
PopUpManager.removePopUp(this);
}
private function handleClick(event:MouseEvent):void {
var str:String = myList.selectedItem as String;
if (!str)
return;
if (myBox.selected) {
Alert.show(
'Select ' + str + '?',
null,
mx.controls.Alert.YES | mx.controls.Alert.NO,
null,
handleConfirm,
null,
mx.controls.Alert.NO
);
} else {
sendEvent();
}
}
private function handleConfirm(event:CloseEvent):void {
if (event.detail == mx.controls.Alert.YES)
sendEvent();
}
private function sendEvent():void {
close();
// XXX in the real app dispatchEvent() is called
trace('selected: ' + (myList.selectedItem as String));
}
]]>
</fx:Script>
<s:VGroup paddingLeft="20" paddingTop="20"
paddingRight="20" paddingBottom="20" gap="20"
width="100%" height="100%">
<s:List id="myList" dataProvider="{myData}"
click="handleClick(event)"
width="100%" height="100%" fontSize="24" />
<s:CheckBox id="myBox" label="Confirm" />
</s:VGroup>
</s:TitleWindow>
Also I wonder, why do I get the warning above:
Data binding will not be able to detect assignments to "myData".
The Spark List dispatches an 'IndexChangeEvent.CHANGE'. You can listen for this event to know when the selection in the List has changed.
<s:List id="myList" dataProvider="{myData}"
change="handleIndexChange()"
width="100%" height="100%" fontSize="24" />
That event is only dispatched whenever the selected index actually changes, which means that when you reopen the window a second time an item might still be selected and when you click on that one, no CHANGE event will be fired. To fix this just deselect the selection before you close the window:
public function close():void {
myList.selectedIndex = -1;
PopUpManager.removePopUp(this);
}
Also make sure to dispatch your event with the selected item before you close the window (and deselect it).
As for your question about the binding warning: you get that message because you didn't mark 'myData' to be bindable. To fix this just use the [Bindable] tag:
[Bindable]
private var myData:ArrayList = new ArrayList();
or skip the binding altogether if you don't need it and just assign the dataprovider to the list in ActionScript:
myList.dataProvider = myData;
I'd recommend two solutions if you absolutely wnat to display what item was selected. Otherwise, the solution provided by RIAStar would do the trick.
Listen to rendererAdd and rendererRemove events within your PopUp
As explained here, you can easily access to your list's renderers without interfering with its virtualLayout business.
Use a custom renderer
I know. But as long as you keep your code clean, itemRenderers won't blow up your application's memory. They're made to render huge amount of items without memory leaks.
In your Test.mxml, modify the codes like:
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var _popup:Popup;
private function showPopup(event:MouseEvent):void {
_popup = new Popup();
PopUpManager.addPopUp(_popup, this, true);
PopUpManager.centerPopUp(_popup);
}
]]>
</fx:Script>
And in your Popup.mxml, I am not sure why you have the TimerEvent in the close function.
Also the trace won't be shown, as you are calling the close() function immediately after the alert's YES button has been clicked..
I have a List with TextInput as item renderer. I want to get the value entered in the TextInput (form the TextInputItemRenderer) and pass it the main application to do some checks(upon tapping enter on the textInput -- see code below).
I know that we can do it thru dispatching event but I still don't understand how to pass a variable from the ItemRenderer to the main app.
Help Pls.
Thanks
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="true" xmlns:components="components.*" width="100%"
>
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
override public function set data( value:Object ) : void {
super.data = value;
}
protected function myTextInput_enterHandler(event:FlexEvent):void
{
trace(myTextInput.text);
What Next??
}
]]>
</fx:Script>
<components:ClearableTextInput text="{data.label}" id="myTextInput" enter="myTextInput_enterHandler(event)"/>
</s:ItemRenderer>
i'm not sure if I got your question correctly but would this help?
http://www.ajibanda.blogspot.com/2011/02/changing-currentstate-of-main-and.html
Instead of trying to access from MainApp to itemRenderer, i think you can do backward. Follow one of two solutions below:
In itemRenderer, assign value you want to check later to a public global variable on the MainApp. The limitation is you then only enable to check it on MainApp, not any where esle (other itemRenderer, component, module etc.)
Use EvenBus to put the value to a global container. Create a static eventBus instance in AppUtils, for example. In itemRenderer, AppUtils.eventBus.dispatch() an event with the value attached to it each time the value changed. And then use AppUtils.eventBus again to addEventListener() to retrieve the value and check wherever you want. Google AS3Commons for EventBus.
I'm trying to implement a very simple way to select a subsection of the screen via mouse. The workflow is the standard one for many apps - click on starting point, move mouse and transparent rectangle updates between first point clicked and current position of the mouse. The basic code looks something like this (minus the graphics, which is simple)
<?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" mouseDown="application1_mouseDownHandler(event)">
<fx:Script>
<![CDATA[
import spark.components.mediaClasses.VolumeBar;
private var _anchorPoint:Point = new Point();
private var _currentPoint:Point = new Point();
protected function application1_mouseDownHandler(event:MouseEvent):void
{
addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
_anchorPoint = new Point(event.stageX, event.stageY);
}
private function handleMouseMove(e:MouseEvent):void
{
_currentPoint.x = e.stageX;
_currentPoint.y = e.stageY;
trace("rectangle between (",_anchorPoint.x, ",", _anchorPoint.y, ") and (", _currentPoint.x, ",", _currentPoint.y, ").");
}
private function handleMouseUp(e:MouseEvent):void
{
removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
}
]]>
</fx:Script>
</s:Application>
This breaks down when the user moves the mouse outside of the app. Not only _currentPoint stops updating, but also if you let go the mouse button outside of the app you miss the mouseUp event, i.e. when you move the mouse back on the app _currentPoint starts updating again as if you had never let go of the mouse button. Was wondering if there is a way in Flex (for web apps) to get around this by listening to mouseMove and mouseUp events when outside of the app (if that's possible) or whatever other way that may make sense.
thank you for your help!
Here's something most people don't know: MouseEvents are tracked outside of the application window if the MOUSE_DOWN event has been fired, but not MOUSE_UP. You can grab mouse positions outside of the application window (and even outside of the browser window) as long as whatever you're doing makes the user hold their mouse down. To test this, try running the following code:
<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="init()">
<fx:Script>
<![CDATA[
protected function init():void {
addEventListener(Event.ADDED_TO_STAGE, magic)
}
protected function magic(e:Event):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, moreMagic);
}
protected function moreMagic(e:MouseEvent):void {
magicalButton.label = "Hold me down! " + String(e.stageX) + "x" + String(e.stageY);
}
]]>
</fx:Script>
<s:Button id="magicalButton" label="Hold me down!"/>
It might be possible with some hacking in AIR, but definitely not with a webapp opened in a browser.
Just imagine what would happen if websites could track your mouse outside the browser and be able to take screenshots of your OS!
Though this won't get you to where you want, you can make use of Event.MOUSE_LEAVE and MouseEvent.MOUSE_MOVE events to track whether the cursor is within the boundaries of the application.
I have a spark borderContainer that contains a spark TextInput.
I have an mouse_down and mouse_up event handlers on the borderContainer in order to drag it around the screen; while dragging I change the cursor.
I would like to make the textInput behave as a "standard" textInput, i.e. when clicking on the textInput the user should not be able to drag the whole component but simply interact with text as he/she would normally. Also, I'd like the textInput cursor to look like a normal textInput cursor.
I'm not sure I'm doing this right. My assumption is that I need to stop the propagation of mouse_down and mouse_up on the textInput to inhibit the drag behavior from its parent, and manage rollOver and rollOut in order for the cursor to look ok. See below an example of my code (to simplify it there is no borderContainer or drag - but the code for that would be very simple - just a bit longer).
So here's the issue: if one clicks on the spark textInput and then rolls out of it the cursor turns into a combination of a textInput cursor + the standard cursor set for for the borderContainer. This doesn't seem to happen on mx textInput components (thus the two boxes), but unfortunately I need to use spark components. My guess is that I'm either not calling the cursorManager correctly or I'm not stopping the propagation of mouse_up properly - it seems like it should hit the textInput but not propagate to the borderContainer. I tried stopPropagation() as well but no luck.
Would love any advice / constructive criticism.
thank you!
f
<?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"
creationComplete="application1_creationCompleteHandler(event)"
mouseDown="application1_mouseDownHandler(event)"
mouseUp="application1_mouseUpHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.managers.CursorManager;
[Bindable] [Embed(source="../resources/hand.png")] private var _handIcon:Class;
[Bindable] [Embed(source="../resources/Fist.png")] private var _fistIcon:Class;
private var _cursorID:int;
protected function textinput1_rollOutHandler(e:MouseEvent):void
{
e.stopImmediatePropagation();
CursorManager.removeCursor(_cursorID);
_cursorID = CursorManager.setCursor(_handIcon);
}
protected function textinput1_rollOverHandler(e:MouseEvent):void
{
e.stopImmediatePropagation();
CursorManager.removeCursor(_cursorID);
}
protected function application1_creationCompleteHandler(e:FlexEvent):void
{
_cursorID = CursorManager.setCursor(_handIcon);
}
private function stopPropagation(event:MouseEvent):void
{
event.preventDefault();
event.stopImmediatePropagation();
}
protected function textinput1_mouseDownHandler(event:MouseEvent):void
{
stopPropagation(event);
}
protected function textinput1_mouseUpHandler(event:MouseEvent):void
{
stopPropagation(event);
}
protected function application1_mouseDownHandler(event:MouseEvent):void
{
CursorManager.removeCursor(_cursorID);
_cursorID = CursorManager.setCursor(_fistIcon);
}
protected function application1_mouseUpHandler(event:MouseEvent):void
{
CursorManager.removeCursor(_cursorID);
_cursorID = CursorManager.setCursor(_handIcon);
}
]]>
</fx:Script>
<s:TextInput x="43" y="30"
rollOut="textinput1_rollOutHandler(event)"
rollOver="textinput1_rollOverHandler(event)"
mouseDown="textinput1_mouseDownHandler(event)"
mouseUp="textinput1_mouseUpHandler(event)"/>
<mx:TextInput x="43" y="70"
rollOut="textinput1_rollOutHandler(event)"
rollOver="textinput1_rollOverHandler(event)"
mouseDown="textinput1_mouseDownHandler(event)"
mouseUp="textinput1_mouseUpHandler(event)"/>
You can simply do not start drag and do not change the cursor if user clicks on input:
protected function application1_mouseDownHandler(event:MouseEvent):void
{
var container:DisplayObjectContainer = event.target as DisplayObjectContainer;
if (!container || container == textInput || textInput.contains(container))
return;
// start drag and change the cursor
}
I had a similar problem but I have several TextInput fields in the container. So to avoid checking every single of them I used this version of the idea:
if (event.target is RichEditableText) return;
Works perfectly...
Greetings, J!
re: spark.components.ButtonBar
In the spark ButtonBar's mouseOver event, how do you determine which of the several buttons the mouse is hovering over? There is, of course, no selected index at this juncture. If it makes a difference, my ButtonBar is not defined in MXML but is instantiated in ActionScript and an ArrayList is assigned to the dataProvider property of my ButtonBar instance.
Thanks for the help.
There's no real easy/built-in way to do this if Flex 4, and I think that's a good thing. Instead, they give you access to the renderers via ElementExistenceEvent.RENDERER_ADD and ElementExistenceEvent.RENDERER_REMOVE, so you can look for all kinds of events on the children. Try this out:
<?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">
<fx:Script>
<![CDATA[
import mx.core.IVisualElement;
import spark.events.RendererExistenceEvent;
protected function rendererAddHandler(event:RendererExistenceEvent):void
{
var element:IVisualElement = event.renderer;
element.addEventListener(MouseEvent.MOUSE_MOVE, renderer_mouseMoveHandler);
}
protected function rendererRemoveHandler(event:RendererExistenceEvent):void
{
var element:IVisualElement = event.renderer;
element.removeEventListener(MouseEvent.MOUSE_MOVE, renderer_mouseMoveHandler);
}
protected function renderer_mouseMoveHandler(event:MouseEvent):void
{
trace(event.currentTarget.label);
}
]]>
</fx:Script>
<s:ButtonBar id="buttonBar"
rendererAdd="rendererAddHandler(event)"
rendererRemove="rendererRemoveHandler(event)">
<s:dataProvider>
<mx:ArrayList source="[one, two, three, four]"/>
</s:dataProvider>
</s:ButtonBar>
</s:Application>
Hope that helps,
Lance
You can simply use the itemRollOver event of the spark buttonbar.