Flash Builder Long Press on List - Mobile - actionscript-3

I want to display a list (in a tile layout, but I don't think that changes anything) and open up the editing window for the tile that I long press on (i.e. press for at least 1 second). The timer starts fine and the longPressHandler event is correctly called after 1 second, however, the object that is selected is the previous object that I had touched.
For example, say I tap on Object A, then I long press on Object B: the longPressHandler will "open" up Object A in the editing window. I have debugged and I see that the SelectedItem property of my List only updates after I end my long press (as in, after I pick up my finger or release the mouse button). Is there any way to open up the currently selected item?
Relevant Actionscript:
private var longPressTimer:Timer = new Timer(1000,1);
private function startLongPressMouse(event:MouseEvent):void {
startLongPressTimer();
list.addEventListener(MouseEvent.MOUSE_MOVE, endLongPressMouse);
}
private function endLongPressMouse(event:MouseEvent):void {
stopLongPressTimer();
enableClick();
list.removeEventListener(MouseEvent.MOUSE_MOVE, endLongPressMouse);
}
private function startLongPress(event:TouchEvent):void {
startLongPressTimer();
list.addEventListener(TouchEvent.TOUCH_MOVE, endLongPress);
}
private function endLongPress(event:TouchEvent):void {
stopLongPressTimer();
enableClick();
list.removeEventListener(TouchEvent.TOUCH_MOVE, endLongPress);
}
private function startLongPressTimer():void {
longPressTimer.start();
longPressTimer.addEventListener(TimerEvent.TIMER_COMPLETE, longPressHandler);
}
protected function disableClick():void {
trace("disable click");
list.removeEventListener(MouseEvent.CLICK, regularClickHander);
}
public function enableClick():void {
list.callLater(list.addEventListener, [MouseEvent.CLICK, regularClickHander]);
}
public function resetListSelection():void {
list.selectedIndex = -1;
list.validateDisplayList();
}
private function stopLongPressTimer():void{
longPressTimer.stop();
longPressTimer.reset()
}
public function longPressHandler(event:TimerEvent):void{
disableClick();
stopLongPressTimer();
lblD.text = "Long Press Detected on: " + list.selectedItem.className;
}
Relevant MXML:
<s:List id="list" dataProvider="{grades}" touchBegin="startLongPress(event)" touchEnd="endLongPress(event)"
mouseDown="startLongPressMouse(event)" mouseUp="endLongPressMouse(event)"
labelField="name"
left.landscape="10" right.landscape="20" top.landscape="350" bottom.landscape="20"
left.portrait="20" right.portrait="20" top.portrait="350" bottom.portrait="20">
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer width="100%" height="200">
<s:Label text="{data.className}" top="30" horizontalCenter="0" color="#646464"/>
<s:Label text="{data.credits}" top="50" horizontalCenter="0" color="#646464" fontSize="14"/>
<s:Label text="{data.grade}" top="100" horizontalCenter="0" color="#646464" fontSize="14"/>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
<s:layout>
<s:TileLayout requestedColumnCount="3" requestedColumnCount.landscape="4" columnAlign="justifyUsingWidth"/>
</s:layout>
</s:List>
I changed the longPressHandler to just display the name of the selected item rather than open up the editing window.
Let me know if you need any more information. Thank you in advance for your help!

One way is to create a custom event (with custom property) so that you can pass whatever data you want when you dispatch the event.
package events;
{
import flash.events.Event;
public class YourCustomEvent extends Event
{
public var yourData:Object;
public function YourCustomEvent(type:String, yourData:Object)
{
super(type);
this.yourData = yourData;
}
override public function clone():Event{
return new YourCustomEvent(type, yourData);
}
}
}
Hope it helps!
PS: I wanted to put this in the comment but it got bigger as a typed :)

Related

Data Value lose color when Scrolling on Datagrid in Flex 4

I am having problem when I am scrolling my datagrid. My item renderer column is losing its value when I scroll.
In my code, I change the colour of the itemrenderer (a label) on MouseOver and MouseOut. When I load the datagrid, this works fine but when I scroll down my grid some values have their colours already changed as if the MouseOver event has been executed on them.
Any one can tell me what is the issue with this?
Please have a look at the code for my datagrid and the itemrenderer. Please note that I am using a flexicious datagrid in my case.
Thanks for your precious help.
<flxs:columnLevel>
<flxs:FlexDataGridColumnLevel>
<flxs:columns>
<flxs:FlexDataGridColumn dataField="testcol" width="118" id="coltest">
<flxs:itemRenderer>
<fx:Component>
<mx:VBox horizontalAlign="left" paddingLeft="10" verticalAlign="middle">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
protected function lbl1_clickHandler(event:MouseEvent):void
{
//Do Something
}
protected function lbl1_mouseOverHandler(event:MouseEvent):void
{
var dataColor:uint;
var dataUnderline:String = 'none';
if (data.payer == 'D'){
dataColor = 0x999999;
}
else{
dataColor = 0x0DACE0; //color
}
}
protected function lbl_mouseOutHandler(event:MouseEvent):void
{
var dataColor:uint;
if (data.payer == 'D'){
dataColor = 0x999999;
}
else{
dataColor = 0x000000;
}
}
]]>
</fx:Script>
<mx:Label id="lbl" paddingLeft="10" left="10" fontWeight="normal" mouseOut="lbl_mouseOutHandler(event)" mouseOver="lbl_mouseOverHandler(event)" text="{data.testcol}" click="lbl1_clickHandler(event)"/>
</mx:VBox>
</fx:Component>
</flxs:itemRenderer>
</flxs:FlexDataGridColumn>
</flxs:columns>
</flxs:FlexDataGridColumnLevel>
</flxs:columnLevel>
</flxs:FlexDataGrid>*
Short answer: You haven't overridden the "set data" on your itemrenderer, so it will keep the previous properties.
Itemrenderers in the Flex world are recycled, meaning that even though you may have 100 objects in your list, you only have 10-12 actual instances of your inline itemrenderer. The caveat is that the when the itemrenderer is populated with new data, the data-dependent properties aren't reset. That lack of resetting is why you have renderers that appear to have been mousedOver when in fact they havent'.
The standard solution is to override the "set data" function which does exactly as it sounds, it performs operations when the data is set. If I had a similar problem, my code would look like (best I can do with a 9:00AM memory).
override public function set data(value:Object):void
{
if( value != null )
{
super.data = value'
dataColor = 0xwhatever-color-non-moused-over-objects-should-have;
}
}
Here is a link with more information.

GridColumn labelFunction not bindable

Recently i noticed one problem when using labelFunction for gridColumn:
It does not refresh, when nested (displayed) property is changed.
// SomeClass and SomeSubClass has [Bindable] metadata
[Bindable]
private var someSubClass:SomeSubClass;
[Bindable]
private var someClass:SomeClass;
private function inits():void{
someSubClass = new SomeSubClass();
someSubClass.value = "SomeSubValue";
someClass = new SomeClass();
someClass.subclass = someSubClass;
var ac:ArrayCollection = new ArrayCollection();
ac.addItem(someClass);
dg.dataProvider = ac;
}
private function myLabelFunction(item:Object, column:GridColumn):String
{
return (item.subclass)? item.subclass.value : "";
}
mxml
<s:Button click="someSubClass.value = 'Hello World'"/>
<s:DataGrid dataProvider="{dataProvider}" width="100%" height="200">
<s:columns>
<s:ArrayList>
<s:GridColumn labelFunction="{myLabelFunction}" headerText="NoRenderer"/>
<s:GridColumn headerText="WithRenderer">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<s:Label text="{data.subclass.value}"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:gridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
When button clicked first column which use labelFunction still displaing old value, but second column (with custom renderer) works fine. I am trying to use as less as possible custom item renderers for performance issue. Or something wrong with my label function?
I've never tried this, but I'm not surprised that it won't update. In the second example, you've explicitly set a binding chain on data.subclass.value. In the first case with myLabelFunction, there's no binding set up on item.subclass, so Flex won't have any "magic" way of knowing you'd like to watch that value for changes.
I would go with the custom renderer, I'm not sure what your particular performance concerns are but it should be manageable as long as you leave virtual layout / renderer recyling on.

Item Renderer not Working correctly in Spark List

I used flex sdk 4.5 for creating Adobe Air application. My application download files one by one. I show the currently downloading, pending and completed files in spark list. I used item Renderer that show the progress of the file and other states.
It works fine if selected files fit in current view. But as number of downloads increase, the Spark list shows vertical scroll. here I have the problem. Here file downloading functionality works fine, but if I try to scroll the list, File titles mix up or displayed out of order.
item renderer code:
<?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"
creationComplete="onInit()"
>
<fx:Script><![CDATA[
import com.streamingnetworks.hdvrwin.vo.DownloadInfo;
import mx.events.CloseEvent;
[Bindable]private var downloadInfo:DownloadInfo;
private var url:String = "";
private var fileName:String = "";
private function onInit():void
{
downloadInfo = data as DownloadInfo;
downloadInfo.addEventListener("Progress", onProgress);
downloadInfo.addEventListener("DownloadComplete", onComplete);
prog.label = "Downloading %3%%";
lblPath.text = " - " + downloadInfo.getPathIp();
trace("Downloading: " + downloadInfo.name);
}
private function onProgress(e:Event):void
{
prog.setProgress(downloadInfo.downloadedBytes, downloadInfo.totalBytes);
lblState.text = downloadInfo.getDownState();
}
private function onComplete(e:Event):void
{
prog.label = "Download Complete";
}
protected function onClose(event:CloseEvent):void
{
downloadInfo.state = "deleted"
}
]]></fx:Script>
<s:TitleWindow top="0" left="0" right="0" bottom="0" dropShadowVisible="false"
title="{'Downloading ' + downloadInfo.name}" close="onClose(event)">
<mx:ProgressBar id="prog" label="" mode="manual"
trackHeight="20" labelPlacement="center" width="100%"/>
<s:HGroup top="25">
<s:Label id="lblState" text="Downloading 0 Bytes of 0 Bytes."/>
<s:Label id="lblPath"/>
</s:HGroup>
</s:TitleWindow>
</s:ItemRenderer>
here the list control where I am using that renderer
<s:List id="lstControls" dataProvider="{urlArr}"
itemRenderer="ItemRenderer"
left="5" right="5" bottom="5" top="40"/>
urlArr is an arrayList that contains only url in string form.
How can I solve this problem? Any Help will be appreciated. Thanks
Your renderers are not updating correctly because you are setting your data when the renderer is created - but you should be overriding the set data method, eg:
override public function set data(value:Object):void
{
super.data = value;
downloadInfo = data as DownloadInfo;
downloadInfo.addEventListener("Progress", onProgress);
downloadInfo.addEventListener("DownloadComplete", onComplete);
prog.label = "Downloading %3%%";
lblPath.text = " - " + downloadInfo.getPathIp();
trace("Downloading: " + downloadInfo.name);
}
And remove the creationComplete listener.
As a better practice. Have all the data your progress bars will need all ready in the data provider and have them react ONLY to changes in the data. Adding and removing listeners and binding internally will only cause you grief. Renderers do not live forever and are re-used inside of list components. Manage everything through overriding set data().
In this example I would create a model object with a name like FileDownloadInfo and properties such as .downloadProgress .fileName and .ipAddress then have the renderer update on property change events of .downloadProgress

selectedIndex in List after Alert

I have a list with an ArrayCollection dataProvider. In my program, there is a button the user can click to perform a function for the selectedIndex of the List, but an Alert is shown first asking them if they are sure they want to perform the action. After the user answers the Alert, the action is performed on the selectedIndex of the list.
My problem is that selectedIndex = -1 after the Alert window CloseEvent, even though it is clearly selected. I got around this by performing validateNow() on the list in the code for the Alert CloseEvent.
My question: Why do I have to do this and am I doing something wrong? Or is this normal/best practice? Also, is there a better/best practice to check a List to see if something is selected besides using try-catch. I don't want the end user to see the generated error if nothing is selected.
Code:
//Note: "fl" is a class with "friendsList" bindable ArrayCollection; for the sake of keeping this short I will not include it
private function _removeFriendClick(event:MouseEvent):void
{
try {
if (this.friendsList.selectedIndex != -1) {
Alert.show("Are you sure you want to remove "+this.fl.friendsList[this.friendsList.selectedIndex].label+" as a friend?", "Remove Friend", Alert.YES | Alert.CANCEL, this, this._removeFriendConfirm, null, Alert.CANCEL);
}
} catch (e:Error) { }
}
private function _removeFriendConfirm(event:CloseEvent):void
{
this.friendsList.validateNow();
trace(this.friendsList.selectedIndex);
}
So, with the above code, if you take out the validateNow(), an exception is thrown because it thinks the selectedIndex is -1.
I would do it in this way:
<?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">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.CloseEvent;
[Bindable]private var friendsList:ArrayCollection = new ArrayCollection([{data:"111", label:"Don"}, {data:"222", label:"John"}]);
private function onBtnRemove():void
{
laFriend.text = "";
try
{
if (cbFriends.selectedIndex != -1)
{
Alert.show("Are you sure you want to remove " + cbFriends.selectedItem.label + " as a friend?", "Remove Friend", Alert.YES | Alert.CANCEL, this, this._removeFriendConfirm, null, Alert.CANCEL);
}
} catch (e:Error) { }
}
private function _removeFriendConfirm(event:CloseEvent):void
{
laFriend.text = "Selected friend: " + cbFriends.selectedItem.label;
}
]]>
</fx:Script>
<mx:VBox>
<s:ComboBox id="cbFriends" dataProvider="{friendsList}"/>
<s:Button id="btnRemove" label="Remove" click="onBtnRemove()"/>
<s:Label id="laFriend" text=""/>
</mx:VBox>
</s:Application>
Do you perform selection just before you call your handler?
If you set selectedIndex, you can't get it back immediately because of the livecycle - value shall be commited before you can read it.
Your validateNow forces that commit. However, it shall happen later without enforcing it manually.

Custom Event from Item Renderer not detected by a class

I am trying to dispatch a custom event from an item renderer(which is a child of the Main application file / root).
Code in Main.mxml:
<s:List id="movieGrid"itemRenderer="views.MovieRenderer" dataProvider="{new ArrayCollection()}">
</s:List>
<s:Group width="100%" height="100%" bottom="60">
<views:DetailedViewInfo id="detailed" includeIn="MoviePage" />
</s:Group>
Renderer (something clicked):
MyEventDispatcher.Dispatcher.dispatchEvent(new MovieClickEvent(MovieClickEvent.CLICKED, data));
DetailedViewInfo (creation complete):
MyEventDispatcher.Dispatcher.addEventListener(MovieClickEvent.CLICKED, clickHandler);
MyEventDispatcher:
package events
{
import flash.events.EventDispatcher;
public class MyEventDispatcher
{
public static var Dispatcher:EventDispatcher = new EventDispatcher();
}
}
Event:
package events
{
import flash.events.Event;
public class MovieClickEvent extends Event
{
public function MovieClickEvent(type:String, theMovieData:Object, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this._result = theMovieData;
}
public function get result():Object
{
return this._result;
}
override public function clone():Event
{
return new MovieClickEvent(type, result, bubbles, cancelable)
}
public static const CLICKED:String = "MovieClickEvent.CLICKED";
private var _result:Object;
}
}
I am able to listen for the event successfully in the Main.mxml but I also need to detect it in a SkinnableContainer - "DetailedViewInfo" that is also a child of Main.mxml:
It his possible at all? I tried importing all related events / classes and same for declarations. It does not work even if I comment out the event listener in Main.mxml. I tried adding a declaration to the item renderer in DetailedViewInfo but that crashes the application with no understandable error.
Could someone explain to me how this should be done? I am using custom events all over the place in my application and hadn't had this happen before. Any help highly appreciated!
It would seem you're adding the event listener after the event was dispatched. I see you have an includeIn statement there: this means the DetailedViewInfo component will not be immediately created, but only when the MoviePage state is entered. The event may be dispatched before the component is created and the event listener attached.
The quick fix for this issue, is to not use includeIn, but set the component's visibility according to the current state:
<views:DetailedViewInfo id="detailed" visible="false" includeInLayout="false"
visible.MoviePage="true" includeInLayout.MoviePage="true" />
However, you may want to review your architecture if you need to resort to this. Unfortunately I can't tell you much more than that, since I don't know your current architecture.