Wie verhindere ich, dass die Statusleiste und die Navigationsleiste während eines Animationsübergangs einer Aktivitätsszene animiert werden?

127

Erstens ist mein Hintergrund in der Statusleiste auf dunkelbraun eingestellt, und mein Hintergrund in der Navigationsleiste ist standardmäßig schwarz. Ich verwende das Material Light-Thema.

Ich beginne eine neue Aktivität ActivityOptions.makeSceneTransitionAnimationmit Standardübergängen und stelle fest, dass sowohl die Status- als auch die Navigationsleiste kurz in Weiß und dann wieder in die richtigen Farben übergehen.

Laut Dokumentation :

Um die volle Wirkung eines Übergangs zu erzielen, müssen Sie Fensterinhaltsübergänge sowohl für die aufrufenden als auch für die aufgerufenen Aktivitäten aktivieren. Andernfalls startet die aufrufende Aktivität den Exit-Übergang, aber dann sehen Sie einen Fensterübergang (wie Skalieren oder Überblenden).

Ich benutze getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);sowohl die anrufenden als auch die angerufenen Aktivitäten.

Wenn ich den Eingabeübergang in eine Folie ändere, haben sowohl die Status- als auch die Navigationsleiste kurz einen Folienübergang mit weißem Hintergrund.

Wie verhindere ich, dass die Statusleiste und die Navigationsleiste während eines Animationsübergangs einer Aktivitätsszene animiert werden?

rlay3
quelle

Antworten:

217

Es gibt zwei mir bekannte Ansätze, um zu verhindern, dass die Navigations- / Statusleiste während des Übergangs animiert wird:

Ansatz 1: Schließen Sie die Statusleiste und die Navigationsleiste vom Standardübergang zum Beenden / Eingeben des Fensters aus

Der Grund, warum die Navigations- / Statusleiste während des Übergangs ein- und ausgeblendet wird, liegt darin, dass standardmäßig alle nicht freigegebenen Ansichten (einschließlich der Hintergründe der Navigations- / Statusleiste) in Ihren aufrufenden / angerufenen Aktivitäten ausgeblendet werden, sobald der Übergang beginnt . Sie können dies jedoch leicht umgehen, indem Sie die Hintergründe der Navigations- / Statusleiste vom Standardübergang zum Beenden / Eingeben des Fensters ausschließen Fade. Fügen Sie einfach den folgenden Code zu den onCreate()Methoden Ihrer Aktivitäten hinzu :

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

Dieser Übergang kann auch im Thema der Aktivität mithilfe von XML deklariert werden (dh in Ihrer eigenen res/transition/window_fade.xmlDatei):

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

Ansatz 2: Fügen Sie die Statusleiste und die Navigationsleiste als gemeinsam genutzte Elemente hinzu

Dieser Ansatz baut auf der Antwort von klmprt auf, die für mich fast funktioniert hat ... obwohl ich noch einige Änderungen vornehmen musste.

In meiner aufrufenden Aktivität habe ich den folgenden Code verwendet, um die Aktivität zu starten:

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

Bisher ist dies im Wesentlichen dasselbe, was klmprt in seiner Antwort vorgeschlagen hat. Ich musste jedoch auch den folgenden Code in die onCreate()Methode meiner aufgerufenen Aktivität einfügen, um zu verhindern, dass die Statusleiste und die Navigationsleiste während des Übergangs "blinken":

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

Durch Hinzufügen der Statusleiste und der Hintergründe der Navigationsleiste als gemeinsam genutzte Elemente werden sie über den Standardübergang zum Beenden / Eingeben des Fensters gezeichnet, was bedeutet, dass sie während des Übergangs nicht ausgeblendet werden. Weitere Informationen zu diesem Ansatz finden Sie in diesem Google+ Beitrag .

Alex Lockwood
quelle
14
Irgendwelche Ideen, warum findViewById (android.R.id.navigationBarBackground) auf Lollipop null zurückgibt? Ich benutze Appcompat-v7
Radzio
3
Ansatz 2 funktioniert, aber es gibt immer noch ein Flackern (in der Aktivität), wenn der Übergang stattfindet. Die Statusleiste und die Navigationsleiste flickr nicht mehr.
Akshat
16
@alex Das hat bei mir fast perfekt funktioniert, bis wir mit android.support.design.designget.TabLayout und android.support.design.widget.AppBarLayout zur neuen Support-Bibliothek gewechselt sind - jetzt ist das Flackern wieder da
Noa Drach
6
android.R.id.navigationBarBackground kann Ihnen eine NPE auf Samsung- oder HTC-Geräten geben, da diese keine Navigationsleiste auf dem Bildschirm haben.
Boy
8
Ich versuche diese Lösung auf Nexus 6 mit Android Nougat. Aber beide Ansätze funktionieren bei mir nicht.
Pardeep Kr
4

Verhindern Sie vollständig, dass Aktivitätsübergänge die Übergänge gemeinsamer Elemente stören:

Rufen Sie beim Beenden der Aktivität getWindow () auf. SetExitTransition (null);

Rufen Sie bei der Eingabe der Aktivität getWindow () auf. SetEnterTransition (null);

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

Ich vermute, dass dies Nebenwirkungen haben kann, weiß es aber nicht genau. Es ist kinderleicht und funktioniert trotzdem.

Verhindern Sie, dass bestimmte Elemente blinken:

Ich begann mit Alex Lockwoods Antwort und experimentierte ein bisschen, um sie zum Laufen zu bringen. Der Kern davon ist korrekt, obwohl ich den Code, den er für die empfangende Aktivität vorschlägt, nicht benötigte, aber ich stieß auf einige Probleme, indem ich ihn in einem Fragment (anstelle einer Aktivität) aufrief und eine Symbolleiste als Aktionsleiste festlegte.

Oh, das Fragment-Ding? Ich habe viele Kommentare gesehen, dass der Versuch, Verweise auf die Statusleiste und die Navigationsleiste abzurufen, null war. Das gleiche passierte mir auch, bis mir klar wurde, dass ich diese im Layout des Fragments nicht finden würde ... sie lagen über diesem Niveau. Daher den folgenden Code, um die Dekoransicht aus der Aktivität abzurufen und diese zu durchsuchen. Dann fand ich sie ohne Probleme.

Am Ende habe ich diese Dienstprogrammmethode entwickelt:

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

Beachten Sie R.string.transition_appbarlayout und R.id.appbarlayout . Diese IDs sind beliebig, solange sie mit dem übereinstimmen, was Ihr Code verwendet. In meinem XML gestalte ich die benutzerdefinierte Aktionsleiste wie folgt (bis auf das Wesentliche bearbeitet):

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

Wenn Sie eine solche Symbolleiste nicht verwenden, kann dieser Teil aus der Dienstprogrammmethode entfernt werden.

Dann würden Sie es in Ihrem Fragment so nennen:

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

Verwenden Sie die gewünschten Werte, sofern diese mit Ihrem XML übereinstimmen.

Dadurch wird verhindert, dass die Statusleiste, die Symbolleiste und die Navigationsleiste (Schaltflächen Zurück / Startseite / Letzte Apps) während des Übergangs blinken. Der Rest des Aktivitätsübergangs ist normal.

In meinem Fall hat unser App-Thema ein android:windowBackgroundBlau. Dies verursacht einen blauen Blitz im Übergang, was ziemlich frustrierend ist. Aber anstatt eine Änderung vorzunehmen, die sich auf die gesamte App auswirkt, gehe ich jetzt zur ersten, schnellen und schmutzigen Option.

Chad Schultz
quelle
3

Sie müssen sie teilen ActivityOptions.makeSceneTransitionAnimation.

Z.B:

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

(Entschuldigen Sie das Pseudo; ich habe nicht den genauen Wert für android.R.id zur Hand)

Sie können einen entsprechenden Übergang ausführen, nachdem Sie die Ansichten freigegeben haben.

klmprt
quelle
Hat das für dich funktioniert? Ich habe es versucht und die Statusleiste und die Navigationsleiste blinken während des Übergangs immer noch weiß.
Rlay3
Ja, es hat bei mir funktioniert. Sind Sie sicher, dass es keine andere Ansicht gibt, die sie vorübergehend überlappen könnte? Haben Sie versucht, einen einfachen Aktivitätsübergang in einer Sandbox-Umgebung einzurichten, um das Problem einzugrenzen?
klmprt
@klmprt Ich denke, Sie müssen auch den Eingabeübergang verschieben, damit er funktioniert ... Ich konnte auch nicht verhindern, dass die Status- / Navigationsleisten animiert werden, indem Sie einfach die Ansichten teilen. Ich denke, Sie müssen warten, bis die Dekoransicht des Fensters fertig ist, bevor Sie den Eingabeübergang beginnen lassen.
Alex Lockwood
@klmprt Was meine Antwort jedoch immer noch nicht anspricht, ist, wie verhindert werden kann, dass die Hintergrundfarbe der Aktionsleiste während des Übergangs animiert wird. Wenn beide Aktivitäten dieselbe Hintergrundfarbe für die Aktionsleiste haben, wird die Hintergrundfarbe der Aktionsleiste animiert, wenn die Aktionsleiste der aufrufenden Aktivität allmählich ausgeblendet wird und die Aktionsleiste der aufgerufenen Aktivität sanft ausgeblendet wird. Haben Sie eine Idee, wie Sie dies umgehen können? Problem?
Alex Lockwood
@klmprt Eigentlich denke ich, dass es eine noch bessere Lösung gibt. Sie können die Hintergründe der Navigations- / Statusleiste einfach als Ziele im Standardübergang zum Beenden / Eingeben des Fensters ausschließen. Siehe meine aktualisierte Antwort für Details.
Alex Lockwood
3

Soweit ich weiß, wird dies durch Überschneidungen des Aktivitätsübergangs verursacht. Um dieses Problem zu beheben, habe ich in den onCreate()Methoden beider Aktivitäten die folgenden Werte verwendet :

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

Ich hatte gerade das gleiche Problem und den Antworten scheint ein kritischer Teil des Puzzles zu fehlen. Denken Sie daran, dass bei einem Übergang mit gemeinsam genutzten Elementen alles in der Zielaktivität geschieht .

Um den Blitzeffekt zu entfernen, fügen Sie der aufgerufenen Aktivität einfach Folgendes hinzu:

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

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

Dies sollte Ihr Problem lösen!

LukeWaggoner
quelle
Hallo. Ist das nicht dasselbe wie Ansatz 1 in der oberen Antwort?
Rlay3
@ rlay3 Nicht ganz. Soweit ich das beurteilen kann, erwähnt er nie die Tatsache, dass dies nur in der Zielaktivität festgelegt werden muss.
LukeWaggoner
hey @LukeWaggoner könntest du mir dabei helfen: stackoverflow.com/questions/50189286/…
blackHawk
Sie müssen die Statusleiste und die Navigationsleiste in beiden Aktivitäten, dem Anrufer und dem Angerufenen, ausschließen.
Giovanni Di Gregorio
1

getWindow().setEnterTransition(null); Beim Übergang Eingabe wurde die weiße Überlagerung für mich entfernt.

Dominic Davies
quelle
0

Hier ist, wie ich es gemacht habe. Ich teile sowohl das Status Barals auch das Navigation Barin der SharedElementTransitionzusammen mit einem 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
quelle