היסודות של סגנונות

יש שלוש דרכים להשתמש בסגנונות באפליקציה:

  1. אפשר להשתמש ישירות ברכיבים קיימים שחושפים פרמטר Style.
  2. החלת סגנון באמצעות Modifier.styleable על פריסות של קומפוזיציות שלא מקבלות פרמטר Style.
  3. במערכת העיצוב המותאמת אישית שלכם, משתמשים ב-Modifier.styleable{} וחושפים פרמטר סגנון ברכיבים שלכם.

מאפיינים זמינים ב-Styles

סגנונות תומכים בהרבה מאותם מאפיינים שמשנים תומכים בהם, אבל לא כל מה שהוא משנה יכול להיות משוכפל באמצעות סגנון. עדיין צריך להשתמש במקשי צירוף כדי לשנות התנהגויות מסוימות, כמו אינטראקציות, ציור מותאם אישית או סידור של מאפיינים.

חלוקה לקבוצות מאפיינים ההרשאה עוברת בירושה לילדים
פריסה וגודל
שוליים פנימיים של תוכן ‫- contentPadding(all: Dp)
- contentPadding(horizontal: Dp, vertical: Dp)
- contentPadding(start: Dp, top: Dp, end: Dp, bottom: Dp)
- contentPaddingHorizontal(value: Dp) / contentPaddingVertical(value: Dp)
- contentPaddingStart(value: Dp) / contentPaddingTop(value: Dp) / contentPaddingEnd(value: Dp) / contentPaddingBottom(value: Dp)
לא
מרווח חיצוני (חיצוני) ‫- externalPadding(all: Dp)
- externalPadding(horizontal: Dp, vertical: Dp)
- externalPadding(start: Dp, top: Dp, end: Dp, bottom: Dp)
- externalPaddingHorizontal(value: Dp) / externalPaddingVertical(value: Dp)
- externalPaddingStart(value: Dp) / externalPaddingTop(value: Dp) / externalPaddingEnd(value: Dp) / externalPaddingBottom(value: Dp)
לא
מידות fillWidth()/fillHeight()/fillSize() וגם width, height וגם size (תומך בשברים Dp, DpSize או Float). לא
מיצוב left/top/right/bottom היסטים. לא
Visual Appearance
מילוי background וגם foreground (תומך ב-Color או ב-Brush). לא
גבולות borderWidth,‏ borderColor וגם borderBrush. לא
צורה shape לא – אבל הוא משמש בשילוב עם נכסים אחרים. ‫clip ו-border משתמשים בצורה המוגדרת הזו.
אזורים כהים dropShadow, innerShadow לא
טרנספורמציות
תנועה מרחבית של שכבת גרפיקה translationX, translationY, scaleX/scaleY, rotationX/rotationY/rotationZ לא
שליטה alpha,‏ zIndex (סדר הערימה) וtransformOrigin (נקודת הציר) לא
טיפוגרפיה
עיצוב textStyle, fontSize, fontWeight, fontStyle וגם fontFamily כן
צבעים contentColor וגם contentBrush. המאפיין הזה משמש גם לעיצוב של סמלים. כן
פסקה lineHeight, letterSpacing, textAlign, textDirection, lineBreak וגם hyphens. כן
קישוטים textDecoration,‏ textIndent וגם baselineShift. כן

שימוש בסגנונות ישירות ברכיבים עם פרמטרים של סגנון

רכיבים שחושפים פרמטר Style מאפשרים לכם להגדיר את הסגנון שלהם:

BaseButton(
    onClick = { },
    style = { }
) {
    BaseText("Click me")
}

בתוך פונקציית ה-lambda של הסגנון, אפשר להגדיר מאפיינים שונים, כמו externalPadding או background:

BaseButton(
    onClick = { },
    style = { background(Color.Blue) }
) {
    BaseText("Click me")
}

רשימה מלאה של המאפיינים הנתמכים מופיעה במאמר מאפיינים זמינים ב-Styles.

החלת סגנונות באמצעות משנים לרכיבים ללא פרמטר קיים

לרכיבים שאין להם פרמטר סגנון מובנה, עדיין אפשר להחיל סגנונות באמצעות התוסף styleable. הגישה הזו שימושית גם כשמפתחים רכיבים מותאמים אישית.

Row(
    modifier = Modifier.styleable { }
) {
    BaseText("Content")
}

בדומה לפרמטר style, אפשר לכלול מאפיינים כמו background,‏ contentPadding או externalPadding בתוך פונקציית ה-lambda.

Row(
    modifier = Modifier.styleable {
        background(Color.Blue)
    }
) {
    BaseText("Content")
}

שימוש בכמה משנים של Modifier.styleable בשרשרת מוסיף מאפיינים שלא עוברים בירושה ל-composable שהוחל עליו, באופן דומה לשימוש בכמה משנים שמגדירים את אותם מאפיינים. במאפיינים שעוברים בירושה, המערכת דורסת את הערכים האלה, והמשנה האחרון styleable בשרשרת מגדיר את הערכים.

כשמשתמשים ב-Modifier.styleable, כדאי גם ליצור ולספק StyleState לשימוש עם התוסף כדי להחיל סגנון מבוסס-מצב. פרטים נוספים זמינים במאמר בנושא מצבים ואנימציות עם סגנונות.

הגדרת סגנון עצמאי

אפשר להגדיר סגנון עצמאי לשימוש חוזר:

val style = Style { background(Color.Blue) }

אחר כך אפשר להעביר את הסגנון המוגדר הזה לפרמטר הסגנון של רכיב שאפשר להרכיב או באמצעות Modifier.styleable. כשמשתמשים ב-Modifier.styleable, צריך גם ליצור אובייקט StyleState. StyleState מוסבר בפירוט במסמכי התיעוד בנושא מצבים ואנימציות עם סגנונות.

בדוגמה הבאה אפשר לראות איך להחיל סגנון ישירות באמצעות פרמטרים מובנים של רכיב, או באמצעות Modifier.styleable:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}

// modifier styleable
val styleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(styleState, style)
) {
    BaseText("Column content")
}

אפשר גם להעביר את הסגנון הזה לכמה רכיבים:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}
BaseText("Different text that uses the same style parameter", style = style)

// modifier styleable
val columnStyleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(columnStyleState, style)
) {
    BaseText("Column")
}
val rowStyleState = remember { MutableStyleState(null) }
Row(
    Modifier.styleable(rowStyleState, style)
) {
    BaseText("Row")
}

הוספת כמה מאפייני סגנון

אפשר להוסיף כמה מאפייני סגנון על ידי הגדרת מאפיינים שונים בכל שורה:

BaseButton(
    onClick = { },
    style = {
        background(Color.Blue)
        contentPaddingStart(16.dp)
    }
) {
    BaseText("Button")
}

מאפיינים בסגנונות לא מצטברים, בניגוד לעיצוב שמבוסס על משנים. הסגנונות מקבלים את הערך האחרון שמוגדר ברשימת המאפיינים בתוך בלוק סגנון אחד. בדוגמה הבאה, הרקע מוגדר פעמיים, והרקע שמוחל הוא TealColor. במקרה של ריווח פנימי, contentPaddingTop מבטל את הריווח הפנימי העליון שהוגדר על ידי contentPadding ולא משלב את הערכים.

BaseButton(
    style = {
        background(Color.Red)
        // Background of Red is now overridden with TealColor instead
        background(TealColor)
        // All directions of padding are set to 64.dp (top, start, end, bottom)
        contentPadding(64.dp)
        // Top padding is now set to 16.dp, all other paddings remain at 64.dp
        contentPaddingTop(16.dp)
    },
    onClick = {
        //
    }
) {
    BaseText("Click me!")
}

כפתור עם שתי הגדרות של צבע רקע ושתי הגדרות של contentPadding
איור 1. כפתור עם שתי הגדרות של צבע רקע ושתי contentPaddingהגדרות שמוגדרות מחדש.

מיזוג של אובייקטים מרובים של סגנונות

אפשר ליצור כמה אובייקטים של סגנון ולהעביר אותם לפרמטר הסגנון של הרכיב שאפשר להרכיב.

val style1 = Style { background(TealColor) }
val style2 = Style { contentPaddingTop(16.dp) }

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

כפתור עם צבע רקע וערך של contentPaddingTop
איור 2. לחצן עם צבע רקע ועם הגדרה של contentPaddingTop.

אם כמה סגנונות מציינים את אותו מאפיין, המאפיין האחרון שמוגדר נבחר. מאחר שמאפיינים לא מצטברים בסגנונות, הערך האחרון של padding שמועבר מחליף את הערך contentPaddingHorizontal שהוגדר על ידי contentPadding הראשוני. בנוסף, צבע הרקע האחרון מבטל את צבע הרקע שהוגדר על ידי הסגנון הראשוני שהועבר.

val style1 = Style {
    background(Color.Red)
    contentPadding(32.dp)
}

val style2 = Style {
    contentPaddingHorizontal(8.dp)
    background(Color.LightGray)
}

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

במקרה הזה, הסגנון שמוחל כולל רקע אפור בהיר וריווח פנימי 32.dp, למעט הריווח הפנימי בצד ימין ובצד שמאל, שהערך שלו הוא 8.dp.

לחצן עם contentPadding שמוחלף על ידי Styles שונים
איור 3. כפתור עם contentPadding שמוחלף על ידי סגנונות שונים.

העברת סגנון

מאפייני סגנון מסוימים, כמו contentColor ומאפיינים שקשורים לסגנון טקסט, מועברים לרכיבי ה-Composable המשניים. סגנון שמוגדר לרכיב צאצא ניתן לביטול של סגנון ההורה שעבר בירושה לרכיב הצאצא הספציפי הזה.

הפצת סגנון באמצעות הפרמטרים Style, styleable ו-direct
איור 4. הפצת סגנון עם Style,‏ styleable ופרמטרים ישירים.
עדיפות שיטה אפקט
‫1 (הגבוה ביותר) ארגומנטים ישירים ברכיב קומפוזבילי המאפיין מחליף את כל ההגדרות. לדוגמה, Text(color = Color.Red)
2 פרמטר סגנון שינויים מקומיים מברירת המחדל של הסגנון Text(style = Style { contentColor(Color.Red)}
3 שרשרת של גורמי שינוי Modifier.styleable{ contentColor(Color.Red) ברכיב עצמו.
‫4 (הנמוך ביותר) סגנונות ברמה העליונה למאפיינים שאפשר להעביר בירושה (טיפוגרפיה/צבע) שמועברים מההורה.

סגנון ברמה העליונה

אפשר להגדיר מאפייני טקסט (כמו contentColor) מתוך פונקציית ה-Composable הראשית, והם מועברים לכל פונקציות ה-Composable של הצאצאים.Text

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children inherit", style = { width(60.dp) })
    BaseText("certain properties")
    BaseText("from their parents")
}

העברת מאפיינים של רכיבים הניתנים להרכבה צאצאים
איור 5. ירושת מאפיינים של רכיבים הניתנים להרכבה מסוג צאצא.

ביטול של מאפיינים בנכס צאצא

אפשר גם להגדיר סגנון לText קומפוזבל ספציפי. אם להורה יש סגנון מוגדר, הסגנון שמוגדר לצאצא מבטל את הסגנון של ההורה.

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children can ", style = {
        contentBrush(Brush.linearGradient(listOf(Color.Red, Color.Blue)))
    })
    BaseText("override properties")
    BaseText("set by their parents")
}

רכיבים הניתנים להרכבה מסוג צאצא מבטלים את המאפיינים של ההורה
איור 6. רכיבי Child composable מבטלים את המאפיינים של רכיבי האב.

הטמעה של מאפייני סגנון מותאמים אישית

אפשר ליצור מאפיינים בהתאמה אישית שממופים להגדרות סגנון קיימות באמצעות פונקציות הרחבה ב-StyleScope, כמו בדוגמה הבאה:

fun StyleScope.outlinedBackground(color: Color) {
    border(1.dp, color)
    background(color)
}

החלת הנכס החדש בהגדרת סגנון:

val customExtensionStyle = Style {
    outlinedBackground(Color.Blue)
}

אין תמיכה ביצירת נכסים חדשים שאפשר להחיל עליהם סגנון. אם תרצו לקבל תמיכה כזו לתרחיש השימוש שלכם, תוכלו לשלוח בקשה להוספת תכונה.

קריאת ערכים של CompositionLocal

דפוס נפוץ הוא לאחסן את הטוקנים של מערכת העיצוב בתוך CompositionLocal, כדי לגשת למשתנים בלי להעביר אותם כפרמטרים. סגנונות יכולים לגשת ל-CompositionLocal כדי לאחזר ערכים ברמת המערכת בתוך סגנון:

val buttonStyle = Style {
    contentPadding(12.dp)
    shape(RoundedCornerShape(50))
    background(Brush.verticalGradient(LocalCustomColors.currentValue.background))
}