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

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

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

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

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

חלוקה לקבוצות מאפיינים ההרשאות שמועברות לילדים
פריסה וגודל
מרווח פנימי contentPadding (פנימי) ו-externalPadding (חיצוני). זמין בגרסאות חד-כיווני, אופקי, אנכי ודו-כיווני. לא
מידות fillWidth/Height/Size() וגם width, height, וגם size (תומך בשברים Dp, DpSize או Float). לא
מיצוב left/top/right/bottom offsets. לא
מראה ויזואלי
מילוי background וגם foreground (תומך ב-Color או ב-Brush). לא
גבולות borderWidth,‏ borderColor וגם borderBrush. לא
צורה shape לא – אבל הוא משמש בשילוב עם נכסים אחרים. ‫clip ו-border משתמשים בצורה המוגדרת הזו.
אזורים כהים dropShadow, innerShadow לא
טרנספורמציות
תנועה מרחבית של שכבת גרפיקה translationX, translationY, scaleX/Y, rotationX/Y/Z לא
שליטה 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 או padding בתוך פונקציית ה-lambda.

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

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

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

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

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

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

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

בדוגמה הבאה אפשר לראות איך להחיל סגנון ישירות באמצעות פרמטרים מובנים של רכיב, או באמצעות 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 ומאפיינים שקשורים לסגנון טקסט, מועברים לקומפוזיציות המשנה. סגנון שמוגדר לרכיב צאצא ניתן לביטול של סגנון האב שהועבר בירושה לרכיב הצאצא הספציפי הזה.

הפצת סגנון באמצעות הפרמטרים 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) בקומפוזיבל האב, והם מועברים לכל קומפוזיבל הצאצא 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 מבטלים את המאפיינים של רכיבי Parent 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))
}