2009-07-13 22 views
8

Questa potrebbe essere una piccola domanda per principianti, ma non posso per la vita di me capirlo.Forzare Flex per aggiornare lo schermo?

Sto utilizzando flex per sviluppare una GUI per un progetto di grandi dimensioni, in particolare una barra di stato nella parte inferiore. All'interno della mia classe StatusBar c'è un ProgressBar, che le altre classi che fanno il lavoro possono dire di aggiornare (completare il completamento della barra e l'etichetta) mentre progrediscono. Il problema che sto incontrando è che flex non aggiornerà cosa è mostrato sullo schermo fino a che non sia troppo tardi, per esempio

ProgressBar inizializzato, 0% fatto
una certa classe imposta la ProgressBar essere fatto il 12%
qualche classe fa un certo lavoro
una certa classe imposta la ProgressBar essere 56% fatto

che cosa sta accadendo è il 12% fatto non è mai la visualizzazione, appende appena a 0% durante il lavoro, quindi salta fino al 56% fatto. Ho cercato di capire il ciclo di vita di un componente flessibile (invalidazione e convalida), e penso di capirlo e di applicarlo correttamente, ma non funziona affatto. Devo dire a flex di ridisegnare la mia StatusBar (o almeno la ProgressBar all'interno) dopo lo una classe imposta il 12%, ma prima dello una classe inizia a fare il suo lavoro. Come faccio a fare questo?

+0

Questo problema dovrebbe essere considerato come * consentendo * Flex di aggiornare lo schermo. Come indicato di seguito, Flash è un modello di esecuzione a thread singolo per il codice. Pertanto, è necessario ridurre il lavoro consentendo al giocatore di aggiornare lo schermo tra i blocchi. callLater() o l'utilizzo di timer per controllare i blocchi in modo autonomo sono meccanismi di base disponibili. Tieni presente che se fai * non * fai questo, il browser probabilmente farà uscire il lettore Flash dopo 45 secondi (impostazione predefinita FF) – verveguy

risposta

2

Provare a chiamare invalidateDisplayList() dopo ogni modifica alla barra di avanzamento. Qualcosa del tipo:

Class StatusBar 
{ 

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

Flex ha un ciclo di invalidazione che evita di ridisegnare lo schermo ogni volta che una proprietà cambia. Ad esempio, se il valore di una proprietà cambia 3 volte in un singolo fotogramma, verrà eseguito il rendering solo con l'ultimo valore impostato. È possibile forzare il ridisegno di un componente chiamando invidateDisplayList(), il che significa che updateDisplayList verrà eseguito immediatamente anziché attendere il fotogramma successivo.

+1

In realtà non inviterà updateDisplayList() "immediatamente" ma lo invocherà al prossimo aggiornamento/cornice dello schermo. Se lo facesse immediatamente, verrebbe a sconfiggere il punto del modello di invalidazione/convalida. –

+0

Il mio male, hai assolutamente ragione. Invece di chiamare invalidateDisplayList(), devi chiamare direttamente updateDisplayList() –

2

ActionScript in Flash player, come Javascript nel browser, è pseudo-multithread. Cioè, sono a thread singolo, ma hanno più stack di esecuzione. Ciò significa che non puoi "dormire" in un determinato thread, ma puoi generare un nuovo stack di esecuzione che viene posticipato fino a un momento successivo. Il modo flessibile per farlo è la funzione "callLater". Puoi anche utilizzare le funzioni setTimeout/setInterval. Oppure puoi usare un oggetto timer incorporato nel flash player. O anche il listener di eventi "ENTER_FRAME". Tutti questi ti consentiranno essenzialmente di fare ciò di cui hai bisogno, se sono corretto sulla causa dei tuoi problemi.

Sembra che tu abbia un "thread" che esegue la maggior parte del tuo lavoro, senza mai fermarsi per consentire l'esecuzione di altri stack di esecuzione (thread *).

Il problema potrebbe essere quello che dice PeZ, ma se ciò non aiuta, è possibile provare alcune chiamate differite per le classi worker. Quindi il tuo processo potrebbe essere simile a questo ora:

  1. Avanzamento inizializzato.
  2. Fai un po 'di lavoro.
  3. Aggiorna barra di stato su 12. (invalida elenco display)
  4. setTimeout (doMoreWork, 100);
  5. Aggiornamento barra di avanzamento a 52.

(se il lavoratore è un UIComponent, si può usare uicomp.callLater (...), in caso contrario, è necessario utilizzare setTimeout/timer/ENTER_FRAME per le classi AS3 puri).

9

Come menzionato in altre risposte, il flash player è a thread singolo, se non si suddivide il lavoro in blocchi discreti che possono essere eseguiti in "frame" separati, si vedranno salti e balzi nel ui, che è effettivamente quello che stai vedendo.

Se davvero si deve vedere che il messaggio del 12%, allora non è sufficiente per invalidare l'elenco di visualizzazione, come l'elenco di visualizzazione non è sempre la possibilità di aggiornare fino a dopo il lavoro il 56% ha completato, è necessario interrompere esplicitamente la ciclo di eventi naturali con una chiamata a validateNow() dopo che il tuo messaggio è stato impostato.

Questo tuttavia non è il modo migliore di fare le cose se le prestazioni sono preoccupanti. È possibile ottenere con l'uso giudiziario di callLater() per pianificare ogni blocco di lavoro a turno, in quanto ciò consentirà al giocatore di completare potenzialmente un ciclo di frame (e aggiornare l'elenco di visualizzazione) prima di tentare il passaggio successivo del processo.

+0

Non riesco ancora a ottenerlo, chiamo invalidateDisplayList() seguito immediatamente da validateNow() e non sembra aggiornarsi. La mia classe StatusBar così come ProgressBar e ProgressBarSkin vengono invalidate e quindi validate, tuttavia va per tutto e anche drawRect() per ridisegnare effettivamente la parte compilata di ProgressBar, ma ancora non si aggiorna in realtà lo schermo. –

+1

Hai provato callLater, setTimeout o simili, come ho suggerito? Sembra che l'elaborazione stia bloccando il renderer dall'aggiornamento dello schermo. Devi fermarti e lasciarlo respirare. – Glenn

+0

Aha! Parlare di bizzarri, posso aggiornarlo quando voglio usare setTimeout, a patto che il tempo di eseguire il resto della funzione dopo il setTimeout (doWork, X) richieda meno di X. In altre parole, devo lasciar andare il flash inattivo (per più di 25 ms). Questo tuttavia si interrompe se metto i punti di interruzione nel posto sbagliato o se provo a scorrere il codice. –

3

Glenn,

Questo non è affatto come il threading in Flex funziona sorta. Come molte UI ha un messaggio pump sul thread principale dell'interfaccia utente (lo fanno nei frame). Quando chiami callLater() posiziona il puntatore alla funzione passata alla fine della coda del messaggio (sul fotogramma successivo) e ritorna immediatamente. La funzione viene quindi chiamata quando il messaggio pump ha terminato l'elaborazione di tutti i messaggi precedenti (come i clic del mouse).

Il problema è che, come il cambiamento di proprietà fa eventi dell'interfaccia utente da attivare, hanno poi posto i propri messaggi sulla pompa che ora viene dopo la tua chiamata al metodo che è stato posizionato lì dal callLater().

Flex ha più thread ma sono lì per le ragioni di Adobe e quindi non sono accessibili all'utente. Non so se c'è un modo per garantire che un aggiornamento dell'interfaccia utente si verifichi in un punto specifico, ma un'opzione è quella di chiamare callLater un numero di volte fino a quando l'operazione si verifica. Inizia con un numero ridotto e aumenta finché il numero di iterazioni non produce il risultato desiderato. Esempio:

// 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); 
    } 
} 
+0

Stai fraintendendo la mia risposta. Stavo parlando principalmente di AS3/Flash, non di Flex. "callLater" è un'implementazione Flex in aggiunta. Usa solo eventi come ENTER_FRAME per lavorare in background. – Glenn

+1

Disattivare callLater per setTimeout potrebbe farlo funzionare in AS3. –

0

sto utilizzando Flash Builder 4.6 e ho anche un problema per la visualizzazione della mia barra di avanzamento. Apro una nuova finestra in cui avvio una nuova classe multiloader (39 Mo di contenuto). La nuova finestra viene aperta in background e la finestra principale mostra una barra di avanzamento finché la classe multiloader non ha terminato il suo lavoro. Tuttavia la finestra di apertura sta bloccando l'animazione della mia finestra principale. So che non è la classe multiloader perché ho visto che funziona correttamente.

Ma cercherò di trovare nuovi modi per farlo.

Lo scopo principale del mio post è la complessità che Adobe ha costruito attorno a Flash. Quando cerchi risorse per la tua applicazione o risposte per le tue domande, è una vera pena trovare la buona risorsa. C'è una totale confusione (da parte di Adobe e lato utente) tra AS3, Flex, Flash CS, Flash Builder, AiR, ... Se provi a sviluppare in AS3, scoprirai che alcuni esempi non funzioneranno per perché non è implementato nel tuo SDK. Hai sempre più forum che ti danno la "migliore pratica" o risposte ironiche basate su esperienze su diverse piattaforme di sviluppo.

Ad esempio, proprio qui sopra, vedo progressBar.value = value; Con la mia esperienza, posso dire che in Flash Builder 4.6, questa proprietà è di sola lettura. Ma potrebbe essere una classe personalizzata fatta dall'utente ma che può dirlo.

1

A volte è necessario impostare a zero prima di assegnare un altro valore. progressBar.setProgress (0, progressBar.maximum); progressBar.setProgress (newValue, progressBar.maximum);

Problemi correlati