3 votes

Comment faire pour que mon superposition en plein écran reste en plein écran après des changements d'orientation?

Je suis en train de créer une application qui crée de petites animations de sprite qui se promènent sur votre écran.

J'ai une activité principale, avec un bouton "démarrer le service". Cela démarre un service, qui (dans onCreate()) crée une vue en plein écran et la attache au gestionnaire de fenêtres principal.

Cette partie fonctionne parfaitement. Elle remplit l'écran, et vous pouvez quitter l'application, et les animations resteront visibles au-dessus de tout.

le rendu initial est correct

Le problème survient lorsque vous tournez l'appareil.

la vue ne tourne pas avec l'appareil

Les sprites se sont déplacés au milieu de l'écran, mais c'est un problème sans rapport; l'important ici est le cadre de délimitation sombre — ce cadre montre le canevas dans lequel je suis autorisé à dessiner, et il doit remplir tout l'écran

La vue est toujours en mode portrait, même si tous les autres agencements semblent s'être mis à jour correctement.


Une partie du problème vient de la manière dont j'ai spécifié les dimensions de la vue. J'ai utilisé des indicateurs pour spécifier "plein écran", mais cela n'a pas réglé la largeur et la hauteur de la vue pour correspondre aux dimensions de l'écran. Ainsi j'ai dû les définir manuellement au démarrage.

Je ne connais pas de moyen de mettre à jour la largeur et la hauteur de la vue une fois que la vue est créée, mais j'ai l'impression que je dois le faire, car les dimensions de la vue déterminent les dimensions du canevas.

Mon service crée la vue de la manière suivante :

public class WalkerService extends Service {
    private WalkerView vue;

    @Override
    public void onCreate() {
        super.onCreate();

        final WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);

        final DisplayMetrics metrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(metrics);

        vue = new WalkerView(this); // s'étend à SurfaceView
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT est refusé dans apiLevel >=19
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                PixelFormat.TRANSLUCENT
        );
        vue.setFitsSystemWindows(false); // nous permet de dessiner par-dessus la barre d'état, la barre de navigation

        // ici je définis _manuellement_ les dimensions, car sinon cela se met par défaut à un carré (quelque chose comme 1024x1024)
        params.width = metrics.widthPixels;
        params.height = metrics.heightPixels;

        wm.addView(vue, params);
    }
}

J'ai essayé d'écouter les changements d'orientation, et de tourner la vue quand cela se produit :

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    vue.setRotation(
            newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
                    ? 90.0f
                    : 0.0f
    );
}

Mais setRotation() ne semblait pas affecter la largeur et la hauteur de ma vue, ni ne changeait l'angle auquel le canevas était rendu.


Suis-je en train de mal comprendre la manière de créer une superposition en plein écran ? Comment puis-je maintenir la capacité de dessiner sur tout l'écran, quelle que soit l'orientation (et même lorsque mon application n'est pas l'application active) ?

Peut-être cela est-il lié au fait que j'attache ma vue au gestionnaire de fenêtres du service Windows — peut-être cela signifie-t-il que ma vue a un parent étrange (je pourrais imaginer que peut-être la vue racine ne serait pas affectée par l'orientation, ou qu'elle n'aurait pas des limites définies pour que les enfants s'étirent pour remplir).

Le code source complet est public sur GitHub au cas où j'aurais omis des informations utiles ici.

WalkerView.java peut être particulièrement pertinent, alors le voici.

1voto

Birchlabs Points 3072

J'ai été induit en erreur par la réponse à une question précédente "Comment créer une activité de superposition en plein écran toujours en haut dans Android". Peut-être que cela fonctionnait pour une ancienne version de l'API? J'utilise l'API niveau 24.

Cette réponse spécifique avait recommandé :

final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
    PixelFormat.TRANSLUCENT
);

Il y a un problème avec cela. Les constructeurs qui existent pour WindowManager.LayoutParams sont les suivants :

enter image description here

Ainsi, le drapeau WindowManager.LayoutParams.FLAG_FULLSCREEN est utilisé comme une valeur explicite pour int w et int h. Ce n'est pas bon !


J'ai trouvé que la construction correcte pour une superposition en plein écran est la suivante :

final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT est refusé dans les apiLevel >=19
    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN,
    PixelFormat.TRANSLUCENT
);

Cela signifie que nous ne spécifions plus explicitement une largeur et une hauteur. La disposition repose entièrement sur nos drapeaux.

Oui, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN est toujours un drapeau requis ; il est nécessaire si vous voulez dessiner par-dessus les décorations comme la barre d'état.

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY devrait être utilisé au lieu de TYPE_SYSTEM_ALERT dans l'API niveau >=19.


Notes supplémentaires (si vous êtes en train de lire ceci, vous essayez probablement de créer une superposition en plein écran) :

Votre manifeste aura besoin des autorisations suivantes (explication ici) :

Je comprends que SYSTEM_ALERT_WINDOW est l'autorisation réellement requise, mais que ACTION_MANAGE_OVERLAY_PERMISSION est également nécessaire : cela vous permet de demander à l'utilisateur à l'exécution d'accorder le privilège SYSTEM_ALERT_WINDOW.

0voto

Henry Points 3697

Je pense que votre situation peut être résolue avec une approche alternative.

  1. Créez une vue personnalisée en plein écran (vous pouvez définir le drapeau approprié dans le constructeur de la vue personnalisée)
  2. Remplacez onSizeChanged() dans la vue personnalisée. Cela vous donnera la hauteur et la largeur du Canvas. Ceci est facultatif. Si vous souhaitez remplir toute la vue personnalisée avec une couleur translucide, cela ne sera pas nécessaire.
  3. Utilisez la largeur et la hauteur ci-dessus, ou simplement utilisez canvas.drawColor() dans le onDraw() de la vue personnalisée.

Cela garantira que chaque fois que la vue est recréée, elle sera redessinée pour remplir tout l'écran (canvas).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X