Jetpack Compose oferuje implementację Material Design, kompleksowego systemu projektowania do tworzenia interfejsów cyfrowych. Komponenty Material Design (przyciski, karty, przełączniki itp.) są oparte na Material Theming, czyli systematycznym sposobie dostosowywania Material Design, aby lepiej odzwierciedlać markę produktu. Motyw Material zawiera atrybuty koloru, typografii i kształtu. Gdy dostosujesz te atrybuty, zmiany zostaną automatycznie odzwierciedlone w komponentach, których używasz do tworzenia aplikacji.
Jetpack Compose implementuje te koncepcje za pomocą funkcji kompozycyjnej MaterialTheme:
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
Skonfiguruj parametry przekazywane do MaterialTheme, aby dostosować motyw aplikacji.
Kolor
Kolory są modelowane w Compose za pomocą klasy Color, która przechowuje dane.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
Możesz je uporządkować w dowolny sposób (jako stałe najwyższego poziomu, w ramach singletonu lub zdefiniowane w tekście), ale zdecydowanie zalecamy określenie kolorów w motywie i pobieranie ich z niego. To podejście umożliwia obsługę ciemnego motywu i motywów zagnieżdżonych.
Compose udostępnia klasę Colors do modelowania systemu kolorów Material. Colors udostępnia funkcje kreatora do tworzenia zestawów jasnych lub ciemnych kolorów:
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 przekazać je do MaterialTheme:
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
Używanie kolorów motywu
Możesz pobrać Colors przekazany do elementu kompozycyjnego MaterialTheme za pomocą MaterialTheme.colors.
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
Kolor powierzchni i treści
Wiele komponentów akceptuje parę kolorów i kolor treści:
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
Dzięki temu możesz nie tylko ustawić kolor elementu kompozycyjnego, ale też podać domyślny kolor treści, czyli elementów kompozycyjnych, które się w nim znajdują. Wiele funkcji kompozycyjnych domyślnie używa tego koloru treści. Na przykład Text opiera swój kolor na kolorze treści elementu nadrzędnego, a Icon używa tego koloru do ustawienia odcienia.
Metoda contentColorFor() pobiera odpowiedni kolor „on” dla dowolnych kolorów motywu. Jeśli na przykład ustawisz kolor tła primary na Surface, ta funkcja ustawi kolor treści na onPrimary.
Jeśli ustawisz kolor tła spoza motywu, musisz też określić odpowiedni kolor treści. Użyj LocalContentColor, aby pobrać preferowany kolor treści dla bieżącego tła na danej pozycji w hierarchii.
Przezroczystość treści
Często chcesz zmieniać stopień wyróżnienia treści, aby przekazać ich znaczenie i zapewnić hierarchię wizualną. Zalecenia dotyczące czytelności tekstu w Material Design sugerują stosowanie różnych poziomów przezroczystości do przekazywania różnych poziomów ważności.
Jetpack Compose implementuje to za pomocą LocalContentAlpha. Możesz określić wartość alfa treści dla hierarchii, podając wartość dla tego CompositionLocal. Zagnieżdżone elementy kompozycyjne mogą używać tej wartości, aby zastosować przezroczystość do swoich treści. Na przykład Text i Icon domyślnie używają kombinacji LocalContentColor dostosowanej do używania LocalContentAlpha. Material określa pewne 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( // ... ) }
Więcej informacji o CompositionLocal znajdziesz w artykule Dane o zasięgu lokalnym z CompositionLocal.
ContentAlpha.high. Drugi wiersz zawiera mniej ważne metadane, dlatego używa stylu ContentAlpha.medium.Ciemny motyw
W Compose jasny i ciemny motyw implementuje się, przekazując różne zestawy 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 MaterialTheme jest opakowany we własną funkcję typu „composable”, która akceptuje parametr określający, czy używać ciemnego motywu. W takim przypadku funkcja pobiera domyślną wartość darkTheme, sprawdzając ustawienie motywu urządzenia.
Możesz użyć takiego kodu, aby sprawdzić, czy bieżące Colors są jasne czy ciemne:
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 Material Design powierzchnie w ciemnych motywach o wyższych wysokościach otrzymują nakładki wysokości, które rozjaśniają ich tło. Im wyższa jest wysokość powierzchni (im bliżej jest ona domniemanego źródła światła), tym jaśniejsza się staje.
Funkcja kompozycyjna Surface automatycznie stosuje te nakładki, gdy używane są ciemne kolory. Podobnie działają inne funkcje kompozycyjne Material, które używają powierzchni:
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
surface. Karty i dolny pasek nawigacyjny znajdują się na różnych poziomach nad tłem, dlatego mają nieco inne kolory – karty są jaśniejsze niż tło, a dolny pasek nawigacyjny jest jaśniejszy niż karty.W przypadku niestandardowych scenariuszy, które nie obejmują elementu Surface, użyj LocalElevationOverlay, czyli elementu CompositionLocal zawierającego element ElevationOverlay używany 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 funkcji kompozycyjnych:
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
Ograniczone akcenty kolorystyczne
W przypadku ciemnych motywów Material zaleca stosowanie ograniczonych akcentów kolorystycznych, preferując w większości przypadków kolor surface zamiast koloru primary. Komponenty Material, takie jak TopAppBar i BottomNavigation, domyślnie implementują to zachowanie.
W przypadku scenariuszy niestandardowych użyj właściwości rozszerzenia primarySurface:
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
Typografia
Material definiuje system typów, zachęcając do używania niewielkiej liczby stylów o nazwach semantycznych.
Compose implementuje system typów za pomocą klas Typography, TextStyle i związanych z czcionkami. Konstruktor Typography oferuje domyślne wartości 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 pisma w całym dokumencie, określ parametr defaultFontFamily i pomiń parametr fontFamily we wszystkich elementach TextStyle:
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
Używanie stylów tekstu
Dostęp do elementów TextStyle można uzyskać za pomocą MaterialTheme.typography. Pobierz elementy TextStyle w ten sposób:
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )
Kształt
Material definiuje system kształtów, który pozwala określać kształty dużych, średnich i małych komponentów.
Compose implementuje system kształtów za pomocą klasy Shapes, która umożliwia określenie CornerBasedShape dla każdej kategorii rozmiaru:
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 domyślnie używa tych kształtów. Na przykład Button, TextField i FloatingActionButton domyślnie mają rozmiar mały, AlertDialog – średni, a ModalDrawer – duży. Pełne mapowanie znajdziesz w dokumentacji schematu kształtów.
Używanie kształtów
Dostęp do elementów Shape można uzyskać za pomocą MaterialTheme.shapes. Pobierz elementy Shape za pomocą takiego kodu:
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
Style domyślne
W Compose nie ma odpowiednika stylów domyślnych z widoków Androida. Podobną funkcjonalność możesz zapewnić, tworząc własne funkcje typu „composable” overload, które opakowują komponenty Material Design. Aby na przykład utworzyć styl przycisku, umieść przycisk we własnej funkcji typu „composable”, bezpośrednio ustawiając parametry, które chcesz lub musisz zmienić, i udostępniając inne jako parametry funkcji typu „composable”.
@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 motywów
Odpowiednik nakładek motywu z widoków Androida możesz uzyskać w Compose, zagnieżdżając komponenty MaterialTheme. Ponieważ MaterialTheme domyślnie ustawia kolory, typografię i kształty na bieżącą wartość motywu, wszystkie inne parametry zachowują swoje wartości domyślne, gdy motyw ustawia tylko jeden z tych parametrów.
Ponadto podczas migracji ekranów opartych na widokach do Compose zwróć uwagę na użycie atrybutu android:theme. Prawdopodobnie potrzebujesz nowego elementu MaterialTheme w tej części drzewa interfejsu Compose.
W tym przykładzie ekran szczegółów używa elementu PinkTheme na większości ekranu, a elementu BlueTheme w sekcji powiązanej. Tę koncepcję ilustrują poniższy zrzut ekranu i kod:
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
Stany komponentów
Komponenty Material, z którymi można wchodzić w interakcje (klikać, przełączać itp.), mogą mieć różne stany wizualne. Możliwe stany to m.in. włączony, wyłączony i naciśnięty.
Funkcje kompozycyjne często mają parametr enabled. Ustawienie wartości false uniemożliwia interakcję i zmienia właściwości, takie jak kolor i wysokość, aby wizualnie przekazać stan komponentu.
enabled = true (po lewej) i enabled = false (po prawej).W większości przypadków możesz polegać na wartościach domyślnych, takich jak kolor i wysokość. Jeśli chcesz skonfigurować wartości używane w różnych stanach, możesz skorzystać z klas i funkcji ułatwiających pracę. Przyjrzyj się temu przykładowi przycisku:
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 ) ) { /* ... */ }
enabled = true (po lewej) i enabled = false (po prawej) z dostosowanymi wartościami koloru i wysokości.Kręgi na wodzie
Komponenty Material Design używają efektu fali, aby wskazać, że użytkownik wchodzi z nimi w interakcję. Jeśli w hierarchii używasz MaterialTheme, Ripple jest używany jako domyślny Indication w modyfikatorach takich jak clickable i indication.
W większości przypadków możesz użyć domyślnego ustawienia 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ć klasę RippleTheme i użyć funkcji użytkowych defaultRippleColor i defaultRippleAlpha. Następnie możesz podać niestandardowy motyw efektu fali w hierarchii, używając 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 ) }
RippleConfiguration.Więcej informacji
Więcej informacji o Material Theming w Compose znajdziesz w tych materiałach.
Codelabs
Filmy
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Niestandardowe systemy projektowania w Compose
- Migracja z Material 2 do Material 3 w Compose
- Ułatwienia dostępu w Compose