Jetpack Compose udostępnia implementację Material Design, czyli kompleksowego systemu projektowania interfejsów cyfrowych. Komponenty Material Design (przyciski, karty, przełączniki itd.) są oparte na motywie Material Design, co umożliwia systematyczne dostosowywanie interfejsu Material Design, aby lepiej odzwierciedlał markę produktu. Motyw materiału zawiera atrybuty kolor, typografia i kształt. Gdy dostosujesz te atrybuty, zmiany zostaną automatycznie odzwierciedlone w komponentach, których używasz do utworzenia aplikacji.
Jetpack Compose implementuje te koncepcje za pomocą funkcji kompozycyjnej MaterialTheme
:
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
Skonfiguruj parametry przesyłane do MaterialTheme
, aby zastosować motyw aplikacji.
Rysunek 1. Pierwszy zrzut ekranu przedstawia aplikację, która nie konfiguruje MaterialTheme
, więc używa stylu domyślnego. Drugi zrzut ekranu pokazuje aplikację, która przekazuje parametry do MaterialTheme
, aby dostosować styl.
Kolor
Kolory są modelowane w Compose za pomocą klasy Color
, prostej klasy przechowywania danych.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
Możesz uporządkować je w dowolny sposób (jako stałe najwyższego poziomu, w jednym tonie lub zdefiniowane w tekście), zdecydowanie zalecamy jednak określenie kolorów w motywie i pobranie ich stamtąd. Takie podejście pozwala na łatwą obsługę ciemnego motywu i motywów zagnieżdżonych.
Rysunek 2. System kolorów Material.
Tworzenie udostępnia klasę Colors
do modelowania systemu kolorów Material Design. Colors
udostępnia funkcje kreatora umożliwiające tworzenie zestawów kolorów jasnych lub ciemnych:
private val Yellow200 = Color(0xffffeb46) private val Blue200 = Color(0xff91a4fc) // ... private val DarkColors = darkColors( primary = Yellow200, secondary = Blue200, // ... ) private val LightColors = lightColors( primary = Yellow500, primaryVariant = Yellow400, secondary = Blue700, // ... )
Po zdefiniowaniu Colors
możesz je przekazać do MaterialTheme
:
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
Używanie kolorów motywu
Możesz pobrać Colors
dostarczone do MaterialTheme
funkcji kompozycyjnej, używając MaterialTheme.colors
.
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
Kolor powierzchni i treści
Wiele komponentów akceptuje parę kolorów i kolorów treści:
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
Pozwala to nie tylko ustawić kolor funkcji kompozycyjnej, ale także domyślny kolor treści oraz zawartych w niej elementów kompozycyjnych. Wiele elementów kompozycyjnych domyślnie używa tego koloru treści. Na przykład Text
określa swój kolor na podstawie koloru treści elementu nadrzędnego, a Icon
używa tego koloru do ustawienia odcienia.
Rysunek 3. Ustawienie różnych kolorów tła umożliwia uzyskanie różnych kolorów tekstu i ikon.
Metoda contentColorFor()
pobiera odpowiedni kolor „on” dla dowolnego koloru motywu. Jeśli na przykład ustawisz kolor tła primary
w elemencie Surface
, będzie ona używać tej funkcji do ustawienia onPrimary
jako koloru treści. Jeśli ustawisz kolor tła inny niż motyw, musisz też określić odpowiedni kolor treści. Użyj funkcji LocalContentColor
, aby pobrać preferowany kolor treści dla bieżącego tła w określonym miejscu w hierarchii.
Treść alfa
Często trzeba zróżnicować umiejscowienie treści, aby podkreślić ich znaczenie i zapewnić wizualną hierarchię. Rekomendacje dotyczące czytelności tekstu w stylu Material Design zalecają stosowanie różnych poziomów nieprzezroczystości, aby wskazywać różne poziomy znaczenia.
Jetpack Compose implementuje to za pomocą LocalContentAlpha
.
Możesz określić wersję alfa treści dla hierarchii, podając wartość dla obiektu CompositionLocal
.
Zagnieżdżone elementy kompozycyjne mogą używać tej wartości, aby stosować do swoich treści grupę alfa.
Na przykład Text
i Icon
domyślnie używają kombinacji LocalContentColor
dopasowanej do wartości LocalContentAlpha
. Material określa niektóre standardowe wartości alfa (high
, medium
, disabled
), które są modelowane przez obiekt ContentAlpha
.
// By default, both Icon & Text use the combination of LocalContentColor & // LocalContentAlpha. De-emphasize content by setting content alpha CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( // ... ) } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Icon( // ... ) Text( // ... ) }
Aby dowiedzieć się więcej na temat: CompositionLocal
, zapoznaj się z danymi o zakresie lokalnym dzięki programowi CompositionLocal.
Rysunek 4. Zastosuj różne poziomy nacisku na tekst, aby wizualnie przedstawić hierarchię informacji. Pierwszy wiersz tekstu to tytuł, który zawiera najważniejszą informację, dlatego zawiera element ContentAlpha.high
. Drugi wiersz zawiera mniej ważne metadane, więc korzysta z elementu ContentAlpha.medium
.
Ciemny motyw
Aby wdrożyć jasne i ciemne motywy w interfejsie tworzenia, musisz udostępnić różne zestawy elementów Colors
do funkcji kompozycyjnej MaterialTheme
:
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
W tym przykładzie funkcja MaterialTheme
jest zawarta we własnej funkcji kompozycyjnej, która akceptuje parametr określający, czy użyć ciemnego motywu. W tym przypadku funkcja pobiera wartość domyślną darkTheme
, wysyłając zapytanie o ustawienie motywu urządzenia.
Aby sprawdzić, czy bieżące urządzenie Colors
jest jasne, czy ciemne, możesz użyć kodu podobnego do tego:
val isLightTheme = MaterialTheme.colors.isLight Icon( painterResource( id = if (isLightTheme) { R.drawable.ic_sun_24 } else { R.drawable.ic_moon_24 } ), contentDescription = "Theme" )
Nakładki wysokości
W przypadku interfejsu Material Design powierzchnie z ciemnym motywem o większych wysokościach otrzymują nakładki wysokości, które rozjaśniają tło. Im większa wysokość powierzchni, tym lżejsza, tym bardziej zbliżona do domniemanego źródła światła.
Te nakładki są stosowane automatycznie przez funkcję Surface
kompozycyjną, gdy używasz ciemnych kolorów, oraz przez dowolny inny element kompozycyjny Material, który używa powierzchni:
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
Rysunek 5. Tło kart i menu nawigacyjnego u dołu mają kolor surface
. Karty i dolna część nawigacji znajdują się na różnych poziomach wysokości nad tłem, dlatego mają nieco inne kolory – karty są jaśniejsze niż tło, a dolna część nawigacji jest jaśniejsza.
W przypadku niestandardowych scenariuszy, które nie obejmują Surface
, użyj LocalElevationOverlay
, czyli CompositionLocal
zawierającego ElevationOverlay
używane przez komponenty Surface
:
// Elevation overlays // Implemented in Surface (and any components that use it) val color = MaterialTheme.colors.surface val elevation = 4.dp val overlaidColor = LocalElevationOverlay.current?.apply( color, elevation )
Aby wyłączyć nakładki wysokości, podaj null
w wybranym punkcie hierarchii kompozycyjnej:
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
Ograniczone kolorowe akcenty
Zespół Material zaleca stosowanie ograniczonych kolorów akcentów dla ciemnych motywów i w większości przypadków preferuje użycie koloru surface
zamiast koloru primary
. Obiekty kompozycyjne materiału, takie jak TopAppBar
i BottomNavigation
,
implementują to zachowanie domyślnie.
Rysunek 6. Ciemny motyw Material Design z ograniczoną liczbą kolorów. Górny pasek aplikacji używa koloru podstawowego w jasnym motywie i koloru powierzchni w ciemnym motywie.
W niestandardowych scenariuszach używaj właściwości rozszerzenia primarySurface
:
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
Typografia
Material określa system typów, co zachęca do używania niewielkiej liczby stylów nazywanych semantycznie.
Rysunek 7. System rodzajów materiałów.
Funkcja tworzenia implementuje system typów z klasami Typography
, TextStyle
i powiązanymi z czcionkami. Konstruktor Typography
udostępnia wartości domyślne dla każdego stylu, więc możesz pominąć te, których nie chcesz dostosowywać:
val raleway = FontFamily( Font(R.font.raleway_regular), Font(R.font.raleway_medium, FontWeight.W500), Font(R.font.raleway_semibold, FontWeight.SemiBold) ) val myTypography = Typography( h1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W300, fontSize = 96.sp ), body1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W600, fontSize = 16.sp ) /*...*/ ) MaterialTheme(typography = myTypography, /*...*/) { /*...*/ }
Jeśli chcesz używać tego samego kroju czcionki w całym tekście, określ defaultFontFamily parameter
i pomiń fontFamily
we wszystkich elementach TextStyle
:
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
Używanie stylów tekstu
Dostęp do obiektów TextStyle
można uzyskać przez MaterialTheme.typography
. Pobierz TextStyle
w ten sposób:
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )
Rysunek 8. Wyraź swoją markę, korzystając z różnych krojów czcionek i stylów.
Kształt
Materiał określa system kształtów, który umożliwia definiowanie kształtów dla dużych, średnich i małych komponentów.
Rysunek 9. System Kształt Material Design.
Tworzenie implementuje system kształtów z klasą Shapes
, co pozwala określić właściwość CornerBasedShape
dla każdej kategorii rozmiarów:
val shapes = Shapes( small = RoundedCornerShape(percent = 50), medium = RoundedCornerShape(0f), large = CutCornerShape( topStart = 16.dp, topEnd = 0.dp, bottomEnd = 0.dp, bottomStart = 16.dp ) ) MaterialTheme(shapes = shapes, /*...*/) { /*...*/ }
Wiele komponentów używa tych kształtów domyślnie. Na przykład wartości Button
, TextField
i FloatingActionButton
są domyślnie ustawiane na małą wartość AlertDialog
, wartość domyślna to „medium”, a ModalDrawer
wartość domyślna to schemat kształtu.
Używanie kształtów
Dostęp do obiektów Shape
można uzyskać przez MaterialTheme.shapes
. Pobierz plik Shape
za pomocą tego kodu:
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
Rysunek 10. Użyj kształtów do przedstawienia marki lub stanu.
Style domyślne
W ramach tworzenia stylów domyślnych z widoków Androida nie ma odpowiednika tego koncepcji. Podobne funkcje możesz uzyskać, tworząc własne „przeciążone” funkcje kompozycyjne, które opakowują komponenty Material Design. Aby na przykład utworzyć styl przycisku, umieść go we własnej funkcji kompozycyjnej, bezpośrednio ustaw parametry, które chcesz zmienić, i udostępnij innym parametry jako parametry funkcji kompozycyjnej.
@Composable fun MyButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary ), onClick = onClick, modifier = modifier, content = content ) }
Nakładki z motywów
Odpowiednik nakładek motywuw widokach Androida w widoku tworzenia wiadomości możesz uzyskać, zagnieżdżając
MaterialTheme
elementy kompozycyjne. MaterialTheme
domyślnie ustawia kolory, typografię i kształty zgodnie z bieżącą wartością motywu, więc jeśli motyw ustawi tylko jeden z tych parametrów, pozostałe parametry zachowają wartości domyślne.
Podczas migracji ekranów opartych na widokach do funkcji tworzenia uważaj na przypadki użycia atrybutu android:theme
. Prawdopodobnie w tej części drzewa interfejsu tworzenia wiadomości będzie Ci potrzebny nowy element MaterialTheme
.
W przykładzie Sowa ekran szczegółów wyświetla się jako PinkTheme
przez większość ekranu, a następnie BlueTheme
dla powiązanej sekcji. Poniżej znajdziesz zrzut ekranu i kod.
Rysunek 11. Zagnieżdżone motywy w próbce Sowa.
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
Stany komponentów
Komponenty materiałów, z którymi można wchodzić w interakcje (kliknięte, przełączone itp.), mogą mieć różne stany wizualne. Dostępne stany to: włączone, wyłączone, naciśnięte itd.
Elementy kompozycyjne często mają parametr enabled
. Ustawienie wartości false
zapobiega interakcji i zmienia właściwości takie jak kolor i wysokości, aby wizualnie przedstawić stan komponentu.
Rysunek 12. Przycisk z klawiszami enabled = true
(po lewej) i enabled = false
(po prawej).
W większości przypadków można polegać na wartościach domyślnych, takich jak kolor i wysokość. Jeśli chcesz skonfigurować wartości używane w różnych stanach, dostępne są klasy i funkcje ułatwiające pracę. Zobacz przykład przycisku poniżej:
Button( onClick = { /* ... */ }, enabled = true, // Custom colors for different states colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary, disabledBackgroundColor = MaterialTheme.colors.onBackground .copy(alpha = 0.2f) .compositeOver(MaterialTheme.colors.background) // Also contentColor and disabledContentColor ), // Custom elevation for different states elevation = ButtonDefaults.elevation( defaultElevation = 8.dp, disabledElevation = 2.dp, // Also pressedElevation ) ) { /* ... */ }
Rysunek 13. Przycisk z kolorami enabled = true
(po lewej) i enabled = false
(po prawej) z dostosowanymi wartościami koloru i wysokości.
Plusk
Komponenty materiału wykorzystują echa, aby wskazać, z którymi elementami wchodzą w interakcję. Jeśli w hierarchii używasz parametru MaterialTheme
, jako domyślnego modyfikatora wewnętrznegoIndication
, np. clickable
i indication
, używany będzie element Ripple
.
W większości przypadków możesz polegać na domyślnej wartości Ripple
. Jeśli chcesz skonfigurować ich wygląd, możesz użyć RippleTheme
, aby zmienić właściwości takie jak kolor i alfa.
Możesz rozszerzyć zakres RippleTheme
i korzystać z funkcji narzędziowych defaultRippleColor
oraz defaultRippleAlpha
. Następnie możesz udostępnić w hierarchii niestandardowy motyw echo za pomocą LocalRippleTheme
:
@Composable fun MyApp() { MaterialTheme { CompositionLocalProvider( LocalRippleTheme provides SecondaryRippleTheme ) { // App content } } } @Immutable private object SecondaryRippleTheme : RippleTheme { @Composable override fun defaultColor() = RippleTheme.defaultRippleColor( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) @Composable override fun rippleAlpha() = RippleTheme.defaultRippleAlpha( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) }
Rysunek 14. Przyciski o różnych wartościach echa dostarczane przez: RippleTheme
.
Więcej informacji
Więcej informacji o podziale tematów na tematy w usłudze Compose znajdziesz w tych dodatkowych materiałach.
Ćwiczenia z programowania
Filmy
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy JavaScript jest wyłączony
- Niestandardowe systemy projektowania w usłudze Compose
- Migracja z Material 2 do Material 3 w sekcji Utwórz
- Ułatwienia dostępu przy tworzeniu wiadomości