Forcing Flex to update the screen?
Asked Answered
S

6

8

This may be a bit of a beginners question, but I can't for the life of me figure it out.

I'm using flex to develop a GUI for a large project, specifically a status bar along the bottom. Within my StatusBar class is a ProgressBar, which other classes doing work can tell to update(change bar completion and label) as they progress. The problem I'm encountering is that flex won't update whats shown on the screen until it's too late, for example

ProgressBar initialized, 0% done
some class sets the ProgressBar to be 12% done
some class does some work
some class sets the ProgressBar to be 56% done

Whats happening is the 12% done is never displaying, it just hangs at 0% during the work, then skips right to 56% done. I've tried to understand the lifecycle of a flex component (invalidation and validation), and I think I understand it and am applying it correctly, but it's not working at all. I need to tell flex to redraw my StatusBar (or at least the ProgressBar within) after some class sets it to be 12% done, but before some class starts doing its work. How do I do this?

Stocky answered 13/7, 2009 at 17:9 Comment(1)
This issue should really be thought of as allowing Flex to update the screen. As pointed out below, Flash is a single-threaded execution model for your code. Thus, you need to chunk your work allowing the player to refresh the display between chunks. callLater() or using timers to control chunking yourself are basic mechanisms available. Keep in mind that if you do not do this, the browser will likely time the Flash player out after 45 seconds (FF default)Woodrow
F
9

As mentioned in other answers, the flash player is single threaded, if you don't break up your work into discrete chunks that can be executed in separate "frames", you're going to see jumps and stutters in the ui, which is effectively what you're seeing.

If you really must see that 12% message, then it's not enough to invalidate the display list, as the display list isn't getting a chance to update until after the 56% work has completed, you must explicitly interrupt the natural event cycle with a call to validateNow() after your message has been set.

This however is not the best way to do things if performance is of concern. You might get by with judicial usage of callLater() to schedule each chunk of work in turn, as this will allow the player to potentially complete a frame cycle (and update the display list) before attempting the next step in your process.

Firing answered 14/7, 2009 at 12:32 Comment(3)
I still can't get it, I call invalidateDisplayList() followed immediately by validateNow() and it doesn't appear to update. My StatusBar class as well as the ProgressBar and ProgressBarSkin are being invalidated and then validated, it goes all the way though and even does the drawRect() to actually redraw the filled in part of the ProgressBar, but it still doesn't actually update on the screen.Stocky
Did you try callLater, setTimeout, or similar, as I suggested? It sounds like your processing is blocking the renderer from updating the screen. You need to pause, and let it breath.Misdeed
Aha! Talk about bizarre, I can get it to update when I want using setTimeout, as long as the time to execute the rest of the function after the setTimeout(doWork,X) takes less than X. In other words I have to let flash go idle (for more then 25ms). This however breaks if I put breakpoints in the wrong place or if I try and step through the code.Stocky
S
3

Glenn,

That is not at all how the threading in Flex works whatsoever. Like many UIs it has a message pump on the main UI thread (they do it in frames). When you call callLater() it places the passed in function pointer at the end of the message pump queue (on the next frame) and returns immediately. The function then gets called when the message pump has finished processing all of the messages prior (like mouse clicks).

The issue is that as the property change causes UI events to be triggered, they then place their own messages on the pump which now comes after your method call that you placed there from callLater().

Flex does have multiple threads but they are there for Adobe's own reasons and therefore are not user accessible. I don't know if there is a way to guarantee that a UI update will occur at a specific point, but an option is to call callLater a number of times until the operation occurs. Start off with a small number and increase until the number of iterations produces the result you want. Example:

// Change this to a number that works... it will probably be over 1, depending on what you're doing.
private const TOTAL_CALL_COUNT:int = 5;

private var _timesCalled:int = 0;

//----------------------------------------------------------------
private function set Progress( progress:int ):void
{
    progressBar.value = progress;
    DoNextFunction();
}

//----------------------------------------------------------------
private function DoNextFunction():void
{
    if( _timesCalled >= TOTAL_CALL_COUNT )
    {
        _timesCalled = 0;
        Function();
    }
    else
    {
        _timesCalled++;
        callLater( DoNextFunction );
    }
}
Shanitashank answered 10/5, 2010 at 21:9 Comment(2)
You are misunderstanding my response. I was speaking of AS3/Flash mostly, not Flex. "callLater" is a Flex implementation on top of that. It just uses events like ENTER_FRAME to work in the background.Misdeed
Switching out callLater for setTimeout would make it work in AS3.Generate
H
2

Try calling invalidateDisplayList() after each changes to your progress bar. Something like :

Class StatusBar
{

    public function set progress(value:uint):void
    {
        progressBar.value = value;
        progressBar.invalidateDisplayList();
    }
}

Flex has an invalidation cycle that avoid screen redrawing everytime a property changes. As an example, if a property's value changes 3 times in a single frame, it will render only with the last value set. You can force a component to be redrawn by calling invidateDisplayList() which means updateDisplayList will be immediatly executed instead of waiting the next frame.

Holloway answered 13/7, 2009 at 20:21 Comment(2)
Actually it won't invoke updateDisplayList() "immediately" but it will invoke it at the next screen update / frame. If it did it immediately it would kind of defeat the point of the invalidation / validation model.Painty
My bad, you're absolutely right. Instead of calling invalidateDisplayList(), you have to call directly updateDisplayList()Holloway
M
2

Actionscript in Flash player, like Javascript in the browser, is pseudo-multithreaded. That is, they're single threaded, but they have multiple execution stacks. This means you can't "sleep" in a particular thread, but you can spawn a new execution stack that gets deferred until a later time. The flex way of doing this is the "callLater" function. You can also use the setTimeout/setInterval functions. Or you can use a timer object built into the flash player. Or even "ENTER_FRAME" event listener. All of these will essentially allow you to do what you need, if I'm correct about the cause of your problems.

It sounds like you have one "thread" doing most of your work, never stopping to allow other execution stacks (threads*) to run.

The problem could be what PeZ is saying, but if that doesn't help, you might want to try some deferred calls for worker classes. So your process might look like this now:

  1. Progress initialized.
  2. Do some work.
  3. Update progress bar to 12. (invalidate display list)
  4. setTimeout(doMoreWork, 100);
  5. Update progress bar to 52.

(if your worker is a UIcomponent, you can use uicomp.callLater(...), otherwise, you need to use setTimeout/timers/enter_frame for pure AS3 classes).

Misdeed answered 13/7, 2009 at 22:2 Comment(0)
R
1

Sometimes its necessary set to zero before assign another value. progressBar.setProgress(0, progressBar.maximum); progressBar.setProgress(newValue, progressBar.maximum);

Recoverable answered 15/4, 2015 at 0:2 Comment(0)
G
0

I'm using Flash Builder 4.6 and I also have a problem for the display of my progress bar. I open a new window where I start a new multiloader class (39 Mo of content). The new window is opened in background and the main window display a progress bar until the multiloader class has finished his work. However the opening window is blocking the animation of my main window. I know it's not the multiloader class cause I saw it running correctly.

But I will try to find some new ways of doing it.

The main purpose of my post is the complexity adobe has build around flash. When you seek ressources for your own application or answers for your questions, it's a real pain to find the good ressource. There is a total mix up (at adobe side and at user side) between AS3, Flex, Flash CS, Flash Builder, AiR, ... If you try to develop in AS3, you will find that some examples won't work for you because it is not implemented in your SDK. You have more and more forums giving you the "best practice" or ironic answers based on experiences on different developping platform.

By example, just here above, I see progressBar.value = value; With my experience, I can say that in Flash Builder 4.6, this property is read-only. But It might be a custom class made by the user but who can tell.

Gill answered 23/12, 2013 at 9:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.