Animuj zmiany układu za pomocą przejścia

Wypróbuj sposób tworzenia wiadomości
Jetpack Compose to zalecany zestaw narzędzi UI na Androida. Dowiedz się, jak korzystać z animacji w Compose

Struktura przejścia Androida pozwala animować każdy ruch z interfejsem, podając układ początkowy i końcowy. Możesz wybrać rodzaj animacji, np. zanikanie lub pomniejszenia, a także rozmiaru widoku. Proces przejścia określa jak animować układ od układu początkowego do końcowego.

Proces przejścia obejmuje te funkcje:

  • Animacje na poziomie grupy: stosować efekty animacji do wszystkich widoków w hierarchii widoków.
  • Wbudowane animacje: korzysta ze wstępnie zdefiniowanych animacji w celu uzyskania typowych efektów, takich jak rozjaśnienie czy ruch.
  • Obsługa pliku zasobów: wczytuj hierarchie widoków i wbudowane animacje z plików zasobów układu.
  • Wywołania zwrotne cyklu życia: otrzymywanie wywołań zwrotnych, które zapewniają kontrolę nad animacją i hierarchią; proces zmiany.

Przykładowy kod animowany między zmianami układu: Podstawowe przejście.

Podstawowy proces animacji między 2 układami wygląda tak:

  1. Utwórz obiekt Scene dla układ początkowy i końcowy. Początkowa scena jest jednak często określane automatycznie na podstawie bieżącego układu.
  2. Tworzenie Transition aby zdefiniować odpowiedni typ animacji.
  3. Zadzwoń do nas TransitionManager.go() a system uruchamia animację, aby zamienić układy.

Diagram na ilustracji 1 pokazuje zależności między układami. sceny, przejścia i końcową animację.

Rysunek 1. Podstawowa ilustracja przedstawiająca jak platforma przejścia tworzy animację.

Tworzenie sceny

Sceny przechowują stan hierarchii widoków, w tym wszystkie jej widoki i ich wartości właściwości. Platforma przejść może uruchamiać animacje między i scenę końcową.

Możesz tworzyć sceny z poziomu układu lub z grupy widoków w kodzie. Jednak scena początkowa przejścia jest często określana automatycznie na podstawie obecny interfejs użytkownika.

Scena może też określać własne działania, które są wykonywane po wprowadzeniu jej zmiany. Ta funkcja jest przydatna do wyczyszczenia ustawień widoku po przejście do określonej sceny.

Tworzenie sceny z zasobu układu

Możesz utworzyć instancję Scene bezpośrednio z zasobu szablonu . Ta metoda jest przydatna, gdy hierarchia widoków w pliku jest w większości statyczna. Powstała scena przedstawia stan hierarchii widoków w momencie, utworzono instancję Scene. Jeśli zmienisz hierarchię widoków, odtworzyć scenę. Platforma tworzy scenę z całego widoku w danej hierarchii. Nie można utworzyć sceny z części pliku układu.

Aby utworzyć instancję Scene z pliku zasobów układu, pobierz główny element sceny z układu jako ViewGroup Następnie wywołaj funkcję Scene.getSceneForLayout() z katalogiem głównym sceny i identyfikatorem zasobu pliku układu, który zawiera hierarchię widoku sceny.

Definiowanie układów scen

Fragmenty kodu znajdujące się w pozostałej części tej sekcji pokazują, jak utworzyć dwa różnych scen z tym samym elementem głównym sceny. Fragmenty kodu pokazują też że możesz wczytać wiele niepowiązanych obiektów Scene, nie sugerując, że są ze sobą powiązane.

Przykład składa się z tych definicji układu:

  • Główny układ aktywności z etykietą tekstową i elementem podrzędnym FrameLayout
  • ConstraintLayout dla pierwszą scenę z dwoma polami tekstowymi.
  • ConstraintLayout dla drugiej sceny z tymi samymi 2 polami tekstowymi w w innej kolejności.

Przykład jest zaprojektowany tak, aby cała animacja odbywała się w elemencie podrzędnym. układ głównego układu aktywności. Etykieta tekstowa w układzie głównym. nie zmienia się.

Główny układ aktywności jest zdefiniowany tak:

res/layout/activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/master_layout">
    <TextView
        android:id="@+id/title"
        ...
        android:text="Title"/>
    <FrameLayout
        android:id="@+id/scene_root">
        <include layout="@layout/a_scene" />
    </FrameLayout>
</LinearLayout>

Ta definicja układu zawiera pole tekstowe i element podrzędny FrameLayout dla elementu element główny sceny. Układ pierwszej sceny znajduje się w głównym pliku układu. Dzięki temu aplikacja może wyświetlać ją jako część początkowego interfejsu użytkownika, a także ją wczytywać. go w scenie, ponieważ platforma może załadować tylko cały plik układu scena.

Układ pierwszej sceny jest zdefiniowany tak:

res/layout/a_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    
</androidx.constraintlayout.widget.ConstraintLayout>

Układ drugiej sceny zawiera te same 2 pola tekstowe – w tej samej kolejności, ale w innej kolejności. Jest on zdefiniowany w ten sposób:

res/layout/inna_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    
</androidx.constraintlayout.widget.ConstraintLayout>

Generuj sceny na podstawie układów

Po utworzeniu definicji dla dwóch układów ograniczeń możesz uzyskać dla każdej z nich. Umożliwia to przełączanie się między dwoma interfejsami. konfiguracji. Aby uzyskać scenę, musisz podać jej źródło i układ identyfikatora zasobu.

Ten fragment kodu pokazuje, jak uzyskać odniesienie do źródła sceny utwórz 2 obiekty Scene z plików układu:

Kotlin

val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this)

Java

Scene aScene;
Scene anotherScene;

// Create the scene root for the scenes in this app.
sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

// Create the scenes.
aScene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this);
anotherScene =
    Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this);

W aplikacji są teraz 2 obiekty Scene oparte na widoku hierarchie. Obie sceny używają rdzenia sceny zdefiniowanego przez tag FrameLayout element w res/layout/activity_main.xml.

Tworzenie sceny w kodzie

Możesz też utworzyć w kodzie wystąpienie Scene na podstawie ViewGroup obiekt. Użyj tej metody do modyfikowania hierarchii widoków bezpośrednio w kodzie lub gdy generujesz je dynamicznie.

Aby utworzyć scenę na podstawie hierarchii widoków w kodzie, użyj funkcji Scene(sceneRoot, viewHierarchy) za pomocą konstruktora. Wywołanie tego konstruktora jest równoważne wywołaniu funkcji Scene.getSceneForLayout() , gdy plik układu został już uzupełniony.

Fragment kodu poniżej pokazuje, jak utworzyć Scene z poziomu głównego elementu sceny i hierarchii widoku sceny w Twój kod:

Kotlin

val sceneRoot = someLayoutElement as ViewGroup
val viewHierarchy = someOtherLayoutElement as ViewGroup
val scene: Scene = Scene(sceneRoot, viewHierarchy)

Java

Scene mScene;

// Obtain the scene root element.
sceneRoot = (ViewGroup) someLayoutElement;

// Obtain the view hierarchy to add as a child of
// the scene root when this scene is entered.
viewHierarchy = (ViewGroup) someOtherLayoutElement;

// Create a scene.
mScene = new Scene(sceneRoot, mViewHierarchy);

Tworzenie działań dotyczących sceny

Platforma umożliwia zdefiniowanie niestandardowych działań związanych ze scenami, które system uruchamia, gdy przejście do następnego punktu widokowego lub wyjście z niego. W wielu przypadkach określenie działań na scenie niestandardowych jest jest niepotrzebne, bo struktura animuje przejście między scenami automatycznie.

Czynności wykonywane na ekranie przydają się w takich przypadkach:

  • Aby animować widoki, które nie należą do tej samej hierarchii Możesz animować widoki dla: między scenami początkowymi i końcowymi za pomocą działań wyjścia i wejścia.
  • Aby animować widoki, których struktura przejść nie może animować się automatycznie, takich jak ListView. Więcej informacje znajdziesz w sekcji o ograniczeniach.

Aby podać niestandardowe działania związane ze scenami, określ je jako Runnable obiektów i przekazać je do funkcji Scene.setExitAction() lub Scene.setEnterAction() funkcji. Platforma wywołuje funkcję setExitAction() na początku scenę przed uruchomieniem animacji przejścia i elementu setEnterAction() na scenie końcowej po uruchomieniu animacji przejścia.

Zastosuj przejście

Schemat przejścia reprezentuje styl animacji między scenami i Transition obiekt. Możesz utworzyć instancję Transition za pomocą wbudowanych podklasy, takie jak AutoTransition i Fade lub zdefiniować własne przejście. Następnie możesz uruchomić animacja między scenami, przechodząc przez koniec Scene oraz Transition na TransitionManager.go()

Cykl przejścia jest podobny do cyklu życia aktywności i przedstawia jest to stan przejścia między elementem początkowym, a zarazem monitorowanym przez platformę po zakończeniu animacji. W ważnych stanach cyklu życia platforma wywołuje które można wdrożyć w celu dostosowania interfejsu na różnych etapach przejścia.

Tworzenie przejścia

W poprzedniej sekcji pokazujemy, jak tworzyć sceny odzwierciedlające stan różne hierarchie widoków. Po zdefiniowaniu sceny początkowej i końcowej chcesz się przełączyć, utwórz obiekt Transition z definicją animacji. Platforma umożliwia określenie wbudowanego przejścia w pliku zasobów i powiększania go w kodzie lub utworzyć wystąpienie wbudowanego przejścia. bezpośrednio w kodzie.

Tabela 1. Wbudowane typy przejść.

Kategoria Oznacz Efekt
AutoTransition <autoTransition/> Przejście domyślne. Zanikanie, przesuwanie, zmienianie rozmiaru i zanikanie w widoku w tej kolejności.
ChangeBounds <changeBounds/> Przenosi widoki i zmienia ich rozmiar.
ChangeClipBounds <changeClipBounds/> Rejestruje View.getClipBounds() przed sceną i po niej i animuje te zmiany podczas przejścia.
ChangeImageTransform <changeImageTransform/> Rejestruje macierz elementu ImageView przed sceną i po niej i animuje go podczas przejścia.
ChangeScroll <changeScroll/> Przechwytuje właściwości przewijania elementów docelowych przed sceną i po niej i animuje wszystkie zmiany.
ChangeTransform <changeTransform/> Rejestruje skalę i obrót widoków przed zmianą sceny i po niej i animuje te zmiany podczas przejścia.
Explode <explode/> Śledzi zmiany widoczności widoków docelowych na początku i na końcu i przesuwa widok poza krawędź sceny.
Fade <fade/> fade_in zmniejsza liczbę wyświetleń.
fade_out przyciemnia liczbę wyświetleń.
fade_in_out (domyślnie) wykonuje czynność fade_out, po której następuje fade_in.
Slide <slide/> Śledzi zmiany widoczności widoków docelowych na początku i na końcu sceny i przesuwają widoki od jednego z krawędzi sceny.

Tworzenie instancji przejścia z pliku zasobów

Ta technika pozwala modyfikować definicję przejścia bez zmiany kodu Twojej aktywności. Technika ta przydaje się również do rozdzielania złożonych definicje przejścia z kodu aplikacji, jak pokazano w sekcji o określaniu kilku przejść.

Aby określić wbudowane przejście w pliku zasobów, wykonaj te czynności:

  • Dodaj do projektu katalog res/transition/.
  • Utwórz nowy plik zasobów XML w tym katalogu.
  • Dodaj węzeł XML dla jednego z wbudowanych przejść.

Na przykład ten plik zasobów określa przejście Fade:

res/transition/fade_transition.xml

<fade xmlns:android="http://schemas.android.com/apk/res/android" />

Ten fragment kodu pokazuje, jak powiększyć instancję Transition wewnątrz o swojej aktywności z pliku zasobów:

Kotlin

var fadeTransition: Transition =
    TransitionInflater.from(this)
                      .inflateTransition(R.transition.fade_transition)

Java

Transition fadeTransition =
        TransitionInflater.from(this).
        inflateTransition(R.transition.fade_transition);

Tworzenie w kodzie instancji przejściowej

Ta technika jest przydatna do dynamicznego tworzenia obiektów przejść, jeśli modyfikować interfejs w kodzie i tworzyć proste, wbudowane przejście z instancjami z niewielką liczbą parametrów lub bez nich.

Aby utworzyć instancję wbudowanego przejścia, wywołaj jedną z publicznych konstruktory w podklasach klasy Transition. Na przykład parametr ten fragment kodu tworzy wystąpienie przejścia Fade:

Kotlin

var fadeTransition: Transition = Fade()

Java

Transition fadeTransition = new Fade();

Zastosuj przejście

Zwykle stosuje się przejście, aby przełączać się między różnymi hierarchiami widoków w w odpowiedzi na zdarzenie, np. działania użytkownika. Przyjrzyjmy się na przykład wyszukiwarce: gdy użytkownik wpisuje wyszukiwane hasło i klika przycisk wyszukiwania, do sceny reprezentującej układ wyników z przejściem, które powoduje zniknięcie przycisku wyszukiwania i zanikanie w wynikach wyszukiwania.

Aby zmienić scenę podczas stosowania przejścia w odpowiedzi na zdarzenie w swoją aktywność, wywołaj funkcję klasy TransitionManager.go() z zakończeniem scena i wystąpienie przejścia, które ma być wykorzystywane w animacji, jak widać na ekranie. ten fragment:

Kotlin

TransitionManager.go(endingScene, fadeTransition)

Java

TransitionManager.go(endingScene, fadeTransition);

Platforma zmienia hierarchię widoku w głównej części sceny za pomocą widoku. hierarchii od sceny końcowej podczas odtwarzania animacji wskazywanej przez tag z instancji przejścia. Początkowa scena to końcowa scena z ostatniego przejścia. Jeśli nie ma poprzedniego przejścia, określa się scena początkowa. automatycznie od obecnego stanu interfejsu.

Jeśli nie określisz instancji przejścia, menedżer przenoszenia może zastosować automatyczne przeniesienie, które w większości sytuacji daje sens w sposób rozsądny. Dla: więcej informacji znajdziesz w dokumentacji interfejsu API TransitionManager zajęcia.

Wybierz konkretne widoki docelowe

Struktura stosuje przejścia do wszystkich widoków w scenie początkowej i końcowej domyślnie. W niektórych przypadkach warto zastosować animację tylko do podzbioru wyświetleń danej sceny. Platforma umożliwia wybór konkretnych widoków, które chcesz ani animacji. Na przykład platforma nie obsługuje animowania zmian w ListView obiektów, więc nie próbuj animować ich podczas przejścia.

Każdy widok, w którym jest animowany przejście, jest nazywany celem. Do wyboru masz tylko wybrać elementy docelowe należące do hierarchii widoku powiązanej ze sceną.

Aby usunąć co najmniej jeden widok z listy celów, wywołaj funkcję removeTarget() przed rozpoczęciem przejścia. Aby dodać tylko określone widoki do listę celów, wywołaj funkcję addTarget() . Więcej informacji znajdziesz w dokumentacji interfejsu API w sekcji Transition.

Określ wiele przejść

Aby uzyskać jak największy wpływ animacji, dopasuj ją do typu zmian, między scenami. Na przykład jeśli usuniesz niektóre widoki i dodasz między scenami, rozjaśnienie lub zaniknięcie animacji zapewnia może oznaczać, że niektóre widoki nie są już dostępne. Jeśli przenosisz widoki do w różnych punktach na ekranie, lepiej animować ruch, użytkownicy zauważają nową lokalizację wyświetleń.

Nie musisz wybierać tylko jednej animacji, ponieważ pozwala łączyć efekty animacji w zestawie przejść, który zawiera grupę pojedyncze wbudowane lub niestandardowe przejścia.

Aby zdefiniować zestaw przejść ze zbioru przejść w formacie XML, utwórz pliku zasobów w katalogu res/transitions/ i wymień przejścia pod element TransitionSet. Na przykład ten fragment kodu pokazuje, jak określ zestaw przejść, który będzie taki sam jak AutoTransition zajęcia:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
    <fade android:fadingMode="fade_in" />
</transitionSet>

Aby zwiększyć zakres przejścia TransitionSet obiekt w dla swojego kodu, wywołaj funkcję TransitionInflater.from() w Twojej aktywności. Klasa TransitionSet rozciąga się od Transition, możesz więc używać jej wraz z menedżerem przenoszenia, tak jak w przypadku każdej innej usługi. inną instancję Transition.

Zastosuj przejście bez scen

Zmiana hierarchii widoków to nie jedyny sposób na zmodyfikowanie interfejsu użytkownika. Ty również wprowadzić zmiany, dodając, modyfikując lub usuwając widoki podrzędne w bieżącej hierarchii.

Możesz np. wdrożyć interakcję związaną z wyszukiwaniem z użyciem parametru jeden układ. Zacznij od układu zawierającego pole do wpisywania wyszukiwania i wyszukiwanie . Aby zmienić interfejs użytkownika tak, aby wyświetlały się wyniki, usuń przycisk wyszukiwania gdy użytkownik go kliknie, wywołując ViewGroup.removeView() i dodaj wyniki wyszukiwania, wywołując ViewGroup.addView() .

Możesz użyć tego podejścia, jeśli alternatywą jest 2 hierarchie, które są prawie identyczne. Zamiast tworzyć i utrzymywać dwa osobne pliki układu dla niewielkiej różnicy w interfejsie możesz mieć jeden plik układu zawierające hierarchię widoków, którą modyfikujesz w kodzie.

Jeśli wprowadzisz zmiany w bieżącej hierarchii widoków w taki sposób, musisz utworzyć scenę. Zamiast tego możesz utworzyć i zastosować przejście między dwóch stanów w hierarchii widoku korzystającego z opóźnionego przejścia. Ta funkcja platforma przejść zaczyna się od bieżącego stanu hierarchii widoków, rekordy zmian wprowadzanych w widokach oraz zastosowanie przejścia, które animuje zmienia się, gdy system ponownie wyświetla interfejs użytkownika.

Aby utworzyć opóźnione przejście w obrębie hierarchii pojedynczego widoku, postępuj zgodnie z tymi kroki:

  1. Gdy wystąpi zdarzenie, które aktywuje przejście, wywołaj funkcję TransitionManager.beginDelayedTransition() dając widok nadrzędny wszystkich widoków którą chcesz zmienić, oraz przejście, które chcesz zastosować. Platforma przechowuje bieżącą stan widoków podrzędnych i ich wartości właściwości.
  2. Wprowadź zmiany w widokach podrzędnych odpowiednio do swojego przypadku użycia. Struktura rejestruje zmiany wprowadzone w widokach podrzędnych i ich właściwościach.
  3. Gdy system ponownie przerysuje interfejs zgodnie ze zmianami, platforma animuje zmiany między stanem pierwotnym a nowym.

Przykład poniżej pokazuje, jak animować dodanie widoku tekstu do widoku w hierarchii przy użyciu opóźnionego przejścia. Pierwszy fragment pokazuje układ plik definicji:

res/layout/activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <EditText
        android:id="@+id/inputText"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    ...
</androidx.constraintlayout.widget.ConstraintLayout>

Następny fragment kodu pokazuje kod animujący dodanie widoku tekstu:

MainActivity

Kotlin

setContentView(R.layout.activity_main)
val labelText = TextView(this).apply {
    text = "Label"
    id = R.id.text
}
val rootView: ViewGroup = findViewById(R.id.mainLayout)
val mFade: Fade = Fade(Fade.IN)
TransitionManager.beginDelayedTransition(rootView, mFade)
rootView.addView(labelText)

Java

private TextView labelText;
private Fade mFade;
private ViewGroup rootView;
...
// Load the layout.
setContentView(R.layout.activity_main);
...
// Create a new TextView and set some View properties.
labelText = new TextView(this);
labelText.setText("Label");
labelText.setId(R.id.text);

// Get the root view and create a transition.
rootView = (ViewGroup) findViewById(R.id.mainLayout);
mFade = new Fade(Fade.IN);

// Start recording changes to the view hierarchy.
TransitionManager.beginDelayedTransition(rootView, mFade);

// Add the new TextView to the view hierarchy.
rootView.addView(labelText);

// When the system redraws the screen to show this update,
// the framework animates the addition as a fade in.

Zdefiniuj wywołania zwrotne cyklu życia przejścia

Cykl przejścia jest podobny do cyklu życia aktywności. Reprezentuje on stany przejściowe, które platforma monitoruje w okresie między wywołaniem do funkcji TransitionManager.go() i wykonania funkcji animację. W ważnych stanach cyklu życia platforma wywołuje wywołania zwrotne zdefiniowane przez TransitionListener za pomocą prostego interfejsu online.

Wywołania zwrotne cyklu życia przejścia przydają się na przykład do kopiowania widoku od początkowej hierarchii widoków do końcowej podczas zmiany sceny. Nie możesz po prostu skopiować wartości z widoku początkowego do w końcowej hierarchii widoków, bo końcowa hierarchia widoków nie jest jest zwiększana do momentu ukończenia przejścia. Zamiast tego musisz zapisać wartość w zmiennej, a następnie skopiuj ją do końcowej hierarchii widoku, gdy platforma Użytkownik zakończył przenoszenie danych. Aby otrzymać powiadomienie o zakończeniu przenoszenia, zastosuj TransitionListener.onTransitionEnd() w Twojej aktywności.

Więcej informacji znajdziesz w dokumentacji interfejsu API w sekcji TransitionListener zajęcia.

Ograniczenia

W tej sekcji znajdziesz kilka znanych ograniczeń platformy przejść:

  • Animacje zastosowane do Opcja SurfaceView może się nie pojawiać . SurfaceView instancja jest aktualizowana z wątku niezwiązanego z interfejsem, więc mogą nie być zsynchronizowane z animacjami w innych widokach.
  • Niektóre typy przejść mogą nie dawać pożądanego efektu animacji. po zastosowaniu do elementu TextureView.
  • Klasy, które obejmują AdapterView, na przykład ListView, zarządzać widokami dziecka w sposób niezgodny z z struktury przejścia. Jeśli spróbujesz animować widok na podstawie elementu AdapterView, ekran urządzenia może przestać odpowiadać.
  • Jeśli próbujesz zmienić rozmiar elementu TextView za pomocą animacji, tekst przesuwa się w nowe miejsce, zanim obiekt zostanie całkowicie rozmiar został zmieniony. Aby uniknąć tego problemu, nie animuj zmiany rozmiaru widoków, które zawierają tekstu.