Interfejs Fragment API umożliwia korzystanie z efektów ruchu i przekształceń na 2 sposoby
aby wizualnie połączyć fragmenty podczas nawigacji. Jednym z nich jest
Struktura animacji, która wykorzystuje
Animation
i
Animator
a drugi to platforma przejścia, która obejmuje
przejściach elementów wspólnych.
Możesz określić niestandardowe efekty dla wprowadzania i kończenia fragmentów oraz dla przejścia wspólnych elementów między fragmentami.
- Efekt Enter określa sposób, w jaki fragment wchodzi na ekran. Przykład: można utworzyć efekt polegający na wsunięciu fragmentu od krawędzi ekranu, gdy przechodzisz do niego.
- Efekt wyjścia określa sposób opuszczenia ekranu przez fragment. Przykład: można utworzyć efekt zanikania fragmentów w trakcie opuszczania aplikacji .
- Przejście ze wspólnego elementu określa sposób udostępniania widoku danych między
między nimi zostaną umieszczone dwa fragmenty. Na przykład obraz wyświetlany
we fragmencie
ImageView
przechodzi do fragmentu B raz B .
ustawiania animacji,
Najpierw musisz utworzyć animacje dla efektów wejścia i zakończenia, które są uruchamianych przy przejściu do nowego fragmentu. Animacje możesz zdefiniować jako zasobów animacji starszych. Za pomocą tych zasobów możesz określić, jak fragmenty mają być obracane, rozciągane, zanikać i poruszają się w trakcie animacji. Na przykład możesz potrzebować bieżącego fragmentu, zanikanie i nowy fragment do przesunięcia od prawej krawędzi ekranu, jak widać na ilustracji 1.
![Otwórz animację i zamknij ją. Bieżący fragment zanika podczas
następny fragment przesuwa się od prawej strony.](https://developer.android.com/static/images/training/basics/fragments/enter-exit-animation.gif?authuser=9&hl=pl)
Te animacje można zdefiniować w katalogu res/anim
:
<!-- res/anim/fade_out.xml -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="1"
android:toAlpha="0" />
<!-- res/anim/slide_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromXDelta="100%"
android:toXDelta="0%" />
Możesz też określić animacje dla uruchamianych efektów wejścia i wyjścia.
może się zdarzyć, gdy użytkownik naciśnie w górę lub w górę
Przycisk Wstecz. Są to animacje popEnter
i popExit
. Dla:
np. gdy użytkownik wróci do poprzedniego ekranu, możesz ustawić
bieżący fragment, aby odsunąć prawą krawędź ekranu i poprzednią
fragment, który ma zanikać.
![animacje popEnter i popExit. Bieżący fragment zsuwa się
ekran w prawo, gdy poprzedni fragment się zmienia.](https://developer.android.com/static/images/training/basics/fragments/pop-animations.gif?authuser=9&hl=pl)
popEnter
i
Animacje: popExit
. Bieżący fragment przesuwa się z ekranu
w prawo, podczas gdy poprzedni fragment się zmienia.Te animacje można zdefiniować w ten sposób:
<!-- res/anim/slide_out.xml -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromXDelta="0%"
android:toXDelta="100%" />
<!-- res/anim/fade_in.xml -->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="0"
android:toAlpha="1" />
Po zdefiniowaniu animacji możesz ich używać za pomocą wywołania
FragmentTransaction.setCustomAnimations()
przekazywanie zasobów animacji według ich identyfikatorów zasobów, jak widać w tagu
następujący przykład:
Kotlin
supportFragmentManager.commit { setCustomAnimations( R.anim.slide_in, // enter R.anim.fade_out, // exit R.anim.fade_in, // popEnter R.anim.slide_out // popExit ) replace(R.id.fragment_container, fragment) addToBackStack(null) }
Java
Fragment fragment = new FragmentB(); getSupportFragmentManager().beginTransaction() .setCustomAnimations( R.anim.slide_in, // enter R.anim.fade_out, // exit R.anim.fade_in, // popEnter R.anim.slide_out // popExit ) .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit();
Ustaw przejścia
Przejścia mogą też służyć do definiowania efektów wejścia i wyjścia. Te przejścia można definiować w plikach zasobów XML. Możesz na przykład: chcesz, aby bieżący fragment zanikał, a nowy fragment został przesunięty z przy prawej krawędzi ekranu. Przejścia te można zdefiniować w ten sposób:
<!-- res/transition/fade.xml -->
<fade xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"/>
<!-- res/transition/slide_right.xml -->
<slide xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:slideEdge="right" />
Po zdefiniowaniu opcji przejścia zastosuj je, wywołując
setEnterTransition()
na wpisywanym fragmencie
setExitTransition()
na wyjściu z fragmentu, przekazując zwiększone zasoby przejścia
według identyfikatora zasobu, jak widać w tym przykładzie:
Kotlin
class FragmentA : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val inflater = TransitionInflater.from(requireContext()) exitTransition = inflater.inflateTransition(R.transition.fade) } } class FragmentB : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.slide_right) } }
Java
public class FragmentA extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TransitionInflater inflater = TransitionInflater.from(requireContext()); setExitTransition(inflater.inflateTransition(R.transition.fade)); } } public class FragmentB extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TransitionInflater inflater = TransitionInflater.from(requireContext()); setEnterTransition(inflater.inflateTransition(R.transition.slide_right)); } }
Obsługa fragmentów Przejście na AndroidaX. Fragmenty obsługują też procesu przenoszenia zasad, zalecamy przejście na AndroidaX, ponieważ są one obsługiwane w interfejsach API na poziomie 14 i nowszych oraz zawierają poprawki błędów, których nie było w starszych przejścia na nową platformę.
Używanie wspólnych przejść elementów
W ramach Zasad przejścia.
przejścia między wspólnymi elementami określają sposób przechodzenia odpowiednich widoków między nimi
dwóch fragmentów podczas przejścia z fragmentem. Możesz na przykład dodać
obraz wyświetlany w polu ImageView
na fragmencie A, przechodząc do fragmentu B
gdy widoczny jest symbol B, jak widać na rysunku 3.
![Przejście fragmentu ze wspólnym elementem.](https://developer.android.com/static/images/training/basics/fragments/shared-element-transition.gif?authuser=9&hl=pl)
Ogólnie rzecz biorąc, aby utworzyć przejście fragmentu ze wspólnymi elementami:
- Do każdego udostępnionego widoku elementu przypisz niepowtarzalną nazwę przejścia.
- Dodaj wspólne widoki elementów i nazwy przejść do
FragmentTransaction
- Ustaw animację przejścia elementu.
Po pierwsze musisz przypisać niepowtarzalną nazwę przejścia do każdego udostępnionego widoku elementów.
umożliwia mapowanie widoków z jednego fragmentu na drugi. Ustaw
nazwy przejścia w udostępnianych elementach w każdym układzie fragmentów za pomocą funkcji
ViewCompat.setTransitionName()
, który zapewnia zgodność z interfejsem API na poziomie 14 i wyższym.
Na przykład nazwa przejścia dla elementu ImageView
we fragmentach A i B
można przypisać w następujący sposób:
Kotlin
class FragmentA : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... val itemImageView = view.findViewById<ImageView>(R.id.item_image) ViewCompat.setTransitionName(itemImageView, “item_image”) } } class FragmentB : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... val heroImageView = view.findViewById<ImageView>(R.id.hero_image) ViewCompat.setTransitionName(heroImageView, “hero_image”) } }
Java
public class FragmentA extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... ImageView itemImageView = view.findViewById(R.id.item_image); ViewCompat.setTransitionName(itemImageView, “item_image”); } } public class FragmentB extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... ImageView heroImageView = view.findViewById(R.id.hero_image); ViewCompat.setTransitionName(heroImageView, “hero_image”); } }
Aby uwzględnić udostępnione elementy w przejściu fragmentów, tag
FragmentTransaction
musi wiedzieć, jak wyświetlenia każdego udostępnionego elementu są mapowane na podstawie jednego
fragment do następnego. Dodaj każdy z udostępnionych elementów do
FragmentTransaction
przez połączenie
FragmentTransaction.addSharedElement()
,
i nazwy przejścia odpowiedniego widoku w
następny fragment, jak w tym przykładzie:
Kotlin
val fragment = FragmentB() supportFragmentManager.commit { setCustomAnimations(...) addSharedElement(itemImageView, “hero_image”) replace(R.id.fragment_container, fragment) addToBackStack(null) }
Java
Fragment fragment = new FragmentB(); getSupportFragmentManager().beginTransaction() .setCustomAnimations(...) .addSharedElement(itemImageView, “hero_image”) .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit();
Aby określić, w jaki sposób udostępniane elementy będą przechodzić między fragmentami,
musisz ustawić przejście Enter dla fragmentu
przeszedł na stronę. Zadzwoń do nas
Fragment.setSharedElementEnterTransition()
w metodzie onCreate()
fragmentu, jak widać w tym przykładzie:
Kotlin
class FragmentB : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = TransitionInflater.from(requireContext()) .inflateTransition(R.transition.shared_image) } }
Java
public class FragmentB extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Transition transition = TransitionInflater.from(requireContext()) .inflateTransition(R.transition.shared_image); setSharedElementEnterTransition(transition); } }
Przejście shared_image
jest zdefiniowane w ten sposób:
<!-- res/transition/shared_image.xml -->
<transitionSet>
<changeImageTransform />
</transitionSet>
Wszystkie podklasy obiektu Transition
są obsługiwane jako udostępnione przejścia elementów. Jeśli
Jeśli chcesz utworzyć niestandardowy Transition
, zobacz
Utwórz niestandardową animację przejścia.
W poprzednim przykładzie model changeImageTransform
jest jednym z dostępnych
gotowych tłumaczeń, których możesz używać. Dodatkowe Transition
podklas w dokumentacji interfejsu API dla argumentów
Zajęcia: Transition
.
Domyślnie przejście do udostępnianego elementu służy też jako
przejście Return dla udostępnionych elementów. Przejście powrotne określa,
elementy udostępnione przechodzą z powrotem do poprzedniego fragmentu, gdy fragment
transakcja jest wysunięta na tylny stos. Jeśli chcesz podać inny
przejście powrotne, możesz to zrobić za pomocą funkcji
Fragment.setSharedElementReturnTransition()
w metodzie onCreate()
fragmentu.
Przewidywana zgodność wsteczna
Z prognozowania wstecznego możesz korzystać w przypadku wielu animacji zawierających fragmenty, ale nie wszystkich. Stosując prognozowanie wsteczne, pamiętaj o tych kwestiach:
- Importuj
Transitions 1.5.0
lub nowsze orazFragments 1.7.0
lub nowsze. - Klasa i podklasy
Animator
oraz biblioteka przenoszenia AndroidaX to: obsługiwane. - Biblioteka klas
Animation
i bibliotekaTransition
nie są obsługiwane. - Animacje fragmentów prognozujących działają tylko na urządzeniach z Androidem 14 lub wyższe.
setCustomAnimations
,setEnterTransition
,setExitTransition
setReenterTransition
,setReturnTransition
,setSharedElementEnterTransition
isetSharedElementReturnTransition
są z przewidywaniami.
Więcej informacji: Dodano obsługę animacji wstecznych.
Odkładanie przenoszenia
W niektórych przypadkach trzeba odroczyć przejście fragmentu na w krótkim czasie. Na przykład musisz poczekać, aż wszystkie wyświetlenia we fragmencie wejściowym zostały zmierzone i rozmieszczone w taki sposób, aby Android może dokładnie zarejestrować stan początkowy i końcowy przejścia.
Dodatkowo przeniesienie może być odroczone do wszystkie niezbędne dane są wczytywane. Możesz na przykład poczekać do dla udostępnionych elementów wczytano obrazy. W przeciwnym razie przeniesienie może może powodować jaranie, jeśli obraz wczytuje się podczas przejścia lub po nim.
Aby opóźnić przejście, najpierw upewnij się, że fragment
transakcja umożliwia zmianę stanu zmian stanu fragmentu. Aby umożliwić zmianę kolejności
zmiany stanu fragmentów, wywołanie
FragmentTransaction.setReorderingAllowed()
jak w tym przykładzie:
Kotlin
val fragment = FragmentB() supportFragmentManager.commit { setReorderingAllowed(true) setCustomAnimation(...) addSharedElement(view, view.transitionName) replace(R.id.fragment_container, fragment) addToBackStack(null) }
Java
Fragment fragment = new FragmentB(); getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) .setCustomAnimations(...) .addSharedElement(view, view.getTransitionName()) .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit();
Aby opóźnić przejście, wywołaj polecenie
Fragment.postponeEnterTransition()
w metodzie onViewCreated()
fragmentu wprowadzającego:
Kotlin
class FragmentB : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... postponeEnterTransition() } }
Java
public class FragmentB extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... postponeEnterTransition(); } }
Po załadowaniu danych i przygotowaniu się do rozpoczęcia przenoszenia wywołaj polecenie
Fragment.startPostponedEnterTransition()
W poniższym przykładzie użyto parametru
Glide, aby wczytać obraz
w udostępniony ImageView
, odkładając odpowiednie przejście do obrazu
ładowanie zostało zakończone.
Kotlin
class FragmentB : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... Glide.with(this) .load(url) .listener(object : RequestListener<Drawable> { override fun onLoadFailed(...): Boolean { startPostponedEnterTransition() return false } override fun onResourceReady(...): Boolean { startPostponedEnterTransition() return false } }) .into(headerImage) } }
Java
public class FragmentB extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... Glide.with(this) .load(url) .listener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(...) { startPostponedEnterTransition(); return false; } @Override public boolean onResourceReady(...) { startPostponedEnterTransition(); return false; } }) .into(headerImage) } }
W przypadku powolnego połączenia internetowego użytkownika możesz
potrzebują odroczonego przejścia, aby rozpoczęło się po określonym czasie, a nie
niż czekanie, aż wszystkie dane się załadują. W takiej sytuacji możesz
zamiast tego zadzwoń
Fragment.postponeEnterTransition(long, TimeUnit)
w metodzie onViewCreated()
fragmentu, przekazując czas trwania
i jednostkę czasu. Przełożony harmonogram rozpocznie się automatycznie, gdy
w określonym czasie.
Używaj przejść z elementami udostępnionych za pomocą komponentu RecyclerView
Przełożone przejście nie powinno się rozpocząć, dopóki nie wszystkie wyświetlenia na etapie
fragment został zmierzony i rozmieszczony. Jeśli używasz tagu
RecyclerView
, musisz poczekać
aby wszystkie dane zostały załadowane i aby elementy RecyclerView
były gotowe do rysowania.
przed rozpoczęciem przenoszenia. Oto przykład:
Kotlin
class FragmentA : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { postponeEnterTransition() // Wait for the data to load viewModel.data.observe(viewLifecycleOwner) { // Set the data on the RecyclerView adapter adapter.setData(it) // Start the transition once all views have been // measured and laid out (view.parent as? ViewGroup)?.doOnPreDraw { startPostponedEnterTransition() } } } }
Java
public class FragmentA extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { postponeEnterTransition(); final ViewGroup parentView = (ViewGroup) view.getParent(); // Wait for the data to load viewModel.getData() .observe(getViewLifecycleOwner(), new Observer<List<String>>() { @Override public void onChanged(List<String> list) { // Set the data on the RecyclerView adapter adapter.setData(it); // Start the transition once all views have been // measured and laid out parentView.getViewTreeObserver() .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw(){ parentView.getViewTreeObserver() .removeOnPreDrawListener(this); startPostponedEnterTransition(); return true; } }); } }); } }
Zwróć uwagę, że
ViewTreeObserver.OnPreDrawListener
jest ustawiony w elemencie nadrzędnym widoku fragmentów. Ma to na celu zapewnienie, że wszystkie
wyświetlenia fragmentu zostały zmierzone i rozmieszczone, więc są gotowe do
może być rysowana przed rozpoczęciem opóźnionego przejścia.
Kolejna kwestia do rozważenia przy korzystaniu z przejścia udostępnionych elementów z tagiem
RecyclerView
polega na tym, że nie można określić nazwy przejścia w polu
Układ XML RecyclerView
elementu, ponieważ współdzielisz dowolną liczbę elementów
i tego układu. Aby tag
animacja przejścia ma prawidłowy widok.
Każdemu elementowi możesz nadać unikalną nazwę przejścia:
i przypisuje je, gdy zasada ViewHolder
jest powiązana. Na przykład, jeśli dane dot.
Każdy element ma niepowtarzalny identyfikator, którego można użyć jako nazwy przejścia,
w tym przykładzie:
Kotlin
class ExampleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val image = itemView.findViewById<ImageView>(R.id.item_image) fun bind(id: String) { ViewCompat.setTransitionName(image, id) ... } }
Java
public class ExampleViewHolder extends RecyclerView.ViewHolder { private final ImageView image; ExampleViewHolder(View itemView) { super(itemView); image = itemView.findViewById(R.id.item_image); } public void bind(String id) { ViewCompat.setTransitionName(image, id); ... } }
Dodatkowe materiały
Aby dowiedzieć się więcej o przejściach fragmentów, zapoznaj się z tymi dodatkowymi informacjami i zasobami Google Cloud.
Próbki
Posty na blogu
- Ciągłe przenoszenie elementów wspólnych: element RecyclerView do elementu ViewPager
- Przejścia fragmentów