Die Migration von Views zu Compose ist rein UI-bezogen, es gibt jedoch die für eine sichere und inkrementelle Migration berücksichtigt werden sollten. Dieses enthält einige Überlegungen zur Migration App zum Schreiben erstellen.
Design Ihrer App migrieren
Material Design ist das empfohlene Designsystem für das Erstellen von Android-Apps.
Für ansichtenbasierte Apps sind drei Versionen von Material Design verfügbar:
- Material Design 1 mit der Bibliothek AppCompat (z. B.
Theme.AppCompat.*
) - Material Design 2 mithilfe der
MDC – Android
Bibliothek (z.B.
Theme.MaterialComponents.*
) - Material Design 3 mit der Bibliothek MDC-Android (d. h.
Theme.Material3.*
)
Für Editoren-Apps sind zwei Versionen von Material verfügbar:
- Material Design 2 mithilfe der
Bibliothek Compose Material
(z.B.
androidx.compose.material.MaterialTheme
) - Material Design 3 mithilfe der
Bibliothek Compose Material 3
(z.B.
androidx.compose.material3.MaterialTheme
)
Wir empfehlen die Verwendung der neuesten Version (Material 3), wenn das Designsystem Ihrer App in der Lage ist, dies zu tun. Für beide Ansichten sind Migrationsleitfäden verfügbar und Schreiben:
- Material 1 bis Material 2 in Ansichten
- Material 2 bis Material 3 in Ansichten
- Material 2 bis Material 3 in Compose
Wenn Sie neue Bildschirme in Compose erstellen, müssen Sie unabhängig von der verwendeten Version von Material Design eine MaterialTheme
vor allen Composables anwenden, die UI aus den Materialbibliotheken von Compose ausgeben. Das Material
Komponenten (Button
, Text
usw.) hängen davon ab, dass eine MaterialTheme
vorhanden ist.
und ihr Verhalten ist ohne sie nicht definiert.
Alle
Jetpack Compose-Beispiele
ein benutzerdefiniertes Design zum Verfassen von Nachrichten verwenden, das auf MaterialTheme
basiert.
Weitere Informationen finden Sie unter Systeme in Compose und XML-Designs zu Compose migrieren.
Navigation
Wenn Sie die Navigationskomponente in Ihrer App verwenden, lesen Sie Interoperabilität bei der Verwendung von Editoren und Weitere Informationen finden Sie unter Jetpack Navigation zu Navigation Compose migrieren.
Benutzeroberfläche für die kombinierte Ansicht „Compose“ und „Views“ testen
Nach der Migration von Teilen deiner App zu Compose sind Tests unerlässlich, nichts kaputt gemacht haben.
Wenn für eine Aktivität oder ein Fragment die Funktion „Schreiben“ verwendet wird, müssen Sie
createAndroidComposeRule
statt ActivityScenarioRule
zu verwenden. createAndroidComposeRule
-Integration
ActivityScenarioRule
durch einen ComposeTestRule
, mit dem Sie die Funktionen
Zur selben Zeit sehen Sie den Code.
class MyActivityTest { @Rule @JvmField val composeTestRule = createAndroidComposeRule<MyActivity>() @Test fun testGreeting() { val greeting = InstrumentationRegistry.getInstrumentation() .targetContext.resources.getString(R.string.greeting) composeTestRule.onNodeWithText(greeting).assertIsDisplayed() } }
Weitere Informationen finden Sie unter Layout von „Compose“ testen. Für Interoperabilität mit Frameworks für UI-Tests, siehe Interoperabilität mit Espresso und Interoperabilität mit UiAutomator
Compose in Ihre vorhandene Anwendungsarchitektur einbinden
UDF-Architektur (Unidirektionale Datenflüsse) Muster funktionieren nahtlos mit der Funktion „Schreiben“. Wenn die App andere Arten von Architekturmustern, wie Model View Presenter (MVP), empfehlen wir, Sie müssen diesen Teil der UI vor oder während der Einführung von „Compose“ zu UDF migrieren.
ViewModel
in „Compose“ verwenden
Bei Verwendung der Architekturkomponenten
ViewModel
haben, können Sie auf
ViewModel
aus einer beliebigen zusammensetzbaren Funktion mit
das Aufrufen der
viewModel()
wie unter Compose und andere Bibliotheken erläutert.
Achten Sie bei der Einführung von „Compose“ darauf, nicht denselben ViewModel
-Typ in
Verschiedene zusammensetzbare Funktionen, da ViewModel
-Elemente den Bereichen des Ansichtslebenszyklus entsprechen. Die
ist entweder die Hostaktivität, das Fragment oder das Navigationsdiagramm,
Die Navigationsbibliothek wird verwendet.
Wenn die Composeables beispielsweise in einer Aktivität gehostet werden, gibt viewModel()
immer dieselbe Instanz zurück, die erst gelöscht wird, wenn die Aktivität beendet ist.
Im folgenden Beispiel wird derselbe Nutzer („user1“) zweimal begrüßt, da dieselbe GreetingViewModel
-Instanz in allen Composeables unter der Hostaktivität wiederverwendet wird. Die erste erstellte ViewModel
-Instanz wird in anderen
zusammensetzbaren Funktionen.
class GreetingActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { Column { GreetingScreen("user1") GreetingScreen("user2") } } } } } @Composable fun GreetingScreen( userId: String, viewModel: GreetingViewModel = viewModel( factory = GreetingViewModelFactory(userId) ) ) { val messageUser by viewModel.message.observeAsState("") Text(messageUser) } class GreetingViewModel(private val userId: String) : ViewModel() { private val _message = MutableLiveData("Hi $userId") val message: LiveData<String> = _message } class GreetingViewModelFactory(private val userId: String) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return GreetingViewModel(userId) as T } }
Da Navigationsgraphen auch ViewModel
-Elemente umfassen, haben Composables, die ein Ziel in einem Navigationsgraphen sind, eine andere Instanz der ViewModel
.
In diesem Fall bezieht sich der ViewModel
auf den Lebenszyklus des Ziels und
Sie wird gelöscht, wenn das Ziel aus dem Backstack entfernt wird. Im
Beispiel: Wenn der Nutzer zur Seite Profil wechselt, wird eine neue
Instanz von GreetingViewModel
wird erstellt.
@Composable fun MyApp() { NavHost(rememberNavController(), startDestination = "profile/{userId}") { /* ... */ composable("profile/{userId}") { backStackEntry -> GreetingScreen(backStackEntry.arguments?.getString("userId") ?: "") } } }
„Source of Truth“
Wenn Sie Compose in einem Teil der Benutzeroberfläche verwenden, müssen Compose und der Systemcode der Ansicht möglicherweise Daten teilen. Wir empfehlen, diesen freigegebenen Status nach Möglichkeit in einer anderen Klasse zu kapseln, die den von beiden Plattformen verwendeten Best Practices für UDFs entspricht. Beispielsweise in einer ViewModel
, die einen Stream der freigegebenen Daten für die Ausgabe von Datenaktualisierungen bereitstellt.
Das ist jedoch nicht immer möglich, wenn die freigegebenen Daten veränderbar sind oder eng mit einem UI-Element verknüpft sind. In diesem Fall muss ein System die Quelle und dieses System alle Datenaktualisierungen an das andere System weitergeben muss. Als Faustregel gilt: Die „Source of Truth“ sollte dem Element gehören, das der Wurzel der UI-Hierarchie am nächsten ist.
Als „Source of Truth“ verfassen
Verwenden Sie die Methode
SideEffect
zusammensetzbar, um den Erstellungsstatus in Nicht-Compose-Code zu veröffentlichen. In diesem Fall
„Source of Truth“ wird in einer zusammensetzbaren Funktion gespeichert, die Statusaktualisierungen sendet.
Mit Ihrer Analysebibliothek können Sie beispielsweise Ihre Nutzergruppe segmentieren, indem Sie allen nachfolgenden Analyseereignissen benutzerdefinierte Metadaten (in diesem Beispiel Nutzereigenschaften) anhängen. Um den Nutzertyp der
aktuellen Nutzer zu Ihrer Analysebibliothek hinzu, verwenden Sie SideEffect
, um den Wert zu aktualisieren.
@Composable fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics { val analytics: FirebaseAnalytics = remember { FirebaseAnalytics() } // On every successful composition, update FirebaseAnalytics with // the userType from the current User, ensuring that future analytics // events have this metadata attached SideEffect { analytics.setUserProperty("userType", user.userType) } return analytics }
Weitere Informationen finden Sie unter Nebeneffekte beim Schreiben.
System als zentrale Datenquelle betrachten
Wenn das View-System Inhaber des Status ist und ihn für die Funktion „Schreiben“ freigibt, empfehlen wir,
packen Sie den Status in mutableStateOf
-Objekte ein, damit er Thread-sicher ist.
Schreiben. Bei diesem Ansatz werden zusammensetzbare Funktionen vereinfacht,
nicht mehr über die zentrale Informationsquelle, aber das View-System muss
änderbaren Status und die
Ansichten, die diesen Status verwenden.
Im folgenden Beispiel enthält ein CustomViewGroup
einen TextView
und einen
ComposeView
mit einer TextField
zusammensetzbaren Funktion. Der TextView
muss
was der Nutzer in TextField
eingibt.
class CustomViewGroup @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : LinearLayout(context, attrs, defStyle) { // Source of truth in the View system as mutableStateOf // to make it thread-safe for Compose private var text by mutableStateOf("") private val textView: TextView init { orientation = VERTICAL textView = TextView(context) val composeView = ComposeView(context).apply { setContent { MaterialTheme { TextField(value = text, onValueChange = { updateState(it) }) } } } addView(textView) addView(composeView) } // Update both the source of truth and the TextView private fun updateState(newValue: String) { text = newValue textView.text = newValue } }
Freigegebene Benutzeroberfläche migrieren
Wenn Sie schrittweise zu Compose migrieren, müssen Sie möglicherweise die gemeinsam genutzte UI verwenden
in Compose und View. Wenn Ihre App beispielsweise eine benutzerdefinierte CallToActionButton
-Komponente hat, müssen Sie sie möglicherweise sowohl auf Compose- als auch auf datenbankbasierten Bildschirmen verwenden.
In Compose werden gemeinsam genutzte UI-Elemente zu zusammensetzbaren Funktionen, die im gesamten
unabhängig davon, ob das Element mit XML formatiert wurde oder es sich um eine benutzerdefinierte Ansicht handelt. Für
Sie erstellen beispielsweise eine zusammensetzbare Funktion CallToActionButton
für Ihren benutzerdefinierten Aufruf von
Komponente „Aktion“ Button
.
Um die zusammensetzbare Funktion in ansichtsbasierten Bildschirmen zu verwenden, erstellen Sie einen benutzerdefinierten Ansichts-Wrapper,
geht vom AbstractComposeView
an. In der überschriebenen zusammensetzbaren Content
-Funktion
platzieren Sie die von Ihnen erstellte zusammensetzbare Funktion in Ihrem Design "Schreiben", wie in der
Beispiel unten:
@Composable fun CallToActionButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Button( colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary ), onClick = onClick, modifier = modifier, ) { Text(text) } } class CallToActionViewButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : AbstractComposeView(context, attrs, defStyle) { var text by mutableStateOf("") var onClick by mutableStateOf({}) @Composable override fun Content() { YourAppTheme { CallToActionButton(text, onClick) } } }
Beachten Sie, dass die zusammensetzbaren Parameter innerhalb der benutzerdefinierten
angezeigt wird. Dadurch ist die benutzerdefinierte CallToActionViewButton
-Ansicht aufblasbar und nutzbar,
wie eine traditionelle Ansicht. Ein Beispiel dafür finden Sie unter View Binding.
unten:
class ViewBindingActivity : ComponentActivity() { private lateinit var binding: ActivityExampleBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root) binding.callToAction.apply { text = getString(R.string.greeting) onClick = { /* Do something */ } } } }
Wenn die benutzerdefinierte Komponente einen veränderbaren Status enthält, lesen Sie den Hilfeartikel Statusquelle der Wahrheit.
Aufteilungsstatus gegenüber Präsentation priorisieren
Traditionell ist ein View
zustandsorientiert. Ein View
verwaltet Felder, die
beschreiben, was angezeigt werden soll und wie es angezeigt werden soll. Wenn Sie
Konvertieren Sie View
in „Compose“ und trennen Sie die gerenderten Daten
Einen unidirektionalen Datenfluss erreichen, wie unter Zustandswinden näher erläutert.
Ein View
hat beispielsweise ein visibility
-Attribut, das beschreibt,
sichtbar, unsichtbar oder weg. Dies ist eine inhärente Eigenschaft von View
. Während
Andere Codeteile können die Sichtbarkeit einer View
ändern, nur die View
die aktuelle Sichtbarkeit kennen. Die Logik, mit der sichergestellt wird,
Ein View
sichtbar ist, kann fehleranfällig sein und ist oft mit dem View
verknüpft
selbst.
Mit Compose können Sie dagegen ganz einfach mithilfe bedingter Logik in Kotlin völlig unterschiedliche Composeables anzeigen:
@Composable fun MyComposable(showCautionIcon: Boolean) { if (showCautionIcon) { CautionIcon(/* ... */) } }
CautionIcon
muss nicht wissen oder sich darum kümmern, warum es angezeigt wird,
und es gibt kein Konzept von visibility
: Es ist entweder in der Komposition
nicht.
Durch eine klare Trennung der Statusverwaltung und der Präsentationslogik können Sie die Darstellung von Inhalten als Umwandlung des Zustands in die Benutzeroberfläche beliebig ändern. Wenn Sie den Status bei Bedarf hochladen können, sind auch Komponenten wiederverwendbarer, da die Zuweisung von Status flexibler ist.
Gekapselte und wiederverwendbare Komponenten hochstufen
View
-Elemente haben oft eine Vorstellung davon, wo sie sich befinden: innerhalb einer Activity
, einem
Dialog
, eine Fragment
oder irgendwo innerhalb einer anderen View
-Hierarchie. Weil
Sie werden oft von statischen Layoutdateien überhöht, denn die Gesamtstruktur eines
View
ist in der Regel sehr starr. Dies führt zu einer engeren Kopplung und macht es
schwieriger ist, dass ein View
geändert oder wiederverwendet werden kann.
Ein benutzerdefiniertes View
kann beispielsweise davon ausgehen, dass es eine untergeordnete Ansicht eines bestimmten
mit einer bestimmten ID eingeben und die zugehörigen Eigenschaften direkt
Aktion ausführen. Dadurch werden diese View
-Elemente eng miteinander verbunden: das benutzerdefinierte View
-Element.
Das Gerät stürzt ab oder ist kaputt, wenn das Kind das Kind nicht finden kann und es wahrscheinlich nicht
ohne das benutzerdefinierte übergeordnete Element View
wiederverwendet werden.
Mit wiederverwendbaren Compose-Elementen ist das in Compose weniger ein Problem. Eltern können geben Sie ganz einfach Status und Callbacks an, sodass Sie wiederverwendbare zusammensetzbare Funktionen ohne zu wissen, wo sie verwendet werden.
@Composable fun AScreen() { var isEnabled by rememberSaveable { mutableStateOf(false) } Column { ImageWithEnabledOverlay(isEnabled) ControlPanelWithToggle( isEnabled = isEnabled, onEnabledChanged = { isEnabled = it } ) } }
Im obigen Beispiel sind alle drei Teile stärker gekapselt und weniger gekoppelt:
ImageWithEnabledOverlay
muss nur wissen, wie die aktuelleisEnabled
ist. Es muss nicht wissen, dassControlPanelWithToggle
existiert, oder sogar wie es steuerbar ist.ControlPanelWithToggle
weiß nicht, dassImageWithEnabledOverlay
existiert. Es kann null, eine oder mehrere Möglichkeiten zur Anzeige vonisEnabled
geben.ControlPanelWithToggle
muss nicht geändert werden.Für das übergeordnete Element spielt es keine Rolle, wie tief verschachtelt
ImageWithEnabledOverlay
ist. oderControlPanelWithToggle
. Diese Kinder könnten Veränderungen animieren, den Austausch von Inhalten oder die Weitergabe von Inhalten an andere Kinder.
Dieses Muster wird als Inversion of Control bezeichnet. Weitere Informationen dazu finden Sie weiter unten.
finden Sie in der CompositionLocal
-Dokumentation.
Umgang mit Änderungen der Bildschirmgröße
Unterschiedliche Ressourcen für unterschiedliche Fenstergrößen
ist eine der wichtigsten Möglichkeiten,
responsive View
-Layouts erstellen. Qualifizierte Ressourcen sind zwar weiterhin eine Option,
für Layoutentscheidungen
auf Bildschirmebene vereinfacht,
ganz im Code mit normaler
bedingter Logik. Weitere Informationen finden Sie unter Fenstergrößenklassen.
Weitere Informationen finden Sie im Hilfeartikel Unterstützung verschiedener Bildschirmgrößen. erfahren Sie, wie Sie mit Compose adaptive UIs erstellen.
Verschachteltes Scrollen mit Ansichten
Weitere Informationen zum Aktivieren der verschachtelten Scrolling-Interoperabilität zwischen scrollable View-Elemente und scrollbarer zusammensetzbarer Funktionen, die in beide Richtungen verschachtelt sind. Lesen Verschachtelte Scroll-Interoperabilität:
In RecyclerView
schreiben
Zusammensetzbare Funktionen in RecyclerView
sind seit Version RecyclerView
leistungsfähig
1.3.0-alpha02. Stellen Sie sicher, dass Sie mindestens Version 1.3.0-alpha02 von
RecyclerView
, um diese Vorteile zu sehen.
WindowInsets
-Interoperabilität mit Ansichten
Möglicherweise müssen Sie die Standardeinfügungen überschreiben, wenn Ihr Bildschirm sowohl Ansichten als auch Erstellen Sie Code in derselben Hierarchie. In diesem Fall müssen Sie angeben, welche der beiden Seiten die Einleger verwenden und welche sie ignorieren soll.
Wenn Ihr äußerstes Layout beispielsweise ein Android-View-Layout ist, sollten Sie die Einzüge im View-System verwenden und für Compose ignorieren.
Wenn es sich bei Ihrem äußersten Layout um eine zusammensetzbare Funktion handelt, sollten Sie das Element
Einfügungen in Compose und füllen Sie die AndroidView
zusammensetzbaren Funktionen entsprechend aus.
Standardmäßig verbraucht jeder ComposeView
alle Einfügungen an der
WindowInsetsCompat
Verbrauchslevel. Wenn Sie dieses Standardverhalten ändern möchten, setzen Sie ComposeView.consumeWindowInsets
auf false
.
Weitere Informationen finden Sie unter WindowInsets
in Compose.
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Emojis anzeigen
- Material Design 2 in Compose
- Fenstereinblendungen in „Compose“