Jak zapobiec animacji paska stanu i paska nawigacji podczas przejścia do animacji sceny działania?

127

Po pierwsze, tło mojego paska stanu jest ustawione na ciemnobrązowy, a tło mojego paska nawigacji jest domyślnie czarne. Używam motywu Material light.

Rozpoczynam nową czynność, używając ActivityOptions.makeSceneTransitionAnimationdomyślnych przejść i zauważam, że zarówno pasek stanu, jak i pasek nawigacji na krótko stają się białe, a następnie wracają do właściwych kolorów.

Zgodnie z dokumentacją :

Aby uzyskać pełny efekt przejścia, należy włączyć przejścia zawartości okna zarówno dla wywołań, jak i wywoływanych działań. W przeciwnym razie działanie wywołujące rozpocznie przejście wyjściowe, ale wtedy zobaczysz przejście okna (takie jak skala lub zanikanie)

Używam getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);zarówno czynności wywołujących, jak i wywoływanych.

Podobnie, jeśli zmienię przejście Enter na slajd, zarówno pasek stanu, jak i pasek nawigacji mają na chwilę przejście między slajdami na białym tle.

Jak zapobiec animacji paska stanu i paska nawigacji podczas przejścia do animacji sceny działania?

rlay3
źródło

Odpowiedzi:

217

Istnieją dwa znane mi podejścia, które pozwalają zapobiec animacji paska nawigacji / stanu podczas przejścia:

Podejście nr 1: Wyklucz pasek stanu i pasek nawigacji z domyślnego przejścia w oknie wyjścia / wejścia

Powodem, dla którego pasek nawigacji / stanu zanika i pojawia się podczas przejścia, jest to, że domyślnie wszystkie nieudostępnione widoki (w tym tła paska nawigacji / stanu) znikają / znikają w wywołanych / wywołanych działaniach odpowiednio po rozpoczęciu przejścia . Możesz jednak łatwo obejść ten problem, wykluczając tła paska nawigacji / stanu z domyślnego Fadeprzejścia okna . Po prostu dodaj następujący kod do onCreate()metod swoich działań :

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

To przejście można również zadeklarować w motywie działania za pomocą XML (tj. We własnym res/transition/window_fade.xmlpliku):

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

Podejście nr 2: Dodaj pasek stanu i pasek nawigacji jako elementy wspólne

To podejście wywodzi się z odpowiedzi klmprt, która prawie mi się sprawdziła ... chociaż nadal potrzebowałem wprowadzić kilka modyfikacji.

W moim działaniu wywołującym użyłem następującego kodu, aby rozpocząć działanie:

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

Jak dotąd jest to w zasadzie to samo, co sugerował klmprt w swojej odpowiedzi. Jednak musiałem również dodać następujący kod w mojej wywołanej onCreate()metodzie Activity , aby zapobiec „miganiu” paska stanu i paska nawigacji podczas przejścia:

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

Dodanie paska stanu i tła paska nawigacji jako elementów współdzielonych wymusi ich rysowanie na górze domyślnego przejścia okna wyjściowego / wejścia, co oznacza, że ​​nie będą one zanikać podczas przejścia. Więcej dyskusji na temat tego podejścia można znaleźć w tym poście Google+ .

Alex Lockwood
źródło
14
Jakieś pomysły, dlaczego findViewById (android.R.id.navigationBarBackground) zwraca wartość null na Lollipop? Używam appcompat-v7
radzio
3
Podejście nr 2 działa, ale nadal pojawia się migotanie (w ćwiczeniu), gdy następuje przejście. pasek stanu i pasek nawigacyjny już nie migają.
Akshat,
16
@alex To działało prawie idealnie dla mnie, dopóki nie przełączyliśmy się na nową bibliotekę wsparcia z android.support.design.widget.TabLayout i android.support.design.widget.AppBarLayout - teraz migotanie powróciło
Noa Drach
6
android.R.id.navigationBarBackground może zapewnić NPE na urządzeniach Samsung lub HTC, ponieważ nie mają one paska nawigacji na ekranie. Sprawdź zerową wartość przed dodaniem ich jako elementów wspólnych
Boy
8
Próbuję tego rozwiązania na Nexusie 6 z Androidem Nougat. Ale oba podejścia nie działają dla mnie.
Pardeep Kr
4

Całkowicie zapobiegaj zakłócaniu przejść działań przez przejścia elementów współdzielonych:

W przypadku wychodzącego działania wywołaj getWindow (). SetExitTransition (null);

Na wprowadzanej czynności wywołaj getWindow (). SetEnterTransition (null);

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

Podejrzewam, że może to mieć skutki uboczne, ale nie wiem na pewno. To jest śmiertelnie proste i działa.

Zapobiegaj migotaniu określonych elementów:

Zacząłem od odpowiedzi Alexa Lockwooda i przeprowadziłem sporo eksperymentów, aby to zadziałało. Rdzeń jest poprawny, chociaż nie potrzebowałem kodu, który sugeruje dla działania odbierającego, ale napotkałem pewne problemy, wywołując go we fragmencie (zamiast działania) i ustawiając pasek narzędzi jako pasek akcji.

Och, sprawa Fragmentów? Widziałem wiele komentarzy, że próby pobrania odniesień do paska stanu i paska nawigacji były zerowe. To samo przydarzyło się mnie, dopóki nie zdałem sobie sprawy, że nie znajdę tych w układzie Fragmentu ... były powyżej tego poziomu. Stąd poniższy kod, aby uzyskać widok dekoru z działania i przeszukać go. Wtedy znalazłem je bez problemu.

Na koniec opracowałem tę metodę użytkową:

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

Uwaga R.string.transition_appbarlayout i R.id.appbarlayout . Te identyfikatory są dowolne, o ile są zgodne z tym, czego używa Twój kod. W moim XMLu układam niestandardowy pasek akcji w następujący sposób (zredagowany do podstawowych elementów):

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

Jeśli nie używasz takiego paska narzędzi, ta część może zostać usunięta z metody narzędzia.

Następnie nazwałbyś to w swoim fragmencie w ten sposób:

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

Używanie dowolnych wartości, o ile pasuje do twojego XML.

Zapobiega to miganiu paska stanu, paska narzędzi i paska nawigacji (przyciski Wstecz / Strona główna / Ostatnie aplikacje) podczas przejścia. Pozostała część przejścia aktywności jest normalna.

W moim przypadku motyw naszej aplikacji ma android:windowBackgroundkolor niebieski. Powoduje to niebieski błysk w przejściu, co jest dość frustrujące. Ale zamiast dokonywać zmiany, która ma wpływ na całą aplikację, na razie wybieram pierwszą, szybką i brudną opcję.

Chad Schultz
źródło
3

Musisz je udostępnić ActivityOptions.makeSceneTransitionAnimation.

Na przykład:

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

(przepraszam pseudo; nie mam dokładnej wartości android.R.id pod ręką)

Po udostępnieniu widoków możesz uruchomić odpowiednie przejście.

klmprt
źródło
Czy to zadziałało dla Ciebie? Próbowałem tego i pasek stanu i pasek nawigacji nadal migają na biało podczas przejścia.
rlay3
Tak, na mnie zadziałało. Czy na pewno nie ma innego widoku, który mógłby je tymczasowo nakładać? Czy próbowałeś skonfigurować proste przejście między działaniami w środowisku „piaskownicy”, aby wyodrębnić problem?
klmprt
@klmprt Myślę, że musisz również odłożyć przejście na wejście, aby działało ... Nie byłem też w stanie zapobiec animacji pasków statusu / nawigacji po prostu udostępniając widoki. Wydaje mi się, że musisz poczekać, aż widok dekoru okna zakończy układ, zanim zaczniesz wprowadzać przejście.
Alex Lockwood,
@klmprt Jednak moja odpowiedź wciąż nie dotyczy tego, jak zapobiec animacji koloru tła paska akcji podczas przejścia. Jeśli oba działania mają ten sam kolor tła paska akcji, kolor tła paska akcji będzie się animował, gdy pasek akcji wywołującego działania stopniowo zanika, a pasek akcji nazwanego działania delikatnie znika. Czy masz jakiś pomysł, jak to obejść kwestia?
Alex Lockwood,
@klmprt Właściwie myślę, że jest jeszcze lepsze rozwiązanie. Możesz po prostu wykluczyć tła paska nawigacji / stanu jako cele w domyślnym przejściu okna wyjścia / wejścia. Zobacz moją zaktualizowaną odpowiedź, aby uzyskać szczegółowe informacje.
Alex Lockwood
3

O ile rozumiem, jest to spowodowane nakładaniem się aktywności. Aby rozwiązać ten problem, zastosowałem następujące wartości w onCreate()metodach obu działań:

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
Randeep
źródło
3

Po prostu miałem ten sam problem i wydaje się, że w odpowiedziach brakuje kluczowego elementu układanki. Pamiętaj, że przy przejściu elementu współdzielonego wszystko dzieje się w działaniu docelowym .

Aby usunąć efekt flashowania, po prostu dodaj następujący wpis do wywoływanej czynności:

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

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

To powinno rozwiązać Twój problem!

LukeWaggoner
źródło
Cześć. Czy to nie to samo, co Podejście nr 1 w pierwszej odpowiedzi?
rlay3
@ rlay3 Nie do końca. O ile wiem, nigdy nie wspomina, że ​​należy to ustawić tylko w czynności docelowej.
LukeWaggoner
hej @LukeWaggoner, czy możesz mi w tym pomóc: stackoverflow.com/questions/50189286/ ...
blackHawk
Należy wykluczyć pasek stanu i pasek nawigacji w obu działaniach, dzwoniącym i wywoływanym.
Giovanni Di Gregorio,
1

getWindow().setEnterTransition(null); w przejściu wchodzącym usunąłem dla mnie białą nakładkę.

Dominic Davies
źródło
0

Oto jak to zrobiłem. Dzielę się zarówno z, jak Status Bari Navigation Barw SharedElementTransitionwraz z 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);
}
toobsco42
źródło