2012-01-07 1 views
31

Ho un'app di disegno che impiega circa 2-5 secondi per caricare il disegno per disegni complicati (fatto tramite un AsyncTask). Per una migliore esperienza utente, in questo periodo I flash la versione PNG memorizzato del disegno che ho dalla cartella app come ImageView, e mostrare un carico ProgressBar chiamando setContentView() nel costruttore di attività:SurfaceView lampeggia nero sul carico

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    <ImageView 
    android:id="@+id/flash" 
    android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:src="@color/note_bg_white" 
     android:contentDescription="@string/content_desc_flash_img" 
     android:focusable="false" /> 
<RelativeLayout 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:paddingBottom="6dp" 
     android:paddingLeft="6dp" 
    android:paddingRight="6dp" 
     android:gravity="bottom|center_vertical"> 
     <ProgressBar 
      style="?android:attr/progressBarStyleHorizontal" 
    android:id="@+id/toolbar_progress" 
    android:layout_width="match_parent" 
    android:layout_height="18dp" 
    android:gravity="bottom|center_vertical" /> 
    </RelativeLayout> 
</FrameLayout> 

Quando il AsyncTask è completo, ho poi chiamo setContentView() di nuovo con il nuovo layout:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/layout" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="#ffffff"> 
    <com.my.package.DrawingCanvas 
     android:id="@+id/canvas" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:focusable="true" 
     android:background="#ffffff" /> 
</RelativeLayout> 

quando stavo usando un semplice modello Canvas, questo ha funzionato perfettamente, come il costume DrawingCanvas View richiama il disegno per il ghiaione n sull'iniziale onDraw() prima di essere mostrato all'utente, ma ora usando lo SurfaceView con un ciclo di disegno, vedo il layout lampeggiante, quindi quando il disegno è caricato, una schermata nera per circa un secondo, infine il nuovo DrawingCanvas.

Io parto dal presupposto che il motivo è legato al tempo di avvio del filo ciclo disegno, e ho lasciato il mio overide del onDraw() nel SurfaceView, e viene chiamato, ma la tela disponibile in onDraw() non lo fa sembra attrarre allo SurfaceView. Ho anche provato a impostare uno sfondo a tinta unita nell'XML di cui sopra, sperando almeno di mostrare uno sfondo bianco, ma quelli non sembrano mai avere effetto, nemmeno impostandolo dal codice.

Qualche consiglio o spiegazione di ciò che vedo sullo schermo nero?

EDIT:

Ok, hanno confermato che OnDraw() sta volgendo al stessa tela, così lasciato le mie ops disegnare in là pure sperando che sulla tappa iniziale del SurfaceView, l'utente vedrebbe quei disegni come nella normale implementazione Canvas, e quando il thread del disegno è stato ruotato, sovrascrive la tela.

Tuttavia, se cancello le operazioni del disegno, vedo i risultati di onDraw(), ma ancora, DOPO lo schermo nero lampeggiano. E se rimuovo completamente l'onDraw(), vedo ancora il flash nero e poi vedo il layout con lo sfondo bianco dell'XML.

Quindi, a prescindere da cosa, vedrò sempre lo schermo nero, a meno che forse invece di cambiare il layout, semplicemente modifico il layout 'flash' esistente che è già attivo?

EDIT2:

provato con un ViewStub in modo che possa gonfiare il SurfaceView nella vista esistente dopo la nota è stato caricato, ma lo stesso si applica issu ancora. Per quanto posso dire, c'è un ritardo considerevole (~ 200ms) tra il costruttore di SurfaceView e la chiamata a surfaceCreated() in esecuzione, ma non è sicuro che questo sia il punto in cui lo schermo nero sta accadendo, o perché lo schermo sia disegnato per nero ...

Edit3:

mio ultimo tentativo per ora include anche l'SurfaceView transparent. Questo combinato con l'abbandono del layout esistente e l'aggiunta semplicemente a quel layout tramite ViewStub avrebbe comportato una soluzione operativa, sebbene, tuttavia, per una frazione di secondo quando il SurfaceView viene caricato lo schermo diventa nero prima che venga visualizzato SurfaceView, come trasparente. Se qualcuno ha altre idee da provare, per favore pubblicale.

+0

Alcune operazioni pesanti su onCreate potrebbero dare risultati simili. – Ronnie

+0

Mostriamo il layout 'flash' in onCreate(), nessun ritardo lì. Lo schermo ritardo/nero arriva una volta che il disegno è stato caricato e avvio SurfaceView, che implica solo l'avvio del thread. –

+0

Possiamo vedere l'origine della classe DrawingCanvas? –

risposta

0

"la tela disponibile in onDraw() non sembra disegnare in SurfaceView" - Sei sicuro di non creare la seconda (o terza ...) istanza della Vista di superficie? E alcuni di loro potrebbero essere neri e mostrati per un secondo. Non vedo il codice qui, quindi, non posso dirlo con certezza, ma se fosse nella mia applicazione, controllerei prima questo errore.

4

non vorrei farlo con due layout separati.

Provare a rendere l'elemento radice un RelativeLayout e quindi fare in modo che SurfaceView e ImageView siano figli secondari di esso. Qualunque cosa accada con il tuo threading, ecc, ImageView dovrebbe essere disegnato completamente opaco in cima al SurfaceView, in modo da non ottenere un flash.

Una volta che il thread di lavoro ha caricato il disegno e SurfaceView può disegnare da sé, rimuovere la barra di avanzamento e ImageView dalla gerarchia di visualizzazione.

+0

L'ho provato senza successo, ma è possibile rivederlo brevemente se non riesco a trovare la causa principale del flash. –

+0

Dovrebbe funzionare. Forse sarebbe d'aiuto se hai pubblicato il codice per com.my.package.DrawingCanvas? –

+0

Questo non spiega il problema, ma sembra funzionare come soluzione alternativa, quindi la ricompensa che hackbod è stata ampliata su quella che potrebbe essere la causa principale e tutto ciò che ho provato relativo a quella linea di pensiero sembra fallire. –

1

È necessario assicurarsi che onSurfaceChanged() non ritorni fino a quando non si è completamente disegnato e pubblicato il contenuto di Surface Surface.

+0

Come faccio a garantire che Surfaceview sia completamente disegnato e pubblicato? Cosa potrebbe causare una chiamata a 'onSurfaceChanged()' prima di un post completo e disegnare? Ho confermato che View sta chiamando onSurfaceChanged() dopo l'inizio del mio thread di disegno, ma prima che il ciclo di disegno sia in grado di completare la prima operazione (attivata dalla chiamata lockCanvas()?) –

+0

hackbod è uno sviluppatore senior il team di Android, probabilmente ha ragione. Non mi è mai venuto in mente di provare a disegnare da onSurfaceChanged() ma è abbastanza ovvio ora lei lo suggerisce. Significa nel tuo onSurfaceChanged() che chiami holder.lockCanvas(), quindi disegna qualcosa, quindi chiama holder.unlockCanvasAndPost(). Se si dispone di una vista di immagini segnaposto anche nell'albero della vista, è sufficiente riempire la tela della superficie con trasparenza totale per ottenere l'effetto desiderato (non oscurarlo). –

+0

L'aggiunta del codice di disegno a 'onSurfaceChanged()' non ha purtroppo fatto nulla, continuando a vedere il flash nero. –

2

Suppongo che stiate disegnando un codice pesante nella vostra vista di superficie perché, per quanto ne so, la vista di superficie mostrerà la vista completa dopo aver disegnato tutto una volta. Ti suggerisco di andare prima al metodo onDraw() della vista della superficie, quindi impostare sullo sfondo della tela, quindi chiamare con forza invalida per evitare quella schermata nera. Aggiungi una condizione per assicurarti che questa invalida forzatamente venga chiamata una sola volta.

142

Credo di aver trovato la ragione per il flash nero. Nel mio caso sto usando un SurfaceView all'interno di un frammento e aggiungo dinamicamente questo frammento all'attività dopo qualche azione. Nel momento in cui aggiungo il frammento all'attività, lo schermo diventa nero. Ho verificato grepcode per la fonte SurfaceView ed ecco cosa ho trovato: quando la superficie vista appare nella finestra del tempo molto pugno, richiede parametri della finestra cambia chiamando un metodo privato IWindowSession.relayout(..). Questo metodo "dà" una nuova cornice, finestra e superficie della finestra. Penso che lo schermo lampeggi proprio in quel momento.

La soluzione è piuttosto semplice: se la finestra ha già parametri appropriati, non aggiornerà tutte le cose della finestra e lo schermo non lampeggerà. La soluzione più semplice è aggiungere un SurfaceView semplice 0px al primo layout della tua attività. Questo ricreerà la finestra prima che l'attività venga mostrata sullo schermo, e quando imposti il ​​secondo layout continuerà semplicemente ad usare la finestra con i parametri correnti. Spero che aiuti.

UPDATE: Sembra che dopo anni questo comportamento sia ancora presente. Vorrei raccomandare di usare TextureView invece di SurfaceView. Questo è letteralmente una più recente implementazione di stessa cosa che non hanno questo effetto collaterale, così come non hanno un problema di fondo nero quando si è in movimento (ad esempio nel ScrollView, ViewPager, RecyclerView ecc).

+15

Per quanto possa sembrare assurda questa risposta, l'aggiunta di un SurfaceView 0px * 0px nel layout della tua attività (l'attività del layout di root) rimuove effettivamente il flashing nero di SurfaceView nel tuo frammento! L'ho fatto e funziona perfettamente! –

+0

@ErikZ: grazie, il SurfaceView vuoto ha risolto il mio problema! – Oliver

+0

Questa soluzione ha rimosso alcuni problemi di sfarfallio da [CameraPreview come descritto nella documentazione] (http://developer.android.com/guide/topics/media/camera.html#camera-preview), in particolare quando si avvia l'anteprima. – miracula

1

Se si utilizza NavigationDrawer con frammenti, la soluzione Evos non funzionerà!
tenta di utilizzare NavigationDrawer con attività invece di frammenti, questo vi aiuterà a 100%

Come implementare NavDrawer con attività di: link
Un'altra utile link.(In caso di SurfaceView si avvarrà in cima SlidingMenu)

+0

Beh, non è nemmeno collegato al cassetto di navigazione, è più un frammento aggiunto dinamicamente con una superficie all'interno. – Evos

0

ho di fronte lo stesso problema, ma grazie per questa risposta

C'è un approccio meno disordinato, basta mettere getWindow(). SetFormat (PixelFormat. TRASLUCIDO); nella chiamata onCreate() dell'attività host prima di chiamare setContentView().

0

soluzione CrazyOrr ha funzionato per me, ma è stato in profondità nei commenti per la risposta superiore da Evos, ecco che è nuovo:

C'è un approccio meno disordinato, appena messo getWindow () .setFormat (PixelFormat.TRANSLUCENT); nella chiamata onCreate() dell'attività host prima di chiamare setContentView(). - CrazyOrr 5 maggio '16 a 07:29

Semplicemente mettendo

getWindow().setFormat(PixelFormat.TRANSLUCENT); 

nell'attività del onCreate() ha lavorato per me.