View-System und Compose-Interop
Views und Compose können gemeinsam laufen. So migrierst du UI schrittweise, ohne stabile XML-Screens neu zu bauen.
Viele Android-Apps bestehen nicht nur aus Compose. In realen Projekten findest du oft XML-Layouts, Fragments, Custom Views und neue Compose-Screens nebeneinander. View-System und Compose-Interop beschreibt genau diese Zusammenarbeit: Du kannst Compose in bestehende Views einbauen und klassische Views in Compose weiterverwenden.
Was ist das?
Das View-System ist die klassische Android-UI-Welt mit View, ViewGroup, XML-Layouts, Activity, Fragment, RecyclerView und Custom Views. Jetpack Compose ist die moderne deklarative UI-Schicht, in der du Oberflächen mit Kotlin-Funktionen beschreibst. Interop bedeutet: Beide Welten dürfen in derselben App zusammenarbeiten.
Dafür sind zwei Bausteine zentral. Mit ComposeView platzierst du Compose-Inhalte innerhalb eines bestehenden View-Layouts, etwa in einem Fragment mit XML. Mit AndroidView nutzt du eine klassische View innerhalb einer Compose-Hierarchie, zum Beispiel wenn es für eine ältere Komponente noch keinen passenden Compose-Ersatz gibt.
Das ist für professionelle Android-Entwicklung wichtig, weil große Apps selten in einem Schritt migriert werden. Stabile Features neu zu schreiben kostet Zeit und erzeugt Risiko. Interop erlaubt dir, neue UI in Compose zu entwickeln, während bewährte XML-Screens weiterlaufen. So passt das Thema in die Roadmap-Phase Migration und Skalierung: Du lernst, technische Modernisierung kontrolliert durchzuführen.
Wie funktioniert es?
Das mentale Modell ist: Compose und Views haben unterschiedliche UI-Modelle, aber sie können über definierte Brücken miteinander sprechen. Views werden objektorientiert erzeugt, geändert und in einer Hierarchie gemessen und gezeichnet. Compose beschreibt UI als Funktion von State. Wenn sich State ändert, wird der betroffene Teil neu komponiert.
ComposeView ist eine View, deren Inhalt aus Compose besteht. Du kannst sie in XML deklarieren oder in Kotlin erzeugen. Danach rufst du setContent { ... } auf und gibst dort deine Composables an. In Fragments ist der Lifecycle wichtig: Die Composition muss beendet werden, wenn die View des Fragments zerstört wird. Sonst kann UI-State länger leben als die View, die ihn anzeigen soll.
AndroidView funktioniert andersherum. In einem Composable erzeugst du eine klassische View über factory und aktualisierst sie über update. Der factory-Block sollte die View erstellen, der update-Block sollte aktuelle Daten setzen. So vermeidest du, dass bei jeder Recomposition unnötig neue View-Objekte entstehen.
Im Alltag begegnet dir Interop oft bei schrittweisen Umbauten: ein alter Profil-Screen bekommt zuerst eine neue Compose-Kopfzeile, ein XML-Formular wird später durch Compose ersetzt, oder eine vorhandene Custom View bleibt erhalten, weil sie viele getestete Details enthält. Architektur bleibt dabei entscheidend. State sollte aus ViewModel, Flow oder UI-State-Modellen kommen, nicht quer zwischen View und Compose versteckt werden.
In der Praxis
Angenommen, du hast ein bestehendes Fragment mit XML und möchtest nur einen neuen Compose-Bereich ergänzen. In deinem XML liegt eine ComposeView:
<androidx.compose.ui.platform.ComposeView
android:id="@+id/profileHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Im Fragment setzt du den Inhalt und bindest ihn sauber an den View-Lifecycle:
class ProfileFragment : Fragment(R.layout.fragment_profile) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val header = view.findViewById<ComposeView>(R.id.profileHeader)
header.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
header.setContent {
MaterialTheme {
ProfileHeader(
name = "Mira",
subtitle = "Android-Entwicklerin"
)
}
}
}
}
Die Entscheidungsregel dazu: Nutze ComposeView, wenn der umgebende Screen noch View-basiert ist und du nur einen Teil modernisieren willst. Nutze AndroidView, wenn dein Screen schon Compose ist, du aber eine bestehende View-Komponente weiterverwenden musst.
Ein Beispiel für AndroidView ist eine vorhandene Custom View:
@Composable
fun LegacyChart(points: List<Int>) {
AndroidView(
factory = { context ->
ChartView(context)
},
update = { chartView ->
chartView.setPoints(points)
}
)
}
Eine typische Stolperfalle ist State an der falschen Stelle. Wenn du in factory Daten setzt, die sich später ändern, reagiert die View nicht zuverlässig auf Recomposition. Änderbare Daten gehören in update. Eine zweite Stolperfalle betrifft Listen: ComposeView in RecyclerView kann funktionieren, aber du musst Recycling, Composition-Strategie und State sauber prüfen. Sonst siehst du falsche Inhalte, flackernde UI oder unnötige Arbeit beim Scrollen.
Teste Interop nicht nur optisch. Prüfe mit dem Debugger, wann setContent, factory und update laufen. Schreibe kleine UI-Tests für kritische Zustände und achte im Code-Review darauf, ob Lifecycle und State klar getrennt sind.
Fazit
View-System und Compose-Interop ist kein Übergangstrick, sondern ein wichtiges Werkzeug für echte Android-Projekte. Du kannst Compose gezielt einführen, ohne stabile XML- und View-Funktionen sofort zu ersetzen. Übe beide Richtungen: Baue ein Composable per ComposeView in ein Fragment ein und binde eine Custom View per AndroidView in Compose ein. Prüfe danach mit Debugger, Tests und Code-Review, ob Lifecycle, State und Updates dort liegen, wo sie hingehören.