Come posso evitare che la barra di stato e la barra di navigazione si animino durante una transizione di animazione della scena di attività?


127

Innanzitutto, lo sfondo della barra di stato è impostato su marrone scuro e lo sfondo della barra di navigazione è nero predefinito. Sto usando il tema Luce materiale.

Sto iniziando una nuova attività utilizzando ActivityOptions.makeSceneTransitionAnimationcon le transizioni predefinite e noto che sia la barra di stato che quella di navigazione si dissolvono brevemente in bianco e poi ritornano ai colori corretti.

Secondo la documentazione :

Per ottenere l'effetto completo di una transizione, è necessario abilitare le transizioni del contenuto della finestra sia sulle attività chiamanti che chiamate. Altrimenti, l'attività chiamante inizierà la transizione di uscita, ma poi vedrai una transizione di finestra (come ridimensionamento o dissolvenza)

Sto usando getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);su entrambe le attività di chiamata e chiamate.

Allo stesso modo, se cambio la transizione di invio a una diapositiva, sia la barra di stato che quella di navigazione hanno brevemente una transizione di diapositiva con uno sfondo bianco.

Come posso evitare che la barra di stato e la barra di navigazione si animino durante una transizione di animazione della scena di attività?

Risposte:


217

Esistono due approcci che è possibile utilizzare che conosco per impedire che la barra di navigazione / stato si animi durante la transizione:

Approccio n. 1: escludere la barra di stato e la barra di navigazione dalla transizione di dissolvenza in uscita / immissione predefinita della finestra

Il motivo per cui la barra di navigazione / stato si sta dissolvendo in entrata e in uscita durante la transizione è perché per impostazione predefinita tutte le viste non condivise (inclusi gli sfondi della barra di stato / navigazione) svaniranno / entreranno rispettivamente nella chiamata / chiamate Attività una volta iniziata la transizione . Tuttavia, è possibile aggirare facilmente questo problema escludendo gli sfondi della barra di navigazione / di stato dalla Fadetransizione di uscita / invio predefinita della finestra . Aggiungi semplicemente il seguente codice ai onCreate()metodi delle tue attività :

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

Questa transizione potrebbe anche essere dichiarata nel tema dell'attività usando XML (cioè nel tuo res/transition/window_fade.xmlfile):

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

Approccio n. 2: aggiungere la barra di stato e la barra di navigazione come elementi condivisi

Questo approccio si basa sulla risposta di Klmprt, che ha quasi funzionato per me ... anche se avevo ancora bisogno di apportare un paio di modifiche.

Nella mia attività di chiamata, ho utilizzato il seguente codice per avviare l'attività:

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);

Finora questa è essenzialmente la stessa cosa suggerita da Klmprt nella sua risposta. Tuttavia, ho anche dovuto aggiungere il seguente codice nel onCreate()metodo chiamato Activity per evitare che la barra di stato e la barra di navigazione "lampeggino" durante la transizione:

@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'aggiunta della barra di stato e degli sfondi della barra di navigazione come elementi condivisi li costringerà a essere disegnati sopra la transizione di dissolvenza di uscita / entrata predefinita della finestra, il che significa che non si sbiadiranno durante la transizione. Altre discussioni su questo approccio sono disponibili in questo post di Google+ .


14
Qualche idea sul perché findViewById (android.R.id.navigationBarBackground) restituisce null su Lollipop? Sto usando appcompat-v7
radzio

3
L'approccio n. 2 funziona, ma c'è ancora uno sfarfallio (nell'attività) quando si verifica la transizione. la barra di stato e la barra di navigazione non fanno più flickr.
Akshat

16
@alex Questo ha funzionato quasi perfettamente per me fino a quando non siamo passati alla nuova libreria di supporto con android.support.design.widget.TabLayout e android.support.design.widget.AppBarLayout - ora lo sfarfallio è tornato
Noa Drach

6
android.R.id.navigationBarBackground può darti un NPE su dispositivi Samsung o HTC in quanto non hanno una barra di navigazione su schermo. Fai un controllo nullo prima di aggiungerli come elementi condivisi
Ragazzo,

8
Sto provando questa soluzione su Nexus 6 con Android Nougat. Ma entrambi gli approcci non funzionano per me.
Pardeep Kr,

4

Impedisci completamente alle transizioni delle attività di interferire con le transizioni degli elementi condivisi:

Sull'attività in uscita, chiamare getWindow (). SetExitTransition (null);

Sull'attività di immissione, chiamare getWindow (). SetEnterTransition (null);

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

Sospetto che ciò possa avere effetti collaterali, ma non lo so per certo. È morto semplice e funziona però.

Impedire a elementi specifici di lampeggiare:

Ho iniziato con la risposta di Alex Lockwood e ho fatto un bel po 'di sperimentazione per cercare di farlo funzionare. Il nucleo è corretto, anche se non avevo bisogno del codice che suggerisce per l'attività di ricezione, ma ho riscontrato alcuni problemi chiamandolo in un frammento (anziché un'attività) e impostando una barra degli strumenti come barra delle azioni.

Oh, la cosa dei frammenti? Ho visto molti commenti secondo cui il tentativo di recuperare riferimenti alla barra di stato e alla barra di navigazione era nullo. La stessa cosa è successa anche a me, fino a quando ho capito che non avrei trovato quelli nel layout del frammento ... erano al di sopra di quel livello. Quindi, il codice qui sotto per ottenere la vista decor dall'attività e cercarlo. Poi li ho trovati senza problemi.

Alla fine, ho sviluppato questo metodo di utilità:

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();
}

Nota R.string.transition_appbarlayout e R.id.appbarlayout . Questi ID sono arbitrari, purché corrispondano a ciò che utilizza il tuo codice. Nel mio XML, layout la barra delle azioni personalizzata in questo modo (modificata fino all'essenziale):

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

Se non si utilizza una barra degli strumenti come questa, quella parte può essere rimossa dal metodo di utilità.

Quindi lo chiameresti nel tuo frammento in questo modo:

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

Usando qualunque valore tu voglia, purché corrisponda al tuo XML.

Questo impedisce alla barra di stato, alla barra degli strumenti e alla barra di navigazione (pulsanti indietro / home / app recenti) di lampeggiare durante la transizione. Il resto della transizione Attività è normale.

Nel mio caso, il tema della nostra app ha un android:windowBackgroundcolore blu. Ciò provoca un lampo blu nella transizione, il che è abbastanza frustrante. Ma piuttosto che apportare una modifica che influisce sull'intera app in questo modo, per ora vado con la prima opzione, veloce e sporca.


3

Devi condividerli ActivityOptions.makeSceneTransitionAnimation.

Per esempio:

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

(scusa lo psuedo; non ho il valore esatto di android.R.id a portata di mano)

È possibile eseguire una transizione appropriata dopo aver condiviso le viste.


Ha funzionato per te? Ho provato questo e la barra di stato e la barra di navigazione lampeggiano ancora in bianco durante la transizione.
rlay3,

Sì, ha funzionato per me. Sei sicuro che non ci sia un'altra vista che potrebbe sovrapporsi temporaneamente? Hai provato a impostare una semplice transizione di attività in un ambiente "sandboxed" per isolare il problema?
Klmprt,

@klmprt Penso che sia necessario rimandare la transizione di invio per farlo funzionare ... Inoltre, non sono stato in grado di impedire l'animazione delle barre di stato / navigazione semplicemente condividendo le visualizzazioni. Immagino che devi aspettare che la vista decor della finestra finisca il suo layout prima di iniziare la transizione di invio.
Alex Lockwood,

@klmprt Ciò che la mia risposta non affronta ancora è come impedire che il colore di sfondo della barra delle azioni si animi durante la transizione. Se entrambe le attività condividono lo stesso colore di sfondo della barra delle azioni, il colore di sfondo della barra delle azioni sembrerà animarsi man mano che la barra delle attività dell'attività chiamante si attenua gradualmente e la barra delle azioni dell'attività chiamata si dissolve delicatamente. Hai idea di come aggirare questo problema?
Alex Lockwood,

@klmprt In realtà, penso che ci sia una soluzione ancora migliore. È possibile semplicemente escludere gli sfondi della barra di navigazione / di stato come target nella transizione di dissolvenza di uscita / immissione predefinita della finestra. Vedi la mia risposta aggiornata per i dettagli.
Alex Lockwood,

3

Per quanto ho capito, questo è causato dalla sovrapposizione della transizione di attività. Per superare questo problema ho usato i seguenti valori nei onCreate()metodi di entrambe le attività:

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

3

Ho appena avuto lo stesso problema e le risposte sembrano mancare a un pezzo critico del puzzle. Ricorda che durante una transizione di elementi condivisi, tutto accade nell'attività di destinazione .

Per rimuovere l'effetto lampeggiante, è sufficiente aggiungere quanto segue all'attività chiamata:

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

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

Questo dovrebbe risolvere il tuo problema!


Ciao. Non è lo stesso dell'approccio n. 1 nella risposta principale?
rlay3,

@ rlay3 Non del tutto. Per quanto posso dire, non menziona mai il fatto che questo deve essere impostato solo nell'attività di destinazione.
LukeWaggoner l'

hey @LukeWaggoner mi può aiutare questo: stackoverflow.com/questions/50189286/...
Blackhawk

È necessario escludere la barra di stato e la barra di navigazione in entrambe le attività, il chiamante e il chiamante.
Giovanni Di Gregorio,

1

getWindow().setEnterTransition(null); nella transizione di Entering ho rimosso la sovrapposizione bianca per me.


0

Ecco come l'ho fatto. Condivido sia il Status Barche Navigation Baril SharedElementTransitioninsieme a 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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.