8

这可能是一个初学者的问题,但我一生都无法弄清楚。

我正在使用 flex 为一个大型项目开发 GUI,特别是底部的状态栏。在我的 StatusBar 类中有一个 ProgressBar,其他正在工作的类可以告诉它们在进展时更新(更改栏完成和标签)。我遇到的问题是 flex 不会更新屏幕上显示的内容,直到为时已晚,例如

ProgressBar 已初始化,0% 已完成
某些类将 ProgressBar 设置为 12% 已完成
某些类已完成某些工作
某些类将 ProgressBar 设置为 56% 已完成

发生的事情是 12% 的完成从未显示,它只是在工作期间挂在 0%,然后直接跳到 56% 完成。我试图理解一个 flex 组件的生命周期(失效和验证),我想我理解它并且正确地应用它,但它根本不起作用。我需要告诉 flex 在某些类将其设置为 12% 完成后重绘我的 StatusBar(或至少是其中的 ProgressBar) ,但在某些类开始工作之前。我该怎么做呢?

4

6 回答 6

9

正如其他答案中提到的,flash 播放器是单线程的,如果你不将你的工作分解成可以在单独的“帧”中执行的离散块,你会在 ui 中看到跳跃和口吃,这是有效地你所看到的。

如果您真的必须看到 12% 的消息,那么使显示列表无效是不够的,因为在 56% 的工作完成之前显示列表没有机会更新,您必须明确中断自然事件循环validateNow()在您的消息设置后调用。

然而,如果性能受到关注,这并不是最好的方法。您可能会通过司法用法callLater()来依次安排每个工作块,因为这将允许玩家在尝试您的流程的下一步之前可能完成一个帧周期(并更新显示列表)。

于 2009-07-14T12:32:14.237 回答
3

格伦,

这根本不是 Flex 中线程的工作方式。像许多 UI 一样,它在主 UI 线程上有一个消息泵(它们在框架中执行)。当您调用callLater()时,它会将传入的函数指针放在消息泵队列的末尾(在下一帧上)并立即返回。当消息泵之前完成所有消息的处理(如鼠标单击)时,该函数将被调用。

问题是,当属性更改导致 UI 事件被触发时,它们会将自己的消息放在泵上,这些消息现在来自您从callLater()放置的方法调用之后。

Flex 确实有多个线程,但它们的存在是出于 Adob​​e 自身的原因,因此用户无法访问。我不知道是否有办法保证 UI 更新将在特定点发生,但一种选择是调用callLater多次,直到操作发生。从一个小数字开始并增加,直到迭代次数产生您想要的结果。例子:

// 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 );
    }
}
于 2010-05-10T21:09:12.090 回答
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.

于 2009-07-13T20:21:31.697 回答
2

Flash 播放器中的 Actionscript 与浏览器中的 Javascript 一样,是伪多线程的。也就是说,它们是单线程的,但它们有多个执行堆栈。这意味着您不能在特定线程中“休眠”,但您可以生成一个新的执行堆栈,该堆栈被推迟到以后的时间。执行此操作的灵活方式是“callLater”函数。您还可以使用 setTimeout/setInterval 函数。或者您可以使用内置于 Flash 播放器中的计时器对象。甚至是“ENTER_FRAME”事件监听器。如果我对您的问题的原因是正确的,那么所有这些基本上都可以让您做您需要做的事情。

听起来您有一个“线程”在完成大部分工作,从不停止以允许其他执行堆栈(线程*)运行。

问题可能是 PeZ 所说的,但如果这没有帮助,您可能想尝试一些延迟调用工人课程。所以你的过程现在可能看起来像这样:

  1. 进度已初始化。
  2. 做一些工作。
  3. 将进度条更新为 12。(无效显示列表)
  4. 设置超时(做更多工作,100);
  5. 将进度条更新为 52。

(如果你的 worker 是 UIcomponent,你可以使用 uicomp.callLater(...),否则,你需要使用 setTimeout/timers/enter_frame 来处理纯 AS3 类)。

于 2009-07-13T22:02:47.390 回答
1

有时在分配另一个值之前必须将其设置为零。progressBar.setProgress(0, progressBar.maximum); progressBar.setProgress(newValue, progressBar.maximum);

于 2015-04-15T00:02:07.030 回答
0

我正在使用 Flash Builder 4.6,而且我的进度条显示也有问题。我打开一个新窗口,在其中启动一个新的多加载器类(39 个月的内容)。新窗口在后台打开,主窗口显示一个进度条,直到 multiloader 类完成他的工作。但是打开的窗口阻止了我的主窗口的动画。我知道这不是 multiloader 类,因为我看到它运行正常。

但我会尝试找到一些新的方法来做到这一点。

我的帖子的主要目的是 adobe 围绕 flash 构建的复杂性。当您为自己的应用程序寻找资源或为您的问题寻找答案时,找到好的资源真的很痛苦。在 AS3、Flex、Flash CS、Flash Builder、AiR 之间存在完全混淆(在 adobe 端和用户端)...如果您尝试在 AS3 中开发,您会发现有些示例不适用于你因为它没有在你的 SDK 中实现。您有越来越多的论坛根据在不同开发平台上的经验为您提供“最佳实践”或具有讽刺意味的答案。

例如,就在上面,我看到 progressBar.value = value; 以我的经验,我可以说在 Flash Builder 4.6 中,这个属性是只读的。但它可能是用户制作的自定义类,但谁能说出来。

于 2013-12-23T09:02:09.407 回答