Na tej stronie znajdziesz ogólny przegląd warstw architektury, z których składa się Jetpack Compose, oraz podstawowych zasad, które leżą u podstaw tego projektu.
Jetpack Compose nie jest pojedynczym, monolitycznym projektem. Składa się z wielu modułów, które są łączone w pełną platformę. Znajomość różnych modułów, z których składa się Jetpack Compose, umożliwia:
- Używaj odpowiedniego poziomu abstrakcji do tworzenia aplikacji lub biblioteki
- Dowiedz się, kiedy możesz przejść na niższy poziom, aby uzyskać większą kontrolę lub możliwość dostosowania
- Zminimalizuj zależności
Warstwy
Główne warstwy Jetpack Compose to:
Rysunek 1. Główne warstwy Jetpack Compose.
Każda warstwa jest zbudowana na niższych poziomach, łącząc funkcje w celu tworzenia komponentów wyższego poziomu. Każda warstwa jest oparta na publicznych interfejsach API warstw niższych, co umożliwia weryfikację granic modułów i w razie potrzeby zastąpienie dowolnej warstwy. Przyjrzyjmy się tym warstwom od dołu.
- Czas trwania
- Ten moduł zawiera podstawowe informacje o środowisku wykonawczym Compose, takie jak
remember
,mutableStateOf
, adnotacja@Composable
iSideEffect
. Możesz rozważyć bezpośrednie korzystanie z tej warstwy, jeśli potrzebujesz tylko funkcji zarządzania drzewem w Compose, a nie interfejsu. - Interfejs
- Warstwa interfejsu składa się z wielu modułów (
ui-text
,ui-graphics
,ui-tooling
, itp.). Te moduły implementują podstawowe elementy zestawu narzędzi interfejsu, takie jakLayoutNode
,Modifier
, moduły obsługi danych wejściowych, niestandardowe układy i rysowanie. Możesz rozbudować tę warstwę, jeśli potrzebujesz tylko podstawowych pojęć związanych z zestawem narzędzi interfejsu. - Fundacja
- Ten moduł zawiera niezależne od systemu projektowania bloki konstrukcyjne interfejsu Compose, takie jak
Row
,Column
,LazyColumn
, rozpoznawanie określonych gestów itp. Możesz wykorzystać warstwę podstawową do utworzenia własnego systemu projektowania. - Materiał
- Ten moduł zawiera implementację systemu Material Design dla interfejsu Compose, w tym system motywów, komponenty ze stylami, efekty falowania i ikony. Korzystaj z tej warstwy, gdy używasz Material Design w aplikacji.
Zasady projektowania
Podstawową zasadą Jetpack Compose jest udostępnianie małych, wyspecjalizowanych elementów funkcjonalności, które można łączyć (komponować) ze sobą, zamiast kilku monolitycznych komponentów. Takie podejście ma wiele zalet.
Kontrola
Komponenty wyższego poziomu zwykle wykonują więcej czynności, ale ograniczają zakres bezpośredniej kontroli. Jeśli potrzebujesz większej kontroli, możesz „przejść” do komponentu niższego poziomu.
Jeśli na przykład chcesz animować kolor komponentu, możesz użyć interfejsu API animateColorAsState
:
val color = animateColorAsState(if (condition) Color.Green else Color.Red)
Jeśli jednak chcesz, aby komponent zawsze zaczynał się od koloru szarego, nie możesz tego zrobić za pomocą tego interfejsu API. Zamiast tego możesz użyć interfejsu API niższego poziomu:Animatable
val color = remember { Animatable(Color.Gray) } LaunchedEffect(condition) { color.animateTo(if (condition) Color.Green else Color.Red) }
Interfejs API wyższego poziomu animateColorAsState
jest oparty na interfejsie API niższego poziomu Animatable
. Korzystanie z interfejsu API niższego poziomu jest bardziej skomplikowane, ale daje większą kontrolę. Wybierz poziom abstrakcji, który najlepiej odpowiada Twoim potrzebom.
Dostosowywanie
Składanie komponentów wyższego poziomu z mniejszych elementów ułatwia dostosowywanie komponentów w razie potrzeby. Rozważmy na przykład implementację Button
udostępnianą przez warstwę Material:
@Composable fun Button( // … content: @Composable RowScope.() -> Unit ) { Surface(/* … */) { CompositionLocalProvider(/* … */) { // set LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { Row( // … content = content ) } } } }
Button
składa się z 4 komponentów:
Materiał
Surface
zapewniający tło, kształt, obsługę kliknięć itp.A
CompositionLocalProvider
który zmienia przezroczystość treści, gdy przycisk jest włączony lub wyłączony.A
ProvideTextStyle
ustawia domyślny styl tekstu do użycia.Row
określa domyślne zasady układu treści przycisku.
Aby uprościć strukturę, pominęliśmy niektóre parametry i komentarze, ale cały komponent ma tylko około 40 wierszy kodu, ponieważ po prostu łączy te 4 komponenty w celu wdrożenia przycisku. Komponenty takie jak Button
określają, które parametry mają być udostępniane, aby umożliwić typowe dostosowania, ale jednocześnie nie dopuścić do nadmiernej liczby parametrów, które mogłyby utrudnić korzystanie z komponentu. Komponenty Material Design oferują na przykład dostosowania określone w systemie Material Design, co ułatwia stosowanie zasad tego systemu.
Jeśli jednak chcesz wprowadzić zmiany wykraczające poza parametry komponentu, możesz „zejść” o poziom niżej i utworzyć rozwidlenie komponentu. Na przykład Material Design określa, że przyciski powinny mieć tło w jednolitym kolorze. Jeśli potrzebujesz tła z gradientem, ta opcja nie jest obsługiwana przez parametry Button
. W takim przypadku możesz użyć implementacji Material Button
jako punktu odniesienia i utworzyć własny komponent:
@Composable fun GradientButton( // … background: List<Color>, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background( Brush.horizontalGradient(background) ) ) { CompositionLocalProvider(/* … */) { // set material LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { content() } } } }
Powyższa implementacja nadal korzysta z komponentów z warstwy Material, takich jak koncepcje bieżącej wartości alfa treści i bieżącego stylu tekstu. Zastępuje jednak materiał Surface
elementem Row
i nadaje mu styl, aby uzyskać pożądany wygląd.
Jeśli nie chcesz w ogóle korzystać z koncepcji Material, np. podczas tworzenia własnego systemu projektowania, możesz używać tylko komponentów warstwy podstawowej:
@Composable fun BespokeButton( // … backgroundColor: Color, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background(backgroundColor) ) { // No Material components used content() } }
Jetpack Compose rezerwuje najprostsze nazwy dla komponentów najwyższego poziomu. Na przykład androidx.compose.material.Text
jest zbudowany na podstawie androidx.compose.foundation.text.BasicText
.
Dzięki temu możesz podać własną implementację z najbardziej rozpoznawalną nazwą, jeśli chcesz zastąpić wyższe poziomy.
Wybór odpowiedniej abstrakcji
Zgodnie z filozofią Compose, która polega na tworzeniu warstwowych komponentów wielokrotnego użytku, nie zawsze należy sięgać po bloki konstrukcyjne niższego poziomu. Wiele komponentów wyższego poziomu nie tylko oferuje więcej funkcji, ale często wdraża sprawdzone metody, takie jak obsługa ułatwień dostępu.
Jeśli na przykład chcesz dodać obsługę gestów do komponentu niestandardowego, możesz to zrobić od podstaw, korzystając z Modifier.pointerInput
. Istnieją jednak inne komponenty wyższego poziomu, które są na nim oparte i mogą stanowić lepszy punkt wyjścia, np. Modifier.draggable
, Modifier.scrollable
lub Modifier.swipeable
.
Z reguły lepiej jest korzystać z komponentu najwyższego poziomu, który oferuje potrzebne funkcje, aby korzystać ze sprawdzonych metod, które zawiera.
Więcej informacji
Przykład tworzenia niestandardowego systemu projektowania znajdziesz w przykładzie Jetsnack.
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy JavaScript jest wyłączony.
- Kotlin w Jetpack Compose
- Listy i siatki
- Efekty uboczne w Compose