אמצעי שינוי מאפשרים לכם להוסיף עיצוב או לשפר קומפוזיציה. המשנים מאפשרים לכם לבצע פעולות מהסוגים הבאים:
- שינוי הגודל, הפריסה, ההתנהגות והמראה של רכיב ה-Composable
- הוספת מידע, כמו תוויות נגישות
- עיבוד קלט של משתמשים
- הוספת אינטראקציות ברמה גבוהה, כמו הפיכת אלמנט לקליקבילי, לניתן לגלילה, לניתן לגרירה או לניתן להגדלה
המשנים הם אובייקטים סטנדרטיים של Kotlin. כדי ליצור משנה, קוראים לאחת מהפונקציות של המחלקה Modifier
:
@Composable private fun Greeting(name: String) { Column(modifier = Modifier.padding(24.dp)) { Text(text = "Hello,") Text(text = name) } }
אפשר לשרשר את הפונקציות האלה כדי ליצור פונקציה מורכבת:
@Composable private fun Greeting(name: String) { Column( modifier = Modifier .padding(24.dp) .fillMaxWidth() ) { Text(text = "Hello,") Text(text = name) } }
בדוגמה של הקוד שלמעלה, אפשר לראות פונקציות שונות של שינוי שמשמשות יחד.
-
padding
יוצר רווח מסביב לרכיב. - הפונקציה
fillMaxWidth
גורמת לרכיב הניתן להרכבה למלא את הרוחב המקסימלי שמוקצה לו מהרכיב ההורה.
מומלץ שכל הפונקציות הניתנות להרכבה יקבלו פרמטר modifier
, ויעבירו את ה-modifier הזה לצאצא הראשון שלהן שיוצר ממשק משתמש.
כך תוכלו להשתמש בקוד שוב ושוב, וההתנהגות שלו תהיה צפויה ואינטואיטיבית יותר. למידע נוסף, אפשר לעיין בהנחיות לשימוש ב-Compose API, רכיבים מקבלים ומכבדים פרמטר Modifier.
הסדר של משני הצעות המחיר חשוב
הסדר של פונקציות לשינוי הוא משמעותי. מכיוון שכל פונקציה משנה את הערך Modifier
שמוחזר על ידי הפונקציה הקודמת, הרצף משפיע על התוצאה הסופית. הנה דוגמה:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { // rest of the implementation } }
בקוד שלמעלה, כל האזור ניתן ללחיצה, כולל הריווח מסביב, כי המאפיין padding
הוחל אחרי המאפיין clickable
. אם סדר המקשים לשינוי הפוך, הרווח שנוסף על ידי padding
לא מגיב לקלט של המשתמש:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .padding(padding) .clickable(onClick = onClick) .fillMaxWidth() ) { // rest of the implementation } }
גורמי שינוי מובנים
Jetpack Compose מספק רשימה של משני מאפיינים מובנים שיעזרו לכם לעצב או לשפר רכיב Composable. אלה כמה משנים נפוצים שתשתמשו בהם כדי לשנות את הפריסות.
padding
וגם size
כברירת מחדל, הפריסות שמופיעות בכלי הכתיבה עוטפות את הרכיבים שלהן. אבל אפשר להגדיר גודל באמצעות התג size
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image(/*...*/) Column { /*...*/ } } }
שימו לב: יכול להיות שהגודל שציינתם לא יכובד אם הוא לא עומד במגבלות של רכיב האב של פריסת הרכיבים. אם אתם רוצים שהגודל של הקומפוזיציה יהיה קבוע בלי קשר למגבלות הנכנסות, אתם יכולים להשתמש במגדיר requiredSize
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.requiredSize(150.dp) ) Column { /*...*/ } } }
בדוגמה הזו, גם אם הגדרת ההורה height
היא 100.dp
, הגובה של Image
יהיה 150.dp
, כי שינוי המאפיין requiredSize
קודם להגדרה של ההורה.
אם רוצים שפריסת הצאצא תמלא את כל הגובה הזמין שמוגדר על ידי ההורה, מוסיפים את המשנה fillMaxHeight
(ב-Compose יש גם את fillMaxSize
ואת fillMaxWidth
):
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.fillMaxHeight() ) Column { /*...*/ } } }
כדי להוסיף ריווח פנימי מסביב לאלמנט, מגדירים משנה padding
.
אם רוצים להוסיף ריווח מעל קו הבסיס של הטקסט כדי להשיג מרחק מסוים בין החלק העליון של הפריסה לבין קו הבסיס, משתמשים במגדיר paddingFromBaseline
:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text( text = artist.name, modifier = Modifier.paddingFromBaseline(top = 50.dp) ) Text(artist.lastSeenOnline) } } }
היסט
כדי למקם פריסה ביחס למיקום המקורי שלה, מוסיפים את המשנה offset
ומגדירים את ההיסט בציר x ובציר y.
ההיסטים יכולים להיות חיוביים או לא חיוביים. ההבדל בין padding
לבין offset
הוא שכאשר מוסיפים offset
לרכיב שאפשר להרכיב, המידות שלו לא משתנות:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text(artist.name) Text( text = artist.lastSeenOnline, modifier = Modifier.offset(x = 4.dp) ) } } }
המשנה offset
מוחל אופקית בהתאם לכיוון הפריסה.
בהקשר של משמאל לימין, ערך חיובי של offset
מעביר את הרכיב ימינה, ובהקשר של מימין לשמאל, הוא מעביר את הרכיב שמאלה.
אם אתם רוצים להגדיר היסט בלי להתחשב בכיוון הפריסה, אפשר להשתמש במאפיין absoluteOffset
, שבו ערך היסט חיובי תמיד מזיז את הרכיב ימינה.
המשנה offset
מספק שני עומסים יתרים – offset
שמקבל את ההיסטים כפרמטרים ו-offset
שמקבל למבדא.
לקבלת מידע מפורט יותר על המקרים שבהם כדאי להשתמש בכל אחת מהאפשרויות האלה ועל אופטימיזציה של הביצועים, כדאי לקרוא את הקטע שיפור הביצועים של כתיבה – דחיית קריאות ככל האפשר.
היקף הבטיחות בכתיבה
ב-Compose יש משנים שאפשר להשתמש בהם רק כשמחילים אותם על רכיבי Compose מסוימים. ב-Compose, האכיפה מתבצעת באמצעות היקפים מותאמים אישית.
לדוגמה, אם רוצים להגדיל את הילד Box
לגודל של ההורה Box
בלי להשפיע על הגודל של Box
, משתמשים במגדיר matchParentSize
. matchParentSize
זמין רק בBoxScope
.
לכן, אפשר להשתמש בו רק בחשבון של ילד או ילדה בBox
.
הבטיחות של היקף המשתנים מונעת הוספה של משנים שלא יפעלו ברכיבי Composable ובהיקפים אחרים, וחוסכת זמן של ניסוי וטעייה.
המשנים המוגבלים להיקף מסוים מודיעים להורה על מידע מסוים שחשוב שההורה ידע על הילד או הילדה. הן נקראות גם משני נתוני אב. הפרטים הפנימיים שלהם שונים מהמשנים לשימוש כללי, אבל מבחינת השימוש, ההבדלים האלה לא משמעותיים.
matchParentSize
בעוד Box
כמו שצוין למעלה, אם רוצים שגודל הפריסה של רכיב צאצא יהיה זהה לגודל הפריסה של רכיב אב Box
בלי להשפיע על הגודל Box
, משתמשים במגדיר matchParentSize
.
הערה: המאפיין matchParentSize
זמין רק בהיקף Box
, כלומר הוא חל רק על צאצאים ישירים של קומפוזיציות Box
.
בדוגמה שלמטה, גודל הרכיב Spacer
נקבע לפי גודל הרכיב האב Box
, שנקבע לפי גודל הרכיבים הצאצאים הגדולים ביותר, ArtistCard
במקרה הזה.
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
אם במקום matchParentSize
השתמשו ב-fillMaxSize
, הרכיב Spacer
יתפוס את כל השטח הפנוי שהוקצה לרכיב האב, וכתוצאה מכך רכיב האב יתרחב וימלא את כל השטח הפנוי.
weight
ב-Row
וגם ב-Column
כפי שראיתם בקטע הקודם בנושא Padding וגודל, כברירת מחדל, הגודל של רכיב שאפשר להרכיב מוגדר לפי התוכן שהוא מכיל. אפשר להגדיר את הגודל של תוכן קומפוזבילי כך שיהיה גמיש ביחס לישות האב שלו באמצעות weight
Modifier שזמין רק ב-RowScope
וב-ColumnScope
.
נניח שיש לנו Row
שמכיל שני רכיבי Box
composable.
התיבה הראשונה מקבלת רוחב כפול מהתיבה השנייה, כי היא מקבלת weight
כפול מהתיבה השנייה. הרוחב של Row
הוא 210.dp
, הרוחב של Box
הראשון הוא 140.dp
, והרוחב של השני הוא 70.dp
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.fillMaxWidth() ) { Image( /*...*/ modifier = Modifier.weight(2f) ) Column( modifier = Modifier.weight(1f) ) { /*...*/ } } }
חילוץ של משנים ושימוש חוזר בהם
אפשר לשרשר כמה משנים כדי לשנות או להוסיף מאפיינים לרכיב שאפשר להרכבה. השרשרת הזו נוצרת דרך הממשק Modifier
, שמייצג רשימה מסודרת וקבועה של Modifier.Elements
יחידים.
כל Modifier.Element
מייצג התנהגות ספציפית, כמו התנהגויות שקשורות לפריסה, לציור ולגרפיקה, התנהגויות שקשורות למחוות, התנהגויות שקשורות למיקוד ולסמנטיקה, וגם אירועי קלט של המכשיר. סדר הרכיבים חשוב: רכיבי שינוי שנוספו ראשונים יחולו ראשונים.
לפעמים כדאי לעשות שימוש חוזר באותם מופעים של שרשרת משנים בכמה פונקציות Composable, על ידי חילוץ שלהם למשתנים והעברה שלהם להיקפים גבוהים יותר. יש כמה סיבות לכך שהיא יכולה לשפר את קריאות הקוד או את הביצועים של האפליקציה:
- הקצאה מחדש של משני המאפיינים לא תחזור על עצמה כשמתבצעת קומפוזיציה מחדש של פונקציות שאפשר להוסיף להן משני מאפיינים
- שרשראות של משנים יכולות להיות ארוכות ומורכבות מאוד, ולכן שימוש חוזר באותו מופע של שרשרת יכול להפחית את עומס העבודה שזמן הריצה של Compose צריך לבצע כשמשווים אותן
- החילוץ הזה מקדם ניקיון קוד, עקביות ותחזוקה בכל בסיס הקוד
שיטות מומלצות לשימוש חוזר במאפייני שינוי
אתם יכולים ליצור שרשראות של Modifier
משלכם ולחלץ אותן כדי לעשות בהן שימוש חוזר בכמה רכיבים מורכבים. אפשר לשמור רק משנה, כי הוא אובייקט שמכיל נתונים:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
חילוץ של משנים ושימוש חוזר בהם כשמבצעים מעקב אחרי מצב שמשתנה לעיתים קרובות
כשעוקבים אחרי שינויים תכופים במצבים בתוך פונקציות Composable, כמו מצבי אנימציה או scrollState
, יכול להיות שיתבצעו הרבה מאוד פעולות של recomposition. במקרה כזה, המאפיינים ישויכו לכל קומפוזיציה מחדש, ואולי לכל פריים:
@Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // Creation and allocation of this modifier will happen on every frame of the animation! modifier = Modifier .padding(12.dp) .background(Color.Gray), animatedState = animatedState ) }
במקום זאת, אפשר ליצור, לחלץ ולעשות שימוש חוזר באותו מופע של המשנה ולהעביר אותו לרכיב הניתן להרכבה כך:
// Now, the allocation of the modifier happens here: val reusableModifier = Modifier .padding(12.dp) .background(Color.Gray) @Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // No allocation, as we're just reusing the same instance modifier = reusableModifier, animatedState = animatedState ) }
חילוץ של משנים ללא היקף ושימוש חוזר בהם
אפשר להגדיר את המגדירים כך שיחולו על כל הקומפוזיציות או רק על קומפוזיציה ספציפית. במקרה של משנים ללא היקף, אפשר לחלץ אותם בקלות מחוץ לרכיבים הניתנים להרכבה כמשתנים פשוטים:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) @Composable fun AuthorField() { HeaderText( // ... modifier = reusableModifier ) SubtitleText( // ... modifier = reusableModifier ) }
האפשרות הזו יכולה להיות שימושית במיוחד בשילוב עם פריסות עצלות. ברוב המקרים, כדאי להגדיר את אותם משני מחיר לכל הפריטים, כי יכול להיות שיש לכם הרבה פריטים.
val reusableItemModifier = Modifier .padding(bottom = 12.dp) .size(216.dp) .clip(CircleShape) @Composable private fun AuthorList(authors: List<Author>) { LazyColumn { items(authors) { AsyncImage( // ... modifier = reusableItemModifier, ) } } }
חילוץ של מגבילים בהיקף מסוים ושימוש חוזר בהם
כשעובדים עם משנים שמוגדרים בהיקף של רכיבים מסוימים, אפשר לחלץ אותם לרמה הגבוהה ביותר האפשרית ולעשות בהם שימוש חוזר במקומות המתאימים:
Column(/*...*/) { val reusableItemModifier = Modifier .padding(bottom = 12.dp) // Align Modifier.Element requires a ColumnScope .align(Alignment.CenterHorizontally) .weight(1f) Text1( modifier = reusableItemModifier, // ... ) Text2( modifier = reusableItemModifier // ... ) // ... }
צריך להעביר רק את שינויי ההגדרות שחולצו והוגדרו בהיקף מסוים לצאצאים ישירים באותו היקף. בקטע היקף הבטיחות ב-Compose מוסבר למה זה חשוב:
Column(modifier = Modifier.fillMaxWidth()) { // Weight modifier is scoped to the Column composable val reusableItemModifier = Modifier.weight(1f) // Weight will be properly assigned here since this Text is a direct child of Column Text1( modifier = reusableItemModifier // ... ) Box { Text2( // Weight won't do anything here since the Text composable is not a direct child of Column modifier = reusableItemModifier // ... ) } }
שרשור נוסף של משנים שחולצו
אפשר להוסיף עוד שרשראות של משנים או לצרף אותן על ידי קריאה לפונקציה .then()
:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) // Append to your reusableModifier reusableModifier.clickable { /*...*/ } // Append your reusableModifier otherModifier.then(reusableModifier)
רק חשוב לזכור שהסדר של שינויי ההתאמה משנה!
מידע נוסף
רשימה מלאה של שינויים עם הפרמטרים וההיקפים שלהם
כדי להתאמן עוד על השימוש במגדירי מאפיינים, אפשר גם לעיין בסדנת הקוד בנושא פריסות בסיסיות ב-Compose או במאגר Now in Android.
מידע נוסף על משנים מותאמים אישית ועל אופן היצירה שלהם זמין במאמר בנושא פריסות בהתאמה אישית – שימוש במשנה הפריסה.
מומלץ עבורך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- העקרונות הבסיסיים של פריסת חלון הכתיבה
- פעולות ב-Editor {:#editor-actions}
- פריסות בהתאמה אישית {:#custom-layouts }