Flex timer delay changer? - apache-flex

I need help with the following...
var timer:Timer = new Timer(x);
basically x is an array...
when timer.start() is invoked
it runs, the first count is 1000 ms, then the second 800 ms, and the third 6200 ms and so on. In other words, it's a dynamic change in the delay and not a continuous delay of x ms.
How can this be done? Any examples would be greatly appreciated.
Thank you in advance

var timer:Timer = new Timer(delay);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
function timerHandler(e:TimerEvent):void
{
timer.stop();
if (timer.currentCount == 1) {
timer.delay = 800;
} else if (timer.currentCount == 2) {
timer.delay = 6200;
} else {
//other conditions
}
timer.start();
}

What #package said is right. Based on your comment saying you have hundreds of delays, this would be better code.
var delays:Array=[1000, 500, 6200, ...];
var timer:Timer = new Timer(delay);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
function timerHandler(e:TimerEvent):void {
timer.stop();
timer.delay = delays[timer.currentCount-1];
timer.start();
}

Related

Timer in xamarin app speeds up and slows down

Im creating ccountdown timer in xamarin.forms.
The problem is when I pause the timer and next I'll start the timer, it speeds up and slows down and it happens the whole time. It should goes every second but why does it speed up sometimes ever 2/4 seconds ?
The code below:
bool Value =false;
int counter =120;
private void RunTimer(Boolean Value)
{
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
If(Value)
{
counter--;
if(counter <=0)
{
counter = 120;
MainText.Text = dt.AddSeconds(counter).ToString("mm:ss");
}
MainText.Text = dt.AddSeconds(counter).ToString("mm:ss");
}
return true;
}
return false;
});
private void Start_Pause(object sender, EventArgs e)
{
if(value == false)
{
RunTimer(true);
Value = true;
}
else
{
RunTimer(false);
Value = false;
}
}
MainText.Text = dt.AddSeconds(counter).ToString("mm:ss"); <-- This is incorrect. Computer timers are never precise. Instead compute the DateTime end value once, and recompute remaining time by subtracing DateTime.Now on every tick (instead of counting imprecise time periods yourself).
Change your code to something like this:
private void RunTimer(Boolean value)
{
DateTime start = DateTime.Now;
DateTime end = start.AddSeconds( 120 );
Device.StartTimer(
interval: TimeSpan.FromMilliseconds( 100 ),
callback: () =>
{
TimeSpan remaining = end - DateTime.Now;
Device.BeginInvokeOnMainThread( () =>
{
this.MainText.Text = remaining.ToString("mm:ss");
});
return remaining >= TimeSpan.Zero;
});
}
I'm using TimeSpan.FromMilliseconds( 100 ) to ensure the label's text is invalidated closer to when the remaining value actually changes to counteract jitter in the system.
Using return remaining >= TimeSpan.Zero; will stop the countdown as soon as end is passed.
The documentation says that all UI interactions inside StartTimer must be done with Device.BeginInvokeOnMainThread.

can't we use 2 timer events in a single application

Can we use 2 timer events in a single application. I'm trying to use 2 timer events in a single application but the 2nd timer event is not working.... any one have an idea??? how to use 2 timer events in a single application....
Thanks in advance....
You can have as many numbers of timers as you want in your application. Just initialize them as if you'd initialize any other timer. Post the code you tried and we might be able to fix the issue.
var t1:Timer = new Timer(1000, 0);
t1.addEventListener(TimerEvent.TIMER, timerHandler1);
t1.start();
var t2:Timer = new Timer(500, 0);
t2.addEventListener(TimerEvent.TIMER, timerHandler2);
t2.start();
public function timerHandler1(event:TimerEvent):void {
trace("First timer triggered");
}
public function timerHandler2(event:TimerEvent):void {
trace("Second timer triggered");
}
It is not mandatory to use separate listeners; you can as well do:
public var t1:Timer;
public var t2:Timer;
t1 = new Timer(1000, 0);
t2 = new Timer(200, 0);
t1.addEventListener(TimerEvent.TIMER, timerHandler);
t2.addEventListener(TimerEvent.TIMER, timerHandler);
public function timerHandler2(event:TimerEvent):void {
if(event.target == t1)
trace("first timer");
else
trace("second timer");
}

Flex ArgumentError: Error #2025 in ItemRenderer

I've got a problem in an ItemRenderer in Flex 3.5. I've looked at the other posts regarding this error but still can't figure it out. The ItemRenderer is part of an AdvancedDataGrid who's data provider is HierarchicalData. I'm getting the ArgumentError but the trace doesn't go to any of my code. I've gone through in debug mode tons of times but it looks like it doesn't happen until after my code runs. Quite strange.
The item renderer has a couple different parts. It figures out what row it should be drawing for based on the xml data and then adds labels and sprites appropriately. If anyone can help, that would be a great help! Thanks!
Here is one of the methods that gets called if the itemrenderer is on a certain row.
private function addLabels(planList:ArrayCollection):void {
height = 0;
var sprite:Sprite = new Sprite();
var curX:Number = (width / planList.length);
height = 110;
for each (var plan:Plan in planList) {
var label:Label = new Label();
label.text = plan.planner.label;
label.rotationZ = 270;
label.visible = true;
label.x = curX - 7;
//Draw line divider
curX += (width / planList.length);
addChild(label);
label.move(label.x, height - 30);
//Draw divider line
sprite.graphics.lineStyle(.5, 0x000000);
sprite.graphics.moveTo(label.x - ((width / planList.length) / 2) + 10.5, 0);
sprite.graphics.lineTo(label.x - ((width / planList.length) / 2) + 10.5, height - 28);
//Draw meatball
sprite.graphics.beginFill(0x00FF33);
sprite.graphics.drawCircle(label.x + 10, height - 15, 10);
sprite.graphics.endFill();
}
rawChildren.addChild(sprite);
}
There's another function that gets called on a different row, but if I comment out the code above everything works fine, so my guess is that the problem definitely lies there. Thanks for the help!
Here is where addLabels gets called:
override protected function createChildren():void {
removeAllChildren();
var count:int = rawChildren.numChildren;
for (var i:int = 0; i < count; i++) {
if (rawChildren.getChildAt(0).parent) {
rawChildren.removeChildAt(0);
}
}
var allPlans:ArrayCollection = new ArrayCollection();
if (_plan) {
getAllPlans(_plan, allPlans);
}
if (_name == "capability") {
}
else if (_name == "components") {
height = 130;
width = 335;
addLabels(allPlans); // <-- RIGHT HERE!
var sprite:Sprite = new Sprite();
sprite.graphics.lineStyle(.5, 0x000000);
sprite.graphics.moveTo(0, 0);
sprite.graphics.lineTo(width, 0);
sprite.graphics.moveTo(0, height - 28);
sprite.graphics.lineTo(width, height - 28);
rawChildren.addChild(sprite);
}
}
I've seen this kind of thing before. This error could be happening in the AdvancedDataGrid itself, not the itemRenderer.
See http://www.judahfrangipane.com/blog/?p=196 for more information.
If you simply want to draw something on specific rows, you might try extending the AdvancedDataGrid to override two functions:
override protected function drawHorizontalLine(s:Sprite, rowIndex:int, color:uint, y:Number):void {
// do some drawing
}
override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void {
// do some drawing
}
Here's the answer if anyone was looking at this..
I'm still not exactly sure what the problem was, but the AdvancedDataGrid certainly did not like me adding a Label as a child to the renderer. Here's how I got around it.. add a TextField as a child to the sprite, as shown:
var newLabel:TextField = new TextField();
newLabel.text = plan.planner.label;
newLabel.rotationZ = 270;
newLabel.visible = true;
newLabel.x = curX - (dividerWidth / 2) - ((newLabel.textHeight) / 1.5);
newLabel.y = height - (RADIUS * 2.5);
newLabel.antiAliasType = AntiAliasType.ADVANCED;
sprite.addChild(newLabel);
Hope this helps someone!

Flex AS3: ProgressBar doesn't move

I am a little stuck and need some advice/help.
I have a progress bar:
<mx:ProgressBar id="appProgress" mode="manual" width="300" label="{appProgressMsg}" minimum="0" maximum="100"/>
I have two listener functions, one sets the progress, and one sets the appProgressMsg:
public function incProgress(e:TEvent):void {
var p:uint = Math.floor(e.data.number / e.data.total * 100);
trace("Setting Perc." + p);
appProgress.setProgress(p, 100);
}
public function setApplicationProgressStep(e:TEvent):void {
trace("Setting step:" + e.data);
appProgressMsg = e.data;
}
I want to reuse this progress bar alot. And not necessarily for ProgressEvents, but when going through steps.
For instance, I loop over a bunch of database inserts, and want to undate the progress etc.
Here is a sample:
public function updateDatabase(result:Object):void {
var total:int = 0;
var i:int = 0;
var r:SQLResult;
trace("updateDatabase called.");
for each (var table:XML in this.queries.elements("table")) {
var key:String = table.attribute("name");
if (result[key]) {
send(TEvent.UpdateApplicationProgressStep, "Updating " + key);
i = 1;
total = result[key].length;
for each (var row:Object in result[key]) {
//now, we need to see if we already have this record.
send(TEvent.UpdateApplicationProgress, { number:i, total: total } );
r = this.query("select * from " + key + " where server_id = '" + row.id + "'");
if (r.data == null) {
//there is no entry with this id, make one.
this.query(table.insert, row);
} else {
//it exists, so let's update.
this.update(key, row);
}
i++;
}
}
}
}
Everything works fine.
That is, the listener functions are called and I get trace output like:
updateDatabase called.
Setting step:Updating project
Setting Perc 25
Setting Perc 50
Setting Perc 75
Setting Perc 100
The issue is, only the very last percent and step is shown. that is, when it's all done, the progress bar jumps to 100% and shows the last step label.
Does anyone know why this is?
Thanks in advance for any help,
Jason
The new code, which works awesomely I might add:
public function updateDatabase(result:Object, eindex:int = 0, sindex:int = 0 ):void {
var total:int = 0;
var i:int = 0;
var j:int;
var r:SQLResult;
var table:XML;
var key:String;
var elems:XMLList = this.queries.elements("table");
var startTime:int = getTimer();
var row:Object;
for (i = eindex; i < elems.length(); i++) {
table = elems[i];
key = table.attribute("name");
if (!result[key])
continue;
total = result[key].length;
send(TEvent.UpdateApplicationProgressStep, "Updating " + key);
for (j = sindex; j < result[key].length; j++) {
if (getTimer() - startTime > 100) {
setTimeout(updateDatabase, 100, result, i, j);
send(TEvent.UpdateApplicationProgress, { number:j, total: total } );
return;
}
row = result[key][j];
r = this.query("select * from " + key + " where server_id = '" + row.id + "'");
if (r.data == null) {
//there is no entry with this id, make one.
this.query(table.insert, row,false);
} else {
//it exists, so let's update.
this.update(key, row,false);
}
}
send(TEvent.UpdateApplicationProgress, { number:1, total: 1 } );
}
}
Flash is single threaded. The display will not update until your function returns. For this reason, you should never have any code that runs for longer than about 100ms (1/10th of a second), otherwise the UI (or even the entire browser) will appear to be locked up.
The general solution is to split up your work over multiple frames, here is some pseudo-code:
function doWork(arg1:Obj, arg2:Obj, start:int=0) {
var startTime = getTimer(); // store starting time
for(i=start; i<length; i++) {
if(getTimer() - startTime > 100) { // see if we've been working too long
trace("Current progress: "+(i/length * 100)+"%");
updateProgress( i / length );
setTimeout(doWork, 100, arg1, arg2, i); // schedule more work to be done
return; // stop current loop
}
trace("Working on item "+i);
// processing here
}
trace("All work done");
}
doWork(data1, data2); // start the work
Your pseudo-code works for updating the progress bar however in my case my "work" was copying of files from DVD to the appStorageDirectory which seem to reintroduce the same issue that your work around resolved - the progress bar now does not update
Here is my hack of your solution
function doWork(arg1:int, arg2:int, start:int=0) {
var startTime = getTimer(); // store starting time
for(var i:int=start; i<arg2; i++) {
if(getTimer() - startTime > 100 ) { // see if we've been working too long
trace("Current progress: "+(i/arg2 * 100)+"%");
setTimeout(doWork, 100, i, arg2, i); // schedule more work to be done
return; // stop current loop
}
trace("Working on item "+i);
dispatchEvent(new progressMadeEvent("incrementChange",i,arg2))
var dir:String = copyRes[i].nativePath.toString().split(OSSep).pop()
copyRes[i].copyTo(appStore.resolvePath(dir)) // copies dir from DVD to appStorageDir
}
trace("All work done");
}

AIR: component behaves wrong after switching window

Ok so I have a component, that has a function to remove itself as a popUp in its current Window, and add itself to a newly created Window.
It works, however, if the component has a child like a ComboBox, the drop down still pops up in the old window where it used to be, also scrollbars, and focus seems to behave incorrectly in the new window also.
It seems to me like Flex still thinks the component is a child of the original window, not the new window. I have no idea how to resolve this though.
Here is my code:
private var ownWindow:Window;
private var _inOwnWindow:Boolean;
private var _removedEffect:Move;
private var _openX:Number;
private var _openY:Number;
public function launchInNewWindow(e:Event):void
{
_openX = Application.application.nativeWindow.x + this.x + 5; //keep in same spot add 5 for systemChrom border
_openY = Application.application.nativeWindow.y + this.y + 30;//keep in same spot add 30 for systemChrom title
this.parent.removeChild(this);
ownWindow = new Window();
ownWindow.systemChrome = 'none';
ownWindow.type = NativeWindowType.LIGHTWEIGHT;
ownWindow.transparent = true;
ownWindow.setStyle('showFlexChrome', false);
ownWindow.width = this.width > 750 ? 750 : this.width;
ownWindow.height = this.height > 550 ? 550 : this.height;
edit.enabled = false;
_removedEffect = this.getStyle('removedEffect') as Move;
if(_removedEffect == null)
{
openNewWindow();
}
else
{
// Wait for removed effect to play before adding to new window
_removedEffect.addEventListener(EffectEvent.EFFECT_END,delayOpenInNewWindow);
}
}
private function delayOpenInNewWindow(e:Event = null):void
{
var t:Timer = new Timer(100,1);
t.addEventListener(TimerEvent.TIMER,openNewWindow);
t.start();
}
private function openNewWindow(e:Event = null):void
{
ownWindow.addChild(this);
ownWindow.width += 5; //add to show dropshadow
ownWindow.height += 10; //add to show dropshadow
ownWindow.open();
_inOwnWindow = true;
ownWindow.nativeWindow.x = _openX;
ownWindow.nativeWindow.y = _openY;
}
Any ideas?
Thanks!!
Before I give this a run, have you tried a callLater on the openNewWindow() line?
[ lame fix attempt, i know -- but given that there doesn't seem to be an event that you can listen for in the case that the removedEffect isn't null and it seems like a timer is your only option there, I think it's o.k. to give lame fix attempts :-) ]

Resources