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

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

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

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

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

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

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

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

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

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

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

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

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