web-dev-qa-db-fra.com

Comment empêcher l'animation de la barre d'état et de la barre de navigation lors d'une transition d'animation de scène d'activité?

Premièrement, l’arrière-plan de la barre d’état est brun foncé et l’arrière-plan de la barre de navigation est noir par défaut. J'utilise le thème Lumière matérielle.

Je commence une nouvelle activité en utilisant ActivityOptions.makeSceneTransitionAnimation Avec des transitions par défaut et je remarque que les barres d'état et de navigation se fondent brièvement en blanc puis reviennent aux couleurs correctes.

Selon le documentation :

Pour obtenir le plein effet d'une transition, vous devez activer les transitions de contenu de fenêtre sur les activités appelantes et appelées. Sinon, l'activité d'appel démarrera la transition de sortie, mais vous verrez alors une transition de fenêtre (telle que scale ou fade).

J'utilise getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); sur les activités appelantes et appelées.

De même, si je modifie la transition d'entrée en diapositive, les barres d'état et de navigation ont brièvement une transition de diapositive avec un arrière-plan blanc.

Comment empêcher l'animation de la barre d'état et de la barre de navigation lors d'une transition d'animation de scène d'activité?

123
rlay3

Je sais que vous pouvez utiliser deux approches pour empêcher l'animation de la barre de navigation/d'état pendant la transition:

Approche n ° 1: Exclure la barre d'état et la barre de navigation de la transition par défaut de la fenêtre pour la transition en fondu.

La barre de navigation/d'état apparaît et disparaît progressivement pendant la transition, car, par défaut, toutes les vues non partagées (y compris les arrière-plans de la barre de navigation/d'état) disparaîtront/apparaîtront dans vos activités appelantes/appelées une fois la transition commencée. . Vous pouvez toutefois facilement contourner ce problème en excluant les arrière-plans de la barre de navigation/la barre d'état de la transition par défaut de la fenêtre exit/enter Fade. Ajoutez simplement le code suivant à vos méthodes onCreate() de Activitys:

Transition fade = new Fade();
fade.excludeTarget(Android.R.id.statusBarBackground, true);
fade.excludeTarget(Android.R.id.navigationBarBackground, true);
getWindow().setExitTransition(fade);
getWindow().setEnterTransition(fade);

Cette transition peut également être déclarée dans le thème de l'activité à l'aide de XML (c'est-à-dire dans votre propre fichier res/transition/window_fade.xml):

<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <targets>
        <target Android:excludeId="@Android:id/statusBarBackground"/>
        <target Android:excludeId="@Android:id/navigationBarBackground"/>
    </targets>
</fade>

Approche n ° 2: Ajouter la barre d'état et la barre de navigation en tant qu'éléments partagés

Cette approche découle de la réponse de klmprt, qui a presque fonctionné a fonctionné pour moi ... bien que je doive encore apporter quelques modifications.

Dans mon activité d'appel, j'ai utilisé le code suivant pour démarrer l'activité:

View statusBar = findViewById(Android.R.id.statusBarBackground);
View navigationBar = findViewById(Android.R.id.navigationBarBackground);

List<Pair<View, String>> pairs = new ArrayList<>();
if (statusBar != null) {
  pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
}
if (navigationBar != null) {
  pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
}
pairs.add(Pair.create(mSharedElement, mSharedElement.getTransitionName()));

Bundle options = ActivityOptions.makeSceneTransitionAnimation(activity, 
        pairs.toArray(new Pair[pairs.size()])).toBundle();
startActivity(new Intent(context, NextActivity.class), options);

Jusqu'à présent, c’est essentiellement la même chose que klmprt a suggéré dans sa réponse. Cependant, je devais également ajouter le code suivant dans la méthode onCreate() de mon activité appelée, afin d'empêcher la barre d'état et la barre de navigation de "clignoter" pendant la transition:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_next);

    // Postpone the transition until the window's decor view has
    // finished its layout.
    postponeEnterTransition();

    final View decor = getWindow().getDecorView();
    decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            decor.getViewTreeObserver().removeOnPreDrawListener(this);
            startPostponedEnterTransition();
            return true;
        }
    });
}

L'ajout d'arrière-plans de la barre d'état et de la barre de navigation en tant qu'éléments partagés les forcera à être dessinés au-dessus de la transition de fondu sortant/entrant par défaut de la fenêtre, ce qui signifie qu'ils ne s'effaceront pas pendant la transition. Vous trouverez plus d'informations sur cette approche dans cet article Google+ .

210
Alex Lockwood

Empêchez complètement les transitions d'activité d'interférer avec les transitions d'éléments partagés:

Sur l'activité existante, appelez getWindow (). SetExitTransition (null);

Dans l'activité en entrée, appelez getWindow (). SetEnterTransition (null);

De https://stackoverflow.com/a/34907685/967131

Je soupçonne que cela peut avoir des effets secondaires, mais je ne le sais pas avec certitude. C'est très simple et ça marche bien.

Empêche le clignotement d'éléments spécifiques:

J'ai commencé avec réponse d'Alex Lockwood et j'ai fait pas mal d'expérimentation pour tenter de le faire fonctionner. Le noyau est correct, bien que je n’aie pas besoin du code qu’il suggère pour l’activité de réception, mais j’ai rencontré quelques problèmes en l’appelant dans un fragment (au lieu d’une activité) et en définissant une barre d’outils comme barre d’action.

Oh, le fragment? J'ai vu beaucoup de commentaires indiquant que les tentatives de récupération des références à la barre d'état et à la barre de navigation étaient nulles. La même chose m'est arrivé aussi, jusqu'à ce que je réalise que je ne trouverais pas ceux dans la disposition du fragment… ils étaient au dessus de ce niveau. Par conséquent, le code ci-dessous permet d’obtenir la vue du décor de l’activité et de la rechercher. Ensuite, je les ai trouvés sans problème.

Finalement, j'ai développé cette méthode utilitaire:

public static Bundle transitionOptions(Activity activity, int transitionViewResId, int transitionNameResId) {
   if (VERSION.SDK_INT < VERSION_CODES.Lollipop) {
       return null;
   }

   View decorView = activity.getWindow().getDecorView();
   View statusBar = decorView.findViewById(Android.R.id.statusBarBackground);
   View navigationBar = decorView.findViewById(Android.R.id.navigationBarBackground);
   View appBarLayout = decorView.findViewById(**R.id.appbarlayout**);
   View transitionView = decorView.findViewById(transitionViewResId);
   String transitionName = activity.getString(transitionNameResId);

   List<Pair<View, String>> pairs = new ArrayList<>();
   pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
   pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
   if (appBarLayout != null) {
       pairs.add(Pair.create(appBarLayout, activity.getString(**R.string.transition_appbarlayout**)));
   }
   pairs.add(Pair.create(transitionView, transitionName));
   //noinspection unchecked - we're not worried about the "unchecked" conversion of List<Pair> to Pair[] here
   return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs.toArray(new Pair[pairs.size()]))
           .toBundle();
}

Remarque R.string.transition_appbarlayout et R.id.appbarlayout . Ces identifiants sont arbitraires, à condition qu'ils correspondent à ceux utilisés par votre code. Dans mon XML, je mets en forme la barre d’actions personnalisée de la manière suivante (édité jusqu’à l'essentiel):

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.AppBarLayout
    Android:id="**@+id/appbarlayout**"
    Android:transitionName="**@string/transition_appbarlayout**">

    <Android.support.v7.widget.Toolbar
        Android:id="@+id/toolbar"/>
</Android.support.design.widget.AppBarLayout>

Si vous n'utilisez pas une barre d'outils comme celle-ci, vous pouvez supprimer cette partie de la méthode de l'utilitaire.

Ensuite, vous l'appelez dans votre fragment comme suit:

startActivity(intent, UIUtils.transitionOptions(getActivity(),
                        R.id.**my_view**,
                        R.string.**transition_my_view**));

En utilisant les valeurs que vous voulez, tant que cela correspond à votre XML.

Cela empêche la barre d'état, la barre d'outils et la barre de navigation (boutons Précédent/Domicile/Applications récentes) de clignoter pendant la transition. Le reste de la transition d'activité est normal.

Dans mon cas, notre thème d'application a un Android:windowBackground de bleu. Cela provoque un flash bleu dans la transition, ce qui est assez frustrant. Mais plutôt que d'apporter un changement qui affecte l'ensemble de l'application de la sorte, je vais pour le moment avec la première option, rapide et sale.

4
Chad Schultz

Autant que je sache, cela est dû au chevauchement de la transition d'activité. Pour résoudre ce problème, j'ai utilisé les valeurs suivantes dans les méthodes onCreate() des deux activités:

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
3
Randeep

Vous devez les partager en ActivityOptions.makeSceneTransitionAnimation.

Par exemple:

ActivityOptions.makeSceneTransitionAnimation(... Pair.create(activity.findViewById(Android.R.id.window_status_bar), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME) 

(excusez le pseudo; je n'ai pas la valeur Android.R.id exacte sous la main)

Vous pouvez exécuter une transition appropriée après avoir partagé les vues.

3
klmprt

Je viens d'avoir ce même problème, et les réponses semblent manquer un élément essentiel du puzzle. N'oubliez pas que lors d'une transition d'élément partagé, tout se passe dans le activité de destination.

Afin de supprimer l'effet clignotant, ajoutez simplement ce qui suit à l'activité appelée:

Fade fade = new Fade();
fade.excludeTarget(Android.R.id.statusBarBackground, true);
fade.excludeTarget(Android.R.id.navigationBarBackground, true);

getWindow().setEnterTransition(fade);
getWindow().setExitTransition(fade);

Cela devrait résoudre votre problème!

2
LukeWaggoner

getWindow().setEnterTransition(null); sur la transition d'entrée a supprimé la superposition blanche pour moi.

1
Dominic Davies

Voici comment je l'ai fait. Je partage à la fois le Status Bar et le Navigation Bar dans SharedElementTransition avec un ImageView:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
  View imageView = view.findViewById(R.id.iv);
  Resources resources = view.getResources();
  imageView.setTransitionName(resources.getString(R.string.transition_image_thumbnail));

  Pair<View, String> p1 = Pair.create(imageView, resources.getString(R.string.transition_image_thumbnail));

  Window window = getActivity().getWindow();

  View navigationBar = getActivity().findViewById(Android.R.id.navigationBarBackground);
  View statusBar = getActivity().findViewById(Android.R.id.statusBarBackground);

  Pair<View, String> p2 = Pair.create(statusBar, statusBar.getTransitionName());
  Pair<View, String> p3 = Pair.create(navigationBar, navigationBar.getTransitionName());

  ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
          p1, p2, p3);

  ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
} else {
  startActivity(intent);
}
0
toobsco42