web-dev-qa-db-fra.com

SurfaceView clignote en noir en charge

J'ai une application de dessin qui prend environ 2 à 5 secondes pour charger le dessin de dessins compliqués (effectué via un AsyncTask). Pour une meilleure expérience utilisateur, pendant ce temps, j’ai flashé la version PNG du dessin que j’ai dans le répertoire de l’application en tant que ImageView et j’affiche un chargement ProgressBar appelant setContentView() dans le constructeur d’activité:

<?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>

Lorsque le AsyncTask est terminé, j'appelle à nouveau setContentView() avec la nouvelle présentation:

<?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>

Lorsque j’utilisais un modèle Canvas simple, cela fonctionnait parfaitement, car DrawingCanvas View personnalisé dessinait le dessin à l’écran sur la onDraw() initiale avant d’être présenté à l’utilisateur, mais je lis maintenant le SurfaceView avec une boucle de dessin. mise en page flashé, puis lorsque le dessin est chargé, un écran noir pendant environ une seconde, puis enfin le nouveau DrawingCanvas. 

Je suppose que la raison est liée au temps de démarrage du fil de la boucle de dessin et que j'ai laissé mon excès de onDraw() dans SurfaceView, et il est appelé, mais la toile disponible dans onDraw() ne semble pas attirer le SurfaceView. J'ai également essayé de définir un arrière-plan de couleur unie dans le code XML ci-dessus en espérant au moins afficher un arrière-plan blanc, mais ceux-ci ne semblent jamais avoir d'effet, même en le définissant à partir de code. 

Un conseil ou une explication de ce que je vois avec l'écran noir?

MODIFIER:

Ok, ont confirmé que onDraw () dessine sur le même canevas, donc laissé mes opérations de dessin là-dedans et espérant que lors de la première présentation de SurfaceView, l’utilisateur verrait ces dessins comme dans l’implémentation régulière de Canvas. Le fil de dessin s’était remonté, il écraserait le canevas.

Cependant, si j'efface les opérations du fil de dessin, je vois les résultats de onDraw (), mais encore une fois, APRÈS le clignotement de l'écran noir. Et si je supprime complètement la substitution onDraw (), je vois toujours le flash noir, puis la mise en page avec le fond blanc du code XML.

Alors, on dirait que, quoi qu'il arrive, je vais toujours voir l'écran noir, à moins que peut-être au lieu de changer de mise en page, je modifie simplement la mise en page "flash" existante qui est déjà active?

EDIT2:

J'ai essayé d'utiliser ViewStub pour pouvoir gonfler SurfaceView dans la vue existante après le chargement de la note, mais le même problème s'applique toujours. Autant que je sache, il existe un délai considérable (~ 200 ms) entre le constructeur SurfaceView et l'appel à l'exécution de surfaceCreated (), mais vous n'êtes pas sûr que ce soit là où se produit l'écran noir ou la raison pour laquelle l'écran est dessiné. noir...

EDIT3:

Ma dernière tentative pour le moment consiste à rendre le SurfaceView transparent . Cela, combiné avec le fait de laisser la disposition existante en place et simplement d'ajouter à cette disposition via le ViewStub aurait abouti à une solution de travail, bien que, cependant, pendant une fraction de seconde lorsque SurfaceView charge l'écran clignote en noir avant que SurfaceView ne s'affiche, comme transparent. Si quelqu'un a d'autres idées à essayer, merci de les poster.

32
Paul Mennega

Je pense avoir trouvé la raison du flash noir. Dans mon cas, j'utilise un SurfaceView dans un fragment et j'ajoute ce fragment à l'activité de manière dynamique après une action. Au moment où j'ajoute le fragment à l'activité, l'écran clignote en noir. J'ai vérifié grepcode pour le source SurfaceView et voici ce que j'ai trouvé: lorsque la vue de surface apparaît dans la fenêtre la toute première fois, elle demande que les paramètres de la fenêtre changent en appelant une méthode privée IWindowSession.relayout(..). Cette méthode vous "donne" un nouveau cadre, une nouvelle fenêtre et une nouvelle surface de fenêtre. Je pense que l'écran clignote juste à ce moment. 

La solution est assez simple: si votre fenêtre a déjà des paramètres appropriés, elle n'actualisera pas tous les éléments de la fenêtre et l'écran ne clignotera pas. La solution la plus simple consiste à ajouter un SurfaceViewView plain 0px height à la première présentation de votre activité. Cela va recréer la fenêtre avant que l'activité ne soit affichée à l'écran, et lorsque vous définissez votre deuxième mise en page, elle continue simplement à utiliser la fenêtre avec les paramètres actuels. J'espère que ça aide.

UPDATE: après des années, ce comportement est toujours présent. Je recommanderais d'utiliser TextureView au lieu de SurfaceView. Il s'agit littéralement d'une nouvelle implémentation de la même chose qui n'a pas cet effet secondaire et qui n'a pas de problème de fond noir lorsque vous le déplacez (par exemple dans ScrollView, ViewPager, RecyclerView, etc.). 

147
Evos

Je ne le ferais pas avec deux mises en page distinctes. 

Essayez de transformer votre élément racine en RelativeLayout, puis d’avoir les enfants SurfaceView et ImageView comme frères et soeurs. Quoi qu'il arrive avec votre filetage, etc., ImageView doit être dessiné complètement opaque au-dessus de SurfaceView, afin que vous n'ayez pas de flash.

Une fois que votre thread de travail a chargé le dessin et que SurfaceView peut se dessiner, supprimez la barre de progression et ImageView de la hiérarchie des vues.

5
Reuben Scratton

Je suppose que vous dessinez un code lourd dans votre vue de surface car, autant que je sache, la vue de surface affichera la vue complète après avoir tout dessiné une fois. Je vous suggère tout d’abord d’utiliser la méthode onDraw () de visualisation de la surface, puis de la placer sur l’arrière-plan du canevas, puis d’appeler avec invalidité et invalider pour éviter cet écran noir. Ajoutez une condition pour vous assurer que cette invalidation forcée n’est appelée qu’une fois.

2
Tofeeq

Si vous utilisez NavigationDrawer avec des fragments, la solution Evos ne fonctionnera pas!
Essayez d'utiliser NavigationDrawer avec Activités au lieu de fragments, cela vous aidera à 100%.

Comment implémenter NavDrawer avec des activités: link
Un autre utile link . (dans le cas où SurfaceView dessinerait sur SlidingMenu)

1

Vous devez vous assurer que onSurfaceChanged () ne sera pas renvoyé avant d'avoir complètement dessiné et posté le contenu de la surface de SurfaceView.

1
hackbod

Je suis confronté au même problème mais merci pour cette réponse

Il y a une approche moins compliquée, il suffit de mettre getWindow (). setFormat (PixelFormat.TRANSLUCENT); dans l'activité d'accueil Rappel onCreate () avant d'appeler setContentView ().

0
tej shah

"la toile disponible dans onDraw () ne semble pas attirer la SurfaceView" - Êtes-vous sûr de ne pas créer la deuxième (ou troisième ...) instance de la vue Surface? Et certains d'entre eux pourraient être noirs et être montrés pendant une seconde. Je ne vois pas le code ici, donc, je ne peux pas le dire, mais si c'était dans ma candidature, je vérifierais d'abord cette erreur. 

0
Gangnus

La solution de CrazyOrr a fonctionné pour moi, mais elle était profondément dans les commentaires sur la réponse la plus haute d'Evos, alors la voici à nouveau:

Il y a une approche moins compliquée, il suffit de mettre getWindow (). setFormat (PixelFormat.TRANSLUCENT); dans l'activité d'accueil Rappel onCreate () avant d'appeler setContentView (). - CrazyOrr 5 mai '16 à 7h29

Mettre simplement 

getWindow().setFormat(PixelFormat.TRANSLUCENT); 

dans l'activité onCreate() a travaillé pour moi.

0
Björn Kechel