这可能是一个初学者的问题,但我不能为我的生活弄明白。
我正在使用flex为大型项目开发GUI,特别是底部的状态栏。在我的StatusBar类中有一个ProgressBar,其他正在工作的类可以告诉他们在进展时更新(更改栏完成和标签)。我遇到的问题是flex不会更新屏幕上显示的内容,直到为时已晚,例如
ProgressBar已初始化,0%已完成
某些类将ProgressBar设置为12%
某些班级做了一些工作
某些类将ProgressBar设置为56%
发生的事情是12%完成从未显示,它只是在工作期间挂起0%,然后直接跳到56%完成。我试图理解flex组件的生命周期(失效和验证),我想我理解它并且正确地应用它,但它根本不起作用。我需要告诉flex重新绘制我的StatusBar(或者至少在其中的ProgressBar)某个类将它设置为12%完成后,但在某些类开始执行其工作之前。我该怎么做?
答案 0 :(得分:9)
正如其他答案中所提到的,flash播放器是单线程的,如果你不将你的工作分解为可以在单独的“帧”中执行的离散块,你将会看到ui中的跳跃和口吃,这实际上就是你所看到的。
如果你真的必须看到12%的消息,那么使显示列表无效是不够的,因为显示列表没有机会更新,直到56%的工作完成后,你必须明确地中断自然设置好邮件后调用validateNow()
的事件周期。
然而,如果表现令人担忧,这不是最好的做事方式。您可以通过司法使用callLater()
依次安排每个工作块,因为这将允许玩家在尝试您的流程中的下一步之前可能完成一个框架周期(并更新显示列表)。
答案 1 :(得分:3)
格伦,
这根本不是Flex中的线程如何工作的。像许多UI一样,它在主UI线程上有一个消息泵(它们在框架中执行)。当您调用 callLater()时,它会将传入的函数指针放在消息泵队列的末尾(在下一帧上)并立即返回。然后在消息泵完成之前处理所有消息(如鼠标点击)时调用该函数。
问题在于,当属性更改导致UI事件被触发时,他们会在泵上放置自己的消息,现在来自您从 callLater()放置的方法调用之后。
Flex确实有多个线程,但它们是出于Adobe自身的原因,因此无法访问用户。我不知道是否有办法保证在特定点发生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 );
}
}
答案 2 :(得分:2)
尝试在每次更改进度条后调用invalidateDisplayList()。类似的东西:
Class StatusBar
{
public function set progress(value:uint):void
{
progressBar.value = value;
progressBar.invalidateDisplayList();
}
}
Flex有一个无效循环,可以避免每次属性更改时重新绘制屏幕。例如,如果属性的值在单个帧中更改3次,则它将仅使用最后设置的值进行渲染。您可以通过调用invidateDisplayList()来强制重新绘制组件,这意味着updateDisplayList将立即执行,而不是等待下一帧。
答案 3 :(得分:2)
Flash Player中的Actionscript,就像浏览器中的Javascript一样,是伪多线程的。也就是说,它们是单线程的,但它们有多个执行堆栈。这意味着您不能在特定线程中“休眠”,但是您可以生成一个新的执行堆栈,该堆栈会延迟到稍后的时间。这样做的灵活方式是“callLater”功能。您还可以使用setTimeout / setInterval函数。或者您可以使用内置于Flash播放器中的计时器对象。甚至是“ENTER_FRAME”事件监听器。如果我对你的问题的原因是正确的话,所有这些基本上都会让你做你需要的。
听起来你有一个“线程”完成你的大部分工作,从不停止允许其他执行堆栈(线程*)运行。
问题可能是PeZ所说的,但如果这没有帮助,你可能想尝试一些对工人类的延期调用。所以你的流程现在看起来像这样:
(如果你的worker是一个UI组件,你可以使用uicomp.callLater(...),否则你需要对纯AS3类使用setTimeout / timers / enter_frame。)
答案 4 :(得分:1)
有时在分配另一个值之前必须将其设置为零。 progressBar.setProgress(0,progressBar.maximum); progressBar.setProgress(newValue,progressBar.maximum);
答案 5 :(得分:0)
我正在使用Flash Builder 4.6,我的进度条显示也有问题。我打开一个新窗口,在那里我开始一个新的多重装载器类(39 Mo的内容)。新窗口在后台打开,主窗口显示进度条,直到多重装载器类完成其工作。然而,打开窗口阻止了我的主窗口的动画。我知道这不是多重装载器类,因为我看到它正常运行。
但我会尝试找到一些新方法。
我的帖子的主要目的是adobe围绕flash构建的复杂性。 当您为自己的应用程序寻找资源或为您的问题寻找答案时,找到好的资源是一件非常痛苦的事情。在AS3,Flex,Flash CS,Flash Builder,AiR等之间总共混淆了(在adobe端和用户端)...如果你尝试在AS3中开发,你会发现一些例子不适用于你是因为它没有在你的SDK中实现。您有越来越多的论坛根据不同开发平台的经验为您提供“最佳实践”或讽刺性答案。
例如,就在上面,我看到progressBar.value = value;根据我的经验,我可以说在Flash Builder 4.6中,此属性是只读的。但它可能是由用户制作的自定义类,但谁可以告诉。