דף זה מתאר כיצד לטפל בגדלים ולספק גמישות ורספונסיבית עם 'בקצרה', באמצעות רכיבים קיימים במסגרת 'בקצרה'.
שימוש ב-Box
, ב-Column
וב-Row
במסגרת 'בקצרה' יש שלוש פריסות קומפוזביליות עיקריות:
Box
: הצבת רכיבים מעל פריט אחר. מתורגמת לRelativeLayout
.Column
: הצבת רכיבים זה אחרי זה בציר האנכי. תרגום לLinearLayout
בכיוון אנכי.Row
: הצבת רכיבים זה אחרי זה בציר האופקי. תרגום אלLinearLayout
בכיוון אופקי.
התכונה 'בקצרה' תומכת באובייקטים Scaffold
. מיקום של Column
, Row
Box
תכנים קומפוזביליים בתוך אובייקט Scaffold
נתון.
כל אחד מהתכנים הקומפוזביליים האלה מאפשר להגדיר יישור אנכי ואופקי של התוכן ומגבלות הרוחב, הגובה, המשקל או המרווח הפנימי באמצעות עם מגבילי התאמה אישית. בנוסף, כל ילד יכול להגדיר את תנאי הצירוף שלו כדי לשנות את המרחב המשותף ואת המיקום בתוך ההורה.
הדוגמה הבאה ממחישה איך ליצור Row
בחלוקה שווה
את הצאצאים שלו לרוחב, כפי שמוצג באיור 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
הערך Row
ממלא את הרוחב הזמין המקסימלי, ובגלל שלכל ילד יש אותו
הן יתחלקו באופן שווה את השטח הזמין. אפשר להגדיר משקולות שונות,
גדלים, מרווחים או התאמות כדי להתאים את הפריסה לצרכים שלכם.
שימוש בפריסות שניתן לגלול
דרך נוספת לספק תוכן רספונסיבי היא לאפשר גלילה בדף. הדבר
אפשרית עם התוכן הקומפוזבילי LazyColumn
. התוכן הקומפוזבילי הזה מאפשר להגדיר קבוצה
של פריטים שיוצגו בתוך מאגר שאפשר לגלול בווידג'ט של האפליקציה.
קטעי הקוד הבאים מציגים דרכים שונות להגדרת פריטים בתוך
LazyColumn
אפשר לציין את מספר הפריטים:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
מספקים פריטים בודדים:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
מספקים רשימה או מערך של פריטים:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
אפשר גם להשתמש בשילוב של הדוגמאות שלמעלה:
LazyColumn { item { Text("Names:") } items(peopleNameList) { name -> Text(name) } // or in case you need the index: itemsIndexed(peopleNameList) { index, person -> Text("$person at index $index") } }
חשוב לשים לב שקטע הקוד הקודם לא מציין את הערך itemId
. ציון של
בעזרת itemId
אפשר לשפר את הביצועים ולשמור על הגלילה
מיקום ברשימה ועדכונים של appWidget
מ-Android 12 ואילך (ל
לדוגמה, בעת הוספה או הסרה של פריטים מהרשימה). הדוגמה הבאה
מראה איך לציין itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
הגדרת SizeMode
הגדלים של AppWidget
עשויים להשתנות בהתאם למכשיר, לבחירת המשתמש או למרכז האפליקציות,
לכן חשוב לספק פריסות גמישות כפי שמתואר במאמר
הדף 'פריסות ווידג'ט גמישות'. התכונה 'בקצרה' מפשטת את התהליך בעזרת SizeMode
ואת הערך LocalSize
. הקטעים הבאים מתארים את שלושת הסוגים
במצב תצוגה מותאם.
SizeMode.Single
מצב ברירת המחדל הוא SizeMode.Single
. הוא מציין שרק סוג אחד
תוכן סופק, כלומר, גם אם הגודל הזמין של AppWidget
ישתנה,
גודל התוכן לא משתנה.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the minimum size or resizable // size defined in the App Widget metadata val size = LocalSize.current // ... } }
כשמשתמשים במצב הזה, צריך לוודא את הדברים הבאים:
- ערכי המטא-נתונים של הגודל המינימלי והמקסימלי מוגדרים כראוי, בהתאם על גודל התוכן.
- התוכן גמיש מספיק בטווח הגדלים הצפוי.
באופן כללי, כדאי להשתמש במצב הזה כאשר:
א) לAppWidget
יש גודל קבוע, או
ב) הוא לא משנה את התוכן שלו לאחר שינוי הגודל.
SizeMode.Responsive
המצב הזה מקביל למתן פריסות רספונסיביות, שמאפשר
GlanceAppWidget
כדי להגדיר קבוצה של פריסות רספונסיביות שתחום על ידי
בגדלים שונים. עבור כל גודל מוגדר, התוכן נוצר וממופה
הגודל בזמן היצירה או העדכון של AppWidget
. לאחר מכן המערכת בוחרת
ההתאמה הטובה ביותר בהתאם למידה הזמינה.
לדוגמה, ביעד AppWidget
אפשר להגדיר שלושה גדלים
content:
class MyAppWidget : GlanceAppWidget() { companion object { private val SMALL_SQUARE = DpSize(100.dp, 100.dp) private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp) private val BIG_SQUARE = DpSize(250.dp, 250.dp) } override val sizeMode = SizeMode.Responsive( setOf( SMALL_SQUARE, HORIZONTAL_RECTANGLE, BIG_SQUARE ) ) override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be one of the sizes defined above. val size = LocalSize.current Column { if (size.height >= BIG_SQUARE.height) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) } Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width >= HORIZONTAL_RECTANGLE.width) { Button("School") } } if (size.height >= BIG_SQUARE.height) { Text(text = "provided by X") } } } }
בדוגמה הקודמת, השיטה provideContent
נקראת שלוש פעמים,
ממופים לגודל שהוגדר.
- בקריאה הראשונה, הגודל מוערך ל-
100x100
. התוכן לא לכלול את הלחצן הנוסף או את הטקסטים העליונים והתחתונים. - בקריאה השנייה, הגודל מוערך ל-
250x100
. התוכן כולל את לחצן נוסף, אבל לא בטקסטים העליונים והתחתונים. - בקריאה השלישית, הגודל מוערך ל-
250x250
. התוכן כולל את לחצן נוסף ואת שני הטקסטים.
SizeMode.Responsive
הוא שילוב של שני המצבים האחרים, והוא מאפשר
להגדיר תוכן רספונסיבי בגבולות מוגדרים מראש. באופן כללי, המצב הזה
מניב ביצועים טובים יותר ומאפשר מעברים חלקים יותר כשמשנים את הגודל של AppWidget
.
בטבלה הבאה מוצג ערך המידה, בהתאם לפרמטרים SizeMode
ו-
הגודל הזמין AppWidget
:
גודל זמין | 105 x 110 | 203 x 112 | 72 x 72 | 203 x 150 |
---|---|---|---|---|
SizeMode.Single |
110 x 110 | 110 x 110 | 110 x 110 | 110 x 110 |
SizeMode.Exact |
105 x 110 | 203 x 112 | 72 x 72 | 203 x 150 |
SizeMode.Responsive |
80 x 100 | 80 x 100 | 80 x 100 | 150 x 120 |
* הערכים המדויקים הם למטרות הדגמה בלבד. |
SizeMode.Exact
הפונקציה SizeMode.Exact
מקבילה למתן פריסות מדויקות,
מבקש את התוכן GlanceAppWidget
בכל פעם שהגודל הזמין של AppWidget
משתנה (לדוגמה, כשהמשתמש משנה את הגודל של AppWidget
במסך הבית).
לדוגמה, בווידג'ט היעד, ניתן להוסיף לחצן נוסף אם הרוחב הזמין גדול מערך מסוים.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the size of the AppWidget val size = LocalSize.current Column { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width > 250.dp) { Button("School") } } } } }
המצב הזה מספק יותר גמישות מהמצבים האחרים, אבל יש בו נקודות שכדאי לשים לב אליהן:
- צריך ליצור מחדש את השדה
AppWidget
באופן מלא בכל פעם שהגודל משתנה. הזה עלולות להוביל לבעיות בביצועים ולקפיצות בממשק המשתמש כשהתוכן מורכב. - הגודל הזמין עשוי להשתנות בהתאם לאופן ההטמעה של מרכז האפליקציות. לדוגמה, אם מרכז האפליקציות לא מספק רשימת גדלים, ערך המינימום נעשה שימוש בגודל אפשרי.
- במכשירים עם מערכת הפעלה ישנה יותר מ-Android 12, יכול להיות שהלוגיקה של חישוב הגודל לא תפעל בכל המכשירים במצבים מסוימים.
באופן כללי, כדאי להשתמש במצב הזה אם לא ניתן להשתמש ב-SizeMode.Responsive
(כלומר, לא ניתן להשתמש בקבוצה קטנה של פריסות רספונסיביות).
גישה למשאבים
אפשר להשתמש ב-LocalContext.current
כדי לגשת לכל משאב של Android, כמו שמוצג
בדוגמה הבאה:
LocalContext.current.getString(R.string.glance_title)
מומלץ לספק מזהי משאבים ישירות כדי להקטין את הגודל של
RemoteViews
וכדי להפעיל משאבים דינמיים, כמו דינמיים,
צבעים.
תכנים קומפוזביליים ושיטות מקבלים משאבים באמצעות 'ספק', כמו
ImageProvider
, או בשיטה של עומס יתר כמו
GlanceModifier.background(R.color.blue)
. לדוגמה:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
ידית הטקסט
התכונה 'בקצרה' 1.1.0 כוללת ממשק API להגדרת סגנונות הטקסט. הגדרת סגנונות טקסט באמצעות
fontSize
, fontWeight
או fontFamily
של המחלקה TextStyle.
ב-fontFamily
יש תמיכה בכל הגופנים של המערכת, כפי שמוצג בדוגמה הבאה, אבל
גופנים מותאמים אישית באפליקציות אינם נתמכים:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
הוספת לחצנים מורכבים
הלחצנים המורכבים נוספו ל-Android 12. התכונה 'בקצרה' תומכת לאחור תאימות לסוגים הבאים של לחצנים מורכבים:
בכל אחד מהלחצנים המורכבים האלה מופיעה תצוגה קליקבילית שמייצגת 'סומן' .
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableStateOf(0) } CheckBox( checked = isApplesChecked, onCheckedChange = { isApplesChecked = !isApplesChecked }, text = "Apples" ) Switch( checked = isEnabledSwitched, onCheckedChange = { isEnabledSwitched = !isEnabledSwitched }, text = "Enabled" ) RadioButton( checked = isRadioChecked == 1, onClick = { isRadioChecked = 1 }, text = "Checked" )
כשהמצב משתנה, ה-lambda שסופקה מופעל. אפשר לאחסן את כפי שמוצג בדוגמה הבאה:
class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { val myRepository = MyRepository.getInstance() provideContent { val scope = rememberCoroutineScope() val saveApple: (Boolean) -> Unit = { scope.launch { myRepository.saveApple(it) } } MyContent(saveApple) } } @Composable private fun MyContent(saveApple: (Boolean) -> Unit) { var isAppleChecked by remember { mutableStateOf(false) } Button( text = "Save", onClick = { saveApple(isAppleChecked) } ) } }
אפשר לספק את המאפיין colors
גם עבור CheckBox
, Switch
וגם
RadioButton
כדי להתאים אישית את הצבעים:
CheckBox( // ... colors = CheckboxDefaults.colors( checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight), uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked } ) Switch( // ... colors = SwitchDefaults.colors( checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan), uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta), checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow), uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked }, text = "Enabled" ) RadioButton( // ... colors = RadioButtonDefaults.colors( checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow), uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue) ), )
רכיבים נוספים
הגרסה 'בקצרה 1.1.0' כוללת הפצה של רכיבים נוספים, כפי שמתואר הטבלה הבאה:
שם | תמונה | קישור הפניה | הערות נוספות |
---|---|---|---|
לחצן מלא | רכיב | ||
לחצני מתאר | רכיב | ||
לחצני סמלים | רכיב | ראשי / משני / סמל בלבד | |
סרגל הכותרת | רכיב | ||
פיגום | 'פיגומים' וסרגל הכותרת הם באותה הדגמה. |
לקבלת מידע נוסף על מפרטי עיצוב, עיינו בעיצובים של הרכיבים ערכת עיצוב ב-Figma.