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 ansichtsbasierte Apps sind drei Material-Versionen verfügbar:
- Material Design 1 mit dem
AppCompat-Bibliothek (z.B.
Theme.AppCompat.*
) - Material Design 2 mithilfe der
MDC – Android
Bibliothek (z.B.
Theme.MaterialComponents.*
) - Material Design 3 mithilfe der
MDC – Android
Bibliothek (z.B.
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
beim Erstellen neuer Bildschirme in „Compose“, unabhängig von der Materialversion
Verwenden Sie für das von Ihnen verwendete Design MaterialTheme
vor allen
zusammensetzbare Funktionen, die Benutzeroberflächen aus den Compose Material Libraries 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.
Gemischte Benutzeroberfläche zum Schreiben und Ansehen 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) verwenden, 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()
enthalten, 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 zusammensetzbaren Funktionen beispielsweise in einer Aktivität gehostet werden, ist viewModel()
immer
gibt dieselbe Instanz zurück, die erst gelöscht wird, wenn die Aktivität abgeschlossen ist.
Im folgenden Beispiel wird derselbe Nutzer („user1“) zweimal begrüßt, weil
dieselbe GreetingViewModel
-Instanz in allen zusammensetzbaren Funktionen unter der
Host-Aktivitäten. 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 Navigationsdiagramme auch ViewModel
-Elemente abdecken,
Ziel in einem Navigationsdiagramm eine andere Instanz von ViewModel
haben.
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 die Funktion „Verfassen“ in einem Bereich der Benutzeroberfläche verwenden, kann es vorkommen,
Daten des
Systemcodes der Ansicht freigeben müssen. Wenn möglich, empfehlen wir Ihnen,
den gemeinsamen Status in einer anderen Klasse kapseln, die den Best Practices für UDFs entspricht
von beiden Plattformen genutzt werden. Beispiel: In einer ViewModel
, die einen Stream des
freigegebene Daten, um Datenaktualisierungen durchzuführen.
Dies ist jedoch nicht immer möglich, wenn die freizugebenden Daten veränderlich oder sind eng mit einem UI-Element verbunden. In diesem Fall muss ein System die Quelle und dieses System alle Datenaktualisierungen an das andere System weitergeben muss. Als allgemeine Faustregel: Die "Source of Truth" sollte dem Element gehören, liegt näher an der Untergrenze der UI-Hierarchie.
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.
Ihre Analysebibliothek bietet Ihnen
beispielsweise die Möglichkeit, Nutzer zu segmentieren,
durch Hinzufügen benutzerdefinierter Metadaten (in diesem Beispiel Nutzereigenschaften)
auf alle nachfolgenden Analyseereignisse anwenden. 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 UI 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 müssen Sie sie möglicherweise sowohl in der E-Mail-Funktion
und ansichtsbasierte Bildschirme.
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
Ansicht. 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 änderbaren Status enthält, lesen Sie den Abschnitt Quelle des Status von 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 der Funktion „Schreiben“ lassen sich dagegen ganz unterschiedliche zusammensetzbare Funktionen mit bedingter Logik in Kotlin:
@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. Das die bei Bedarf winden können, machen Zusammensetzbare auch besser wiederverwendbar, ist flexibler.
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.
Dies ist in der Funktion „Mit wiederverwendbaren zusammensetzbaren Funktionen verfassen“ weniger problematisch. 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 explizit welches die Insets aufnehmen und welches sie ignorieren soll.
Wenn das äußerste Layout zum Beispiel ein Android View-Layout ist, sollten Sie
die Einsätze im View-System verarbeiten und beim Schreiben 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. Um dieses Standardverhalten zu ändern, legen Sie Folgendes fest:
ComposeView.consumeWindowInsets
an 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“