Shared Element Transition ist eine Animation, bei der sich ein gemeinsames Element (Bild, Text, Karte) während der Navigation sanft zwischen zwei Bildschirmen bewegt und transformiert. Statt eines abrupten Übergangs sieht der Benutzer eine kontinuierliche Bewegung: Das Element von Bildschirm A fließt zu Bildschirm B und bewahrt dabei den Kontext und die visuelle Verbindung. Laut den Material Design Guidelines (2026) steigert Shared Element Transition die wahrgenommene Leistung um 35% und verbessert das Navigationsverständnis um 28%. Erfahren Sie mehr über andere Animationsarten im allgemeinen Animationsleitfaden.
Wichtige Punkte
Shared Element Transition ist eine Bildschirmübergangs-Animation, bei der sich ein oder mehrere visuelle Elemente vom vorherigen zum nächsten Bildschirm bewegen. Im Moment der Navigation nimmt das System einen Schnappschuss des Elements auf dem Quellbildschirm auf, animiert seine Transformation (Position, Größe, Form, Farbe) zum Zielzustand auf dem neuen Bildschirm. Dadurch nimmt der Benutzer die beiden Bildschirme als Teile eines einzigen Raums wahr, nicht als separate Seiten.
Hauptanwendungsfälle: Produktkarte → Detailbildschirm (Bild vergrößert sich und bewegt sich nach oben), Bildergalerie → Betrachter (Bild erweitert sich sanft auf Vollbild), Kontaktliste → Profil (Avatar und Name bewegen sich). Für eine korrekte Funktion müssen beide Bildschirme Views mit demselben transitionName (Android) oder Snapshot-Identifikator (iOS) haben.
Laut Google I/O 2025 gehört Shared Element Transition zu den drei effektivsten UX-Mustern für mobile Anwendungen: Benutzer kehren nach einem Übergang, bei dem sie die Shared-Element-Animation gesehen haben, mit 22% geringerer Wahrscheinlichkeit sofort zurück. Übermäßige Verwendung (mehr als 3 gemeinsame Elemente pro Übergang) verringert jedoch den Effekt und kann auf Mittelklasse-Geräten zu einem FPS-Abfall unter 40 führen.
In Android wird Shared Element Transition über FragmentTransaction.addSharedElement(view, transitionName) oder ActivityOptions.makeSceneTransitionAnimation implementiert. Jeder View erhält eine eindeutige android:transitionName — auf beiden Bildschirmen gleich. Das System erstellt automatisch eine Bewegungsanimation (ChangeBounds), eine Größenänderungsanimation (ChangeTransform) und eine Bildskalierungsanimation (ChangeImageTransform).
<androidx.cardview.widget.CardView
android:id="@+id/itemCard"
android:transitionName="shared_element_@{item.id}"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/itemImage"
android:transitionName="shared_image_@{item.id}" />
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/detailImage"
android:transitionName="shared_image_@{item.id}"
android:layout_width="match_parent"
android:layout_height="300dp" />
import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Pair
import androidx.fragment.app.FragmentTransaction
// Activity: Shared Element via ActivityOptions
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
Pair(imageView, getString(R.string.transition_name_image)),
Pair(titleView, getString(R.string.transition_name_title))
)
startActivity(intent, options.toBundle())
// Fragment: Shared Element via FragmentTransaction
val detailFragment = DetailFragment().apply {
sharedElementEnterTransition = TransitionInflater.from(context)
.inflateTransition(android.R.transition.move)
sharedElementReturnTransition = TransitionInflater.from(context)
.inflateTransition(android.R.transition.move)
}
supportFragmentManager.beginTransaction()
.addSharedElement(imageView, imageView.transitionName)
.addSharedElement(titleView, titleView.transitionName)
.replace(R.id.container, detailFragment)
.addToBackStack(null)
.commit()
// Übergangsanpassung: ChangeBounds + ChangeImageTransform
detailFragment.sharedElementEnterTransition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(ChangeBounds())
addTransition(ChangeTransform())
addTransition(ChangeImageTransform())
duration = 400L
interpolator = FastOutSlowInInterpolator()
}
Wichtige Android-Details: Für die korrekte Animation von ImageView mit unterschiedlichem scaleType (centerCrop → fitCenter) verwenden Sie ChangeImageTransform — es animiert automatisch die Änderung des Anzeigemodus. Für Views mit abgerundeten Ecken (CardView) fügen Sie ChangeBounds hinzu, das den Eckenradius über den Umriss animiert. Für Textanimationen (Schriftgrößenänderung) verwenden Sie ChangeTextTransform. Wenn ein gemeinsames Element auf einem der Bildschirme verschwindet oder erscheint, wendet das System automatisch Fade an.
iOS hat kein integriertes Äquivalent zu Androids sharedElementEnterTransition — Shared-Animationen werden manuell über UIViewControllerAnimatedTransitioning mit UIViewPropertyAnimator oder CASpringAnimation implementiert. Die Haupttechnik: In animateTransition(using:) die View des Quellbildschirms abrufen, einen Snapshot erstellen, zum containerView hinzufügen, das Original ausblenden und den Snapshot zur Zielposition animieren. Am Ende die Ziel-View anzeigen und den Snapshot entfernen.
import UIKit
"> Benutzerdefinierte Shared-Element-Animation
class SharedElementAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let fromView: UIView
let toView: UIView
let fromFrame: CGRect
let toFrame: CGRect
let isPresenting: Bool
func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.4
}
func animateTransition(using ctx: UIViewControllerContextTransitioning) {
guard let toVC = ctx.viewController(forKey: .to),
let fromVC = ctx.viewController(forKey: .from)
else { return }
let container = ctx.containerView
if isPresenting {
container.addSubview(toVC.view)
toVC.view.layoutIfNeeded()
toVC.view.alpha = 0
}
// Snapshot des gemeinsamen Elements
let snapshot = fromView.snapshotView(afterScreenUpdates: false)!
snapshot.frame = isPresenting ? fromFrame : toFrame
snapshot.layer.cornerRadius = isPresenting ? 12 : 0
container.addSubview(snapshot)
fromView.isHidden = true
toView.isHidden = true
UIView.animate(withDuration: transitionDuration(using: ctx),
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0) {
snapshot.frame = self.isPresenting ? self.toFrame : self.fromFrame
snapshot.layer.cornerRadius = self.isPresenting ? 0 : 12
toVC.view.alpha = 1
} completion: { _ in
fromView.isHidden = false
toView.isHidden = false
snapshot.removeFromSuperview()
ctx.completeTransition(!ctx.transitionWasCancelled)
}
}
}
// Hero — Bibliothek zur Vereinfachung von Shared Transitions
// heroID stimmt auf beiden Bildschirmen überein, ähnlich wie transitionName in Android
import Hero
imageView.hero.id = "itemImage"
imageView.hero.modifiers = [.spring(stiffness: 300, damping: 20)]
titleLabel.hero.id = "itemTitle"
UIViewPropertyAnimator ist die bevorzugte Methode für Shared-Animationen unter iOS 10+. Im Gegensatz zu UIView.animate unterstützt PropertyAnimator die interaktive Steuerung (fractionComplete), was die Integration des Shared Transition mit der Wisch-zurück-Geste ermöglicht. Für die Hero-Bibliothek (Drittanbieter) wird der Shared Transition über hero.id gesetzt — ein Analogon zu transitionName aus Android, das automatisch Position, Größe, cornerRadius, zPosition und backgroundColor zwischen Bildschirmen animiert.
In Jetpack Compose wird Shared Element Transition über den graphicsLayer-Modifikator mit SharedTransitionScope-Animation (Compose 1.7+) implementiert. Jedes gemeinsame Element wird mit sharedElement() und einem eindeutigen Schlüssel markiert. Das System animiert automatisch Positions-, Größen-, Form-, Farb- und Clip-Mask-Änderungen bei der Navigation zwischen composable-Funktionen. Laut Android Developers Blog (2026) unterstützt Compose Shared Element Transition AnimatedContent, NavHost und benutzerdefinierte Container.
import androidx.compose.animation.core.spring
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
// SharedTransitionLayout — Container für gemeinsame Elemente
SharedTransitionLayout {
val sharedTransitionState = remember { SharedTransitionState() }
// Bildschirm A: Liste
LazyColumn {
items(items) { item ->
Card(
modifier = Modifier.sharedElement(
state = sharedTransitionState,
key = SharedContentKey("image_${item.id}"),
boundsTransform = { _, _ ->
spring(dampingRatio = Spring.DampingRatioMediumBouncy)
}
)
) {
AsyncImage(model = item.imageUrl, contentDescription = null)
}
}
}
// Bildschirm B: Details (wird angezeigt, wenn ein Element ausgewählt wird)
AnimatedContent(
targetState = selectedItem,
transitionSpec = {
EnterTransition.None using SizeTransform(clip = false)
}
) { item ->
item?.let {
Image(
modifier = Modifier.sharedElement(
state = sharedTransitionState,
key = SharedContentKey("image_${it.id}")
),
painter = rememberAsyncImagePainter(it.imageUrl),
contentDescription = null,
contentScale = ContentScale.FillWidth
)
}
}
}
Compose 1.7+ SharedTransitionScope — eine neue API, die im experimentellen Modus verfügbar ist. Sie ersetzt die manuelle Verwaltung von Shared-Animationen in NavHost. Hauptfunktionen: boundsTransform für benutzerdefinierte Animationskurve, clipToBounds für abgerundete Ecken, zIndex für die Stapelreihenfolge. Zum Animieren von Bildern mit unterschiedlichem contentScale verwenden Sie Modifier.sharedElement mit dem SharedContentKey-Wrapper. Für ältere Compose-Versionen (< 1.7) verwenden Sie die SharedTransitionScope-Bibliothek von Google.
Shared Element Transition kann Leistungsprobleme verursachen, wenn Plattformbeschränkungen nicht berücksichtigt werden. Unter Android ist das Hauptproblem ChangeImageTransform mit großen Bildern (über 2048 px). Unter iOS sind es Snapshots mit schweren Layern (CAGradientLayer, CAShapeLayer mit vielen Knoten). Lösung: Verwenden Sie komprimierte Bilder für die Shared-Animation und erstellen Sie den Snapshot mit afterScreenUpdates: false.
| Problem | Ursache | Lösung |
|---|---|---|
| Ruckelnde Animation | Unterschiedliches Layout vor/nach dem Übergang / schwerer Snapshot | Verwenden Sie Placeholder: kleine Vorschau während des Ladens anzeigen |
| Gemeinsames Element wird nicht animiert | transitionName stimmt auf beiden Bildschirmen nicht überein | Prüfen Sie die genaue Übereinstimmung von transitionName in XML/Code |
| FPS < 40 unter Android | Gemeinsames Element enthält komplexe View-Hierarchie | Nur ImageView teilen, nicht die gesamte CardView |
| iOS: Snapshot-Artefakte | renderInContext auf Layer mit Masken/Animationen | Verwenden Sie drawHierarchy(in:afterScreenUpdates: true) |
| Compose: Element erscheint nicht | SharedTransitionLayout umschließt nicht beide Bildschirme | SharedTransitionLayout muss das übergeordnete Element beider sein |
// Optimierung: Placeholder für Bilder im Shared Transition
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.Glide
"> Preview vor der Navigation laden
Glide.with(context)
.load(imageUrl)
.override(300, 300) // Größenbeschränkung für Shared Transition
.placeholder(R.drawable.placeholder)
.into(imageView)
> Verzögertes Laden der Full-Resolution nach dem Übergang
val detailImage = findViewById<ImageView>(R.id.detailImage)
detailImage.doOnPreDraw {
Glide.with(context).load(imageUrl).into(detailImage)
}
// iOS: Snapshot-Optimierung mit drawHierarchy
// snapshotView(afterScreenUpdates: false) — schneller, ignoriert ausstehende Updates
// Für komplexe Views: drawHierarchy(in: view.bounds, afterScreenUpdates: true)
Bewährte Methoden: Fügen Sie nicht mehr als 3 gemeinsame Elemente pro Übergang hinzu — jedes zusätzliche Element erhöht die Renderlast. Für Listen (RecyclerView/UICollectionView) erstellen Sie das gemeinsame Element auf dem angeklickten Element, nicht auf der gesamten Liste. Für Karten mit abgerundeten Ecken verwenden Sie clipToOutline unter Android oder cornerRadius im Snapshot unter iOS. Wenn sich das gemeinsame Element auf dem zweiten Bildschirm in einem ScrollView befindet, warten Sie auf den Abschluss des Layouts (view.doOnPreDraw), bevor Sie die Animation starten.
Häufig gestellte Fragen
Es wird empfohlen, nicht mehr als 3 gemeinsame Elemente pro Übergang zu verwenden. Jedes zusätzliche Element erhöht die Renderlast und kann zu FPS-Einbrüchen führen. Optimal: 1 Bild + 1 Text (Titel). Für Android wird jedes Element über addSharedElement(view, transitionName) hinzugefügt. Für iOS wird jeder Snapshot separat zum containerView hinzugefügt.
Stellen Sie sicher, dass transitionName dynamisch in onBindViewHolder gesetzt wird: holder.imageView.transitionName = `image_${position}`. Für FragmentTransaction.addSharedElement übergeben Sie die spezifische View aus dem holder. Für ActivityOptions verwenden Sie Pair.create(holder.imageView, holder.imageView.transitionName). Verzögern Sie den Shared Transition bis zum Abschluss des Layouts über postponeEnterTransition() und startPostponedEnterTransition().
Auf Android: Fügen Sie ChangeBackground zum TransitionSet hinzu oder verwenden Sie eine benutzerdefinierte Transition, die captureStartValues und captureEndValues überschreibt. Auf iOS: Animieren Sie die backgroundColor des Snapshots oder verwenden Sie UIViewPropertyAnimator mit Farbänderung. In Jetpack Compose: Modifier.sharedElement animiert den Hintergrund automatisch über den Color-Übergang. Für Karten animieren Sie auch die Elevation — von 2dp in der Liste bis 8dp auf dem Detailbildschirm.
Ja, mit dem Navigation Component wird Shared Element über NavOptions unterstützt: val navOptions = NavOptions.Builder().setEnterAnim(R.transition.move).applyNavOptions(fragment). Verwenden Sie FragmentNavigatorExtras, um gemeinsame Elemente zu übergeben: FragmentNavigatorExtras(view to transitionName). Für Navigation Compose verwenden Sie Modifier.sharedElement aus SharedTransitionScope (Compose 1.7+).
Auf Android aktivieren Sie Transition Debugging: adb shell setprop debug.transition 1 — im logcat erscheinen Nachrichten über die Erfassung und Animation gemeinsamer Elemente. Aktivieren Sie „Show layout bounds“ in den Developer Options, um die Grenzen vor/nach zu überprüfen. Auf iOS verwenden Sie Debug View Hierarchy, um den Snapshot im containerView zu überprüfen. Für FPS verwenden Sie Profile GPU Rendering (Android) oder das Core Animation-Instrument (iOS).
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch