Ta strona zawiera ogólny przegląd warstw architektury, z których składa się Jetpack Compose, oraz podstawowych zasad, które determinują ten projekt.
Jetpack Compose nie jest pojedynczym projektem monolitycznym. Składa się on z kilku modułów, które są połączone w cały stos. Poznanie 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 „zejść” na niższy poziom, aby lepiej kontrolować lub dostosować.
- Ograniczanie zależności
Warstwy
Najważniejsze warstwy Jetpack Compose to:
Rysunek 1. Główne warstwy Jetpack Compose.
Każda warstwa jest budowana na niższych poziomach, łącząc funkcje w komponenty wyższego poziomu. Każda warstwa opiera się na publicznych interfejsach API niższych warstw, aby weryfikować granice modułu i umożliwiać zastępowanie dowolnej warstwy w razie potrzeby. Przyjrzyjmy się tym warstwom od dołu do góry.
- Czas działania
- Ten moduł zawiera podstawy dotyczące środowiska wykonawczego Compose, takie jak
remember
,mutableStateOf
,@Composable
oraz adnotacjeSideEffect
. Jeśli potrzebujesz tylko umiejętności zarządzania drzewem w usłudze Compose, a nie jej interfejsu użytkownika, możesz rozważyć tworzenie aplikacji bezpośrednio na tej warstwie. - UI
- Warstwa interfejsu składa się z wielu modułów (
ui-text
,ui-graphics
,ui-tooling
itp.). Te moduły zawierają podstawowe elementy zestawu narzędzi interfejsu, takie jakLayoutNode
,Modifier
, moduły obsługi danych wejściowych, układy niestandardowe i rysunki. Możesz wykorzystać tę warstwę, jeśli potrzebujesz tylko podstawowych koncepcji zestawu narzędzi interfejsu. - Fundamenty
- Ten moduł zawiera elementy konstrukcyjne interfejsu Compose niezależne od systemu projektowania, takie jak
Row
iColumn
,LazyColumn
, rozpoznawanie konkretnych gestów itp. Możesz tworzyć własne elementy na podstawie warstwy podstawowej, aby tworzyć własny system projektowania. - Materiał
- Ten moduł zapewnia implementację systemu Material Design dla interfejsu Compose, oferując system motywów, stylizowane komponenty, animacje fali i ikony. Wykorzystaj tę warstwę, korzystając z stylu Material Design w swojej aplikacji.
Zasady projektowania
Zasada przewodnia Jetpack Compose polega na dostarczaniu małych, skoncentrowanych elementów funkcjonalności, które można łączyć (czyli kompilować), zamiast kilku wielkich komponentów. Takie podejście ma wiele zalet.
Kontrola
Komponenty wyższego poziomu zwykle robią więcej, ale ograniczają Twoją bezpośrednią kontrolę. Jeśli potrzebujesz większej kontroli, możesz rozwinąć menu, aby użyć komponentu niższego poziomu.
Jeśli na przykład chcesz animować kolor komponentu, możesz użyć interfejsu animateColorAsState
:
val color = animateColorAsState(if (condition) Color.Green else Color.Red)
Jeśli jednak chcesz, aby komponent zawsze był szary, nie możesz tego zrobić za pomocą tego interfejsu API. Zamiast tego możesz przejść do interfejsu API Animatable
niższego poziomu:
val color = remember { Animatable(Color.Gray) } LaunchedEffect(condition) { color.animateTo(if (condition) Color.Green else Color.Red) }
Sam interfejs animateColorAsState
API wyższego poziomu jest oparty na interfejsie Animatable
API niższego poziomu. 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
W przypadku tworzenia komponentów wyższego poziomu z mniejszych elementów znacznie łatwiej jest je w razie potrzeby dostosować. Weź pod uwagę np. implementację Button
udostępnioną 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
z tłem, kształtem, obsługą kliknięć itp.CompositionLocalProvider
, która zmienia przezroczystość treści, gdy przycisk jest włączony lub wyłączony.ProvideTextStyle
ustawia domyślny styl tekstu, którego chcesz używaćElement
Row
określa domyślną zasadę układu treści przycisku.
Aby ułatwić zrozumienie struktury, pominęliśmy niektóre parametry i komentarze, ale cały komponent składa się z zaledwie około 40 wierszy kodu, ponieważ po prostu łączy te 4 elementy, aby zaimplementować przycisk. Komponenty takie jak Button
mają określone parametry, które udostępniają, aby zachować równowagę między umożliwieniem korzystania z częstych dostosowań a niekontrolowanym wzrostem liczby parametrów, który może utrudnić korzystanie z komponentu. Komponenty Material Design oferują np. opcje dostosowywania określone w systemie Material Design, dzięki czemu łatwo jest przestrzegać zasad tego interfejsu.
Jeśli jednak chcesz wprowadzić zmiany, które wykraczają poza parametry komponentu, możesz „zejść” o poziom niżej i stworzyć jego wersję. Na przykład Material Design określa, że przyciski powinny mieć jednolite kolorowe tło. Jeśli potrzebujesz tła z gradientem, parametry Button
nie obsługują tej opcji. W tym przypadku możesz użyć implementacji Material Button
jako 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ższe rozwiązanie nadal korzysta z komponentów z poziomu Material, takich jak alfa bieżącej zawartości i obecny styl tekstu. Jednak zastępuje materiał Surface
materiałem Row
i stylizuje go, aby uzyskać pożądany wygląd.
Jeśli nie chcesz w ogóle używać koncepcji Material Design, np. podczas tworzenia własnego systemu projektowania, możesz ograniczyć się do korzystania tylko z elementó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
opiera się na androidx.compose.foundation.text.BasicText
.
Dzięki temu, jeśli chcesz zastąpić wyższe poziomy, możesz utworzyć własną implementację z najbardziej widoczną nazwą.
Wybór odpowiedniego poziomu abstrakcji
Filozofia tworzenia warstwowych, wielokrotnego użytku komponentów w Compose oznacza, że nie zawsze musisz sięgać po elementy niższego poziomu. Wiele komponentów wyższego poziomu nie tylko oferuje więcej funkcji, ale często implementuje też najlepsze praktyki, takie jak obsługa ułatwień dostępu.
Jeśli na przykład chcesz dodać obsługę gestów do komponentu niestandardowego,
możesz utworzyć go od podstaw za pomocą
Modifier.pointerInput
ale są też wbudowane inne komponenty wyższego poziomu, które mogą zapewnić lepszy punkt wyjścia, np. Modifier.draggable
,
Modifier.scrollable
lub Modifier.swipeable
.
Zalecamy tworzenie komponentów na najwyższym poziomie, który zapewnia funkcje potrzebne do korzystania z uwzględnionych w nich sprawdzonych metod.
Więcej informacji
Przykładowy projekt systemu niestandardowego znajdziesz w pliku Jetsnack.
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy obsługa JavaScript jest wyłączona
- Kotlin w Jetpack Compose
- Listy i siatki
- Efekty uboczne w edytorze