Material Design 2 in Compose

Jetpack Compose bietet eine Implementierung von Material Design, einem umfassenden Designsystem für digitale Schnittstellen schaffen. Die Material Design-Komponenten (Schaltflächen, Karten, Schalter usw.) basieren auf Material Theming. Dies ist eine systematische Methode, Material Design so anzupassen, dass es die Marke Ihres Produkts besser widerspiegelt. Ein Material Das Design enthält die Attribute Farbe, Typografie und Form. Wenn Sie diese anpassen, -Attribute werden Ihre Änderungen automatisch in den von Ihnen verwendeten Komponenten um Ihre App zu entwickeln.

Jetpack Compose implementiert diese Konzepte mit der MaterialTheme zusammensetzbar:

MaterialTheme(
    colors = // ...
    typography = // ...
    shapes = // ...
) {
    // app content
}

Konfigurieren Sie die Parameter, die Sie an MaterialTheme übergeben, um das Design Ihrer Anwendung festzulegen.

Zwei kontrastierende Screenshots. Bei der ersten Option wird der Standard-
MaterialTheme-Stil verwendet,
im zweiten Screenshot
ein geänderter Stil.

Abbildung 1. Der erste Screenshot zeigt eine App, die keine Konfiguration MaterialTheme. Daher wird der Standardstil verwendet. Der zweite Screenshot zeigt eine App, die Parameter an MaterialTheme übergibt, um das Design anzupassen.

Farbe

Farben werden in Compose mit der Klasse Color modelliert, einer einfachen Datenspeicherklasse.

val Red = Color(0xffff0000)
val Blue = Color(red = 0f, green = 0f, blue = 1f)

Sie können diese beliebig organisieren (als Konstanten der obersten Ebene, Singleton-Format oder inline definiert, empfehlen wir dringend, Farben in Design ändern und die Farben von dort abrufen. So lassen sich dunkle Designs und verschachtelte Designs ganz einfach unterstützen.

Beispiel für die Farbvorlage des Designs

Abbildung 2. Das Farbsystem „Material“.

Compose bietet die Klasse Colors, um das Materialfarbsystem zu modellieren. Colors bietet Builder-Funktionen zum Erstellen von Sets mit hellen oder dunklen Farben:

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,
    // ...
)

Nachdem Sie Ihre Colors definiert haben, können Sie sie an eine MaterialTheme übergeben:

MaterialTheme(
    colors = if (darkTheme) DarkColors else LightColors
) {
    // app content
}

Designfarben verwenden

Sie können die Colors abrufen, die für die zusammensetzbare Funktion MaterialTheme bereitgestellt wurde: mit MaterialTheme.colors.

Text(
    text = "Hello theming",
    color = MaterialTheme.colors.primary
)

Oberflächen- und Inhaltsfarbe

Viele Komponenten akzeptieren ein Paar aus Farbe und Inhaltsfarbe:

Surface(
    color = MaterialTheme.colors.surface,
    contentColor = contentColorFor(color),
    // ...
) { /* ... */ }

TopAppBar(
    backgroundColor = MaterialTheme.colors.primarySurface,
    contentColor = contentColorFor(backgroundColor),
    // ...
) { /* ... */ }

So können Sie nicht nur die Farbe eines Composeables festlegen, sondern auch eine Standardfarbe für den Inhalt, also die darin enthaltenen Composeables, angeben. Viele Für zusammensetzbare Funktionen wird standardmäßig diese Inhaltsfarbe verwendet. Beispielsweise basiert die Farbe von Text auf der Inhaltsfarbe des übergeordneten Elements und Icon verwendet diese Farbe, um seine Tönung festzulegen.

Zwei Beispiele für dasselbe Banner in verschiedenen Farben

Abbildung 3 Wenn Sie unterschiedliche Hintergrundfarben festlegen, werden unterschiedliche Text- und Symbolfarben verwendet.

Die contentColorFor() den entsprechenden "on"-Wert Farbe für alle Designfarben. Wenn Sie beispielsweise für Surface die Hintergrundfarbe primary festlegen, wird mit dieser Funktion onPrimary als Inhaltsfarbe festgelegt. Wenn Sie eine Hintergrundfarbe festlegen, die kein Design ist, sollten Sie auch eine die richtige Farbe für den Inhalt hat. LocalContentColor verwenden um die bevorzugte Inhaltsfarbe für den aktuellen Hintergrund abzurufen. Position in der Hierarchie.

Alphaversion von Inhalten

Oft möchten Sie die Betonung von Inhalten variieren, um ihre Wichtigkeit zu vermitteln und eine visuelle Hierarchie zu schaffen. In den Empfehlungen für die Lesbarkeit von Text in Material Design wird empfohlen, unterschiedliche Opazitätsstufen zu verwenden, um unterschiedliche Wichtigkeitsstufen zu vermitteln.

Jetpack Compose implementiert dies über LocalContentAlpha. Sie können einen Alpha-Content für eine Hierarchie angeben, indem Sie einen Wert angeben. für diese CompositionLocal. Verschachtelte Daten zusammensetzbaren Funktionen können diesen Wert verwenden, um ihren Inhalt als Alpha zu behandeln. Für Text und Icon wird beispielsweise standardmäßig die Kombination LocalContentColor verwendet, die für die Verwendung von LocalContentAlpha angepasst ist. Material gibt einige Standard-Alphawerte an (high, medium, disabled), die vom ContentAlpha-Objekt modelliert werden.

// 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(
        // ...
    )
}

Weitere Informationen zu CompositionLocal finden Sie unter Daten auf lokaler Ebene mit CompositionLocal-Leitfaden.

Screenshot eines Artikeltitels mit verschiedenen Textebenen
Betonung

Abbildung 4: Verschiedene Betonungsstufen auf Text anwenden, um visuell zu kommunizieren Informationshierarchie gesprochen. Die erste Textzeile ist der Titel und enthält die wichtigsten Informationen. Daher wird hier ContentAlpha.high verwendet. Die zweite Zeile enthält weniger wichtige Metadaten und verwendet daher ContentAlpha.medium.

Dunkles Design

In Compose implementieren Sie helle und dunkle Designs, indem Sie dem MaterialTheme-Komposit verschiedene Colors-Elemente zur Verfügung stellen:

@Composable
fun MyTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    MaterialTheme(
        colors = if (darkTheme) DarkColors else LightColors,
        /*...*/
        content = content
    )
}

In diesem Beispiel ist MaterialTheme in einer eigenen zusammensetzbaren Funktion verpackt, die einen Parameter akzeptiert, der angibt, ob ein dunkles Design verwendet werden soll oder nicht. In In diesem Fall erhält die Funktion den Standardwert für darkTheme durch Abfrage der Einstellung für das Gerätedesign.

Mit diesem Code können Sie prüfen, ob die aktuellen Colors hell oder dunkel sind:

val isLightTheme = MaterialTheme.colors.isLight
Icon(
    painterResource(
        id = if (isLightTheme) {
            R.drawable.ic_sun_24
        } else {
            R.drawable.ic_moon_24
        }
    ),
    contentDescription = "Theme"
)

Höhen-Overlays

In Material werden Oberflächen in dunklen Designs mit höheren Elevation Höhen-Overlays, die um den Hintergrund aufzuhellen. Je höher die Höhe einer Oberfläche, also die Anhebung angedeutet, desto heller wird die Oberfläche.

Diese Overlays werden bei Verwendung der zusammensetzbaren Funktion Surface automatisch angewendet, sowie für alle anderen zusammensetzbaren Materialien, die eine Oberfläche verwenden, gehen Sie so vor:

Surface(
    elevation = 2.dp,
    color = MaterialTheme.colors.surface, // color will be adjusted for elevation
    /*...*/
) { /*...*/ }

Screenshot einer App mit leicht unterschiedlichen Farben für Elemente
bei verschiedenen Höhenunterschieden

Abbildung 5: Sowohl die Karten als auch die untere Navigationsleiste haben die Farbe surface als Hintergrund. Da sich die Karten und die untere Navigationsleiste auf unterschiedlichen Höhen über dem Hintergrund befinden, haben sie leicht unterschiedliche Farben: Die Karten sind heller als der Hintergrund und die untere Navigationsleiste ist heller als die Karten.

Für benutzerdefinierte Szenarien ohne Surface verwenden Sie LocalElevationOverlay, ein CompositionLocal mit den ElevationOverlay verwendet von Surface Komponenten:

// 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
)

Um Höhen-Overlays zu deaktivieren, geben Sie null an der gewünschten Stelle in einem zusammensetzbare Hierarchie:

MyTheme {
    CompositionLocalProvider(LocalElevationOverlay provides null) {
        // Content without elevation overlays
    }
}

Eingeschränkte Farbakzente

Material empfiehlt, begrenzte Farben anzuwenden Akzente für dunkles indem Sie die Farbe surface gegenüber der Farbe primary in in den meisten Fällen. Material-Kompositionen wie TopAppBar und BottomNavigation implementieren dieses Verhalten standardmäßig.

Abbildung 6: Dunkles Materialdesign mit wenigen Farbakzenten. Obere App-Leiste Die Hauptfarbe wird im hellen Design und die Oberflächenfarbe im dunklen Design verwendet.

Verwenden Sie für benutzerdefinierte Szenarien das Erweiterungsattribut primarySurface:

Surface(
    // Switches between primary in light theme and surface in dark theme
    color = MaterialTheme.colors.primarySurface,
    /*...*/
) { /*...*/ }

Typografie

Material definiert einen Typ System, mit einigen semantisch benannten Stilen.

Beispiel für verschiedene Schriftarten in verschiedenen Stilen

Abbildung 7. Das Materialtypsystem.

Compose implementiert das Typsystem mit den Klassen Typography, TextStyle und schriftarten. Der Typography-Konstruktor bietet Standardwerte für jeden Stil. Sie können also alle Elemente weglassen, die Sie nicht anpassen möchten:

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, /*...*/) {
    /*...*/
}

Wenn Sie dieselbe Schriftart verwenden möchten, geben Sie defaultFontFamily parameter an und lassen Sie die fontFamily-Elemente aller TextStyle-Elemente aus:

val typography = Typography(defaultFontFamily = raleway)
MaterialTheme(typography = typography, /*...*/) {
    /*...*/
}

Textstile verwenden

Auf TextStyles kann über MaterialTheme.typography zugegriffen werden. Rufen Sie die Für TextStyle gilt:

Text(
    text = "Subtitle2 styled",
    style = MaterialTheme.typography.subtitle2
)

Screenshot mit einer Mischung aus verschiedenen Schriftbildern für unterschiedliche Zwecke

Abbildung 8. Verwenden Sie eine Auswahl von Schriftarten und Stilen, um Ihre Marke zu präsentieren.

Form

Material definiert einen Formsystem können Sie um Formen für große, mittlere und kleine Komponenten zu definieren.

Zeigt eine Vielzahl von Material Design-Formen

Abbildung 9: Das Materialformsystem.

In Compose wird das Formsystem mit der Klasse Shapes implementiert. Damit können Sie für jede Größenkategorie eine CornerBasedShape angeben:

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, /*...*/) {
    /*...*/
}

Viele Komponenten verwenden diese Formen standardmäßig. Beispiel: Button, TextField und FloatingActionButton auf „klein“ eingestellt, AlertDialog ist standardmäßig „Medium“ und ModalDrawer. ist standardmäßig groß – siehe Formschema-Referenz für die vollständige Zuordnung.

Formen verwenden

Der Zugriff auf Shape erfolgt über MaterialTheme.shapes. Rufen Sie die Shapes mit folgendem Code ab:

Surface(
    shape = MaterialTheme.shapes.medium, /*...*/
) {
    /*...*/
}

Screenshot einer App, in der Materialformen den Status eines Elements anzeigen

Abbildung 10. Verwenden Sie Formen, um Marke oder Zustand zum Ausdruck zu bringen.

Standardstile

Es gibt kein äquivalentes Konzept in der Funktion zur Erstellung von Standardformate aus Android Views. Sie können ähnliche Funktionen bereitstellen, indem Sie eigene zusammensetzbare Funktionen erstellen, die Materialkomponenten umschließen. Um beispielsweise einen Schaltflächenstil zu erstellen, fassen Sie eine Schaltfläche in Ihrer eigenen zusammensetzbaren Funktion zusammen, legen Sie die zu ändernden Parameter direkt fest und stellen Sie andere als Parameter an die enthaltende zusammensetzbare Funktion zur Verfügung.

@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
    )
}

Themen-Overlays

Sie können das Äquivalent zu Design-Overlays aus Android-Ansichten in Compose erzielen, indem Sie MaterialTheme-Kompositionen verschachteln. Weil In MaterialTheme werden Farben, Typografie und Formen standardmäßig auf den aktuellen Designwert gesetzt. Wenn ein Design nur einen dieser Parameter festlegt, behalten die anderen Parameter ihre Standardwerte.

Wenn Sie auf Ansichten basierende Bildschirme zu Compose migrieren, achten Sie außerdem auf die Verwendung des Attributs android:theme. Wahrscheinlich benötigen Sie in diesem Teil des Compose-UI-Baums eine neue MaterialTheme.

In diesem Beispiel wird auf dem Detailbildschirm für den größten Teil des Bildschirms ein PinkTheme und dann für den zugehörigen Bereich ein BlueTheme verwendet. Siehe Screenshot und Code unten.

Abbildung 11. Verschachtelte Themen

@Composable
fun DetailsScreen(/* ... */) {
    PinkTheme {
        // other content
        RelatedSection()
    }
}

@Composable
fun RelatedSection(/* ... */) {
    BlueTheme {
        // content
    }
}

Komponentenstatus

Materialkomponenten, mit denen interagiert werden kann (z. B. angeklickt oder ein-/ausgeschaltet werden), können sich in verschiedenen visuellen Status befinden. Zu den Status gehören „Aktiviert“, „Deaktiviert“, „Gedrückt“ usw.

Composables haben oft einen enabled-Parameter. Wird sie auf false gesetzt, Interaktion und ändert Eigenschaften wie Farbe und Elevation, um Komponentenstatus.

Abbildung 12. Schaltfläche mit enabled = true (links) und enabled = false (rechts)

In den meisten Fällen können Sie für Werte wie Farbe und Höhe die Standardeinstellungen verwenden. Wenn Sie Werte konfigurieren möchten, die in verschiedenen Status verwendet werden, gibt es Klassen und Komfortfunktionen verfügbar. Hier ein Beispiel für die Schaltfläche:

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
    )
) { /* ... */ }

Abbildung 13 Schaltfläche mit enabled = true (links) und enabled = false (rechts) mit angepassten Farb- und Höhenwerten.

Kreise im Wasser

Bei Materialkomponenten wird mit Wellen angezeigt, dass mit ihnen interagiert wird. Wenn Sie MaterialTheme in Ihrer Hierarchie verwenden, wird ein Ripple als StandardIndication in Modifikatoren wie clickable und indication

In den meisten Fällen können Sie den Standardwert Ripple verwenden. Falls Sie das Ihres Erscheinungsbilds, können Sie RippleTheme Eigenschaften wie Farbe und Alpha ändern.

Sie können RippleTheme verlängern und die defaultRippleColor und defaultRippleAlpha Dienstprogrammfunktionen. Sie können dann Ihr benutzerdefiniertes Wellenthema in Ihrer Hierarchie mithilfe von 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
    )
}

Alt-Text

Abbildung 14: Schaltflächen mit unterschiedlichen Ripple-Werten, die über RippleTheme bereitgestellt werden

Weitere Informationen

Weitere Informationen zu Material Theming in Compose findest du hier: zusätzliche Ressourcen.

Codelabs

Videos