כדי לתמוך בתוכן שניתן להתאמה באפליקציה, הטמיעו תנועות פינץ' (צביטה) לשינוי מרחק התצוגה. זו השיטה הסטנדרטית והעקבית בפלטפורמה לשיפור הנגישות, שמאפשרת למשתמשים להתאים באופן אינטואיטיבי את הגודל של הטקסט ושל רכיבי ממשק המשתמש לפי הצרכים שלהם. אתם יכולים להגדיר באפליקציה התנהגות מותאמת אישית של שינוי קנה מידה, עם שליטה מפורטת והתנהגות לפי הקשר, שמציעה חוויה שהמשתמשים בדרך כלל מגלים מהר יותר מאשר תכונה ברמת המערכת כמו הגדלת המסך.
בחירת אסטרטגיית התאמה לעומס (scaling)
האסטרטגיות שמתוארות במדריך הזה גורמות לממשק המשתמש להתארגן מחדש כדי להתאים לרוחב המסך. כך משתפרת הנגישות, כי לא צריך להזיז את המסך אופקית או בתנועת זיגזג כדי לקרוא שורות טקסט ארוכות.
קריאה נוספת: מחקרים מאשרים שלמשתמשים עם לקות ראייה, תוכן שמותאם מחדש קריא וקל יותר לניווט מאשר ממשקים שדורשים הזזה דו-ממדית. לפרטים נוספים, ראו השוואה בין תוכן עם הזזה והגדלה לבין תוכן שניתן להזרמה במכשירים ניידים.
שינוי קנה המידה של כל הרכיבים או רק של רכיבי הטקסט
בטבלה הבאה מוצגת ההשפעה החזותית של כל אסטרטגיית התאמה.
| אסטרטגיה | קנה מידה לצפיפות | שינוי גודל הגופן |
|---|---|---|
התנהגות |
התאמת הגודל של כל האלמנטים באופן פרופורציונלי. התוכן משתנה בהתאם לגודל של המאגר שלו, כך שהמשתמש לא צריך להזיז את המסך אופקית כדי לראות את כל התוכן. |
משפיע רק על רכיבי טקסט. הפריסה הכללית והרכיבים שהם לא טקסט נשארים באותו גודל. |
מהם הסולמות |
כל הרכיבים החזותיים: טקסט, רכיבים (כפתורים, סמלים), תמונות וריווח בפריסה (ריפוד, שוליים) |
טקסט בלבד |
הדגמה |
המלצות
עכשיו, אחרי שראיתם את ההבדלים החזותיים, הטבלה הבאה תעזור לכם לשקול את היתרונות והחסרונות ולבחור את האסטרטגיה הטובה ביותר לתוכן שלכם.
סוג ממשק המשתמש |
שיטה מומלצת |
הסבר |
פריסות עם הרבה טקסט דוגמאות: כתבות, אפליקציות להעברת הודעות |
צפיפות או שינוי גודל הגופן |
כדי לשנות את הגודל של כל אזור התוכן, כולל תמונות מוטבעות, עדיף להשתמש בשינוי קנה מידה של צפיפות. אם צריך לשנות רק את קנה המידה של הטקסט, אפשר להשתמש בשינוי קנה המידה של הגופן. |
פריסות עם מבנה חזותי דוגמאות: חנויות אפליקציות, פידים של רשתות חברתיות |
שינוי קנה המידה של הצפיפות |
שומר על הקשרים החזותיים בין תמונות וטקסט בקרוסלות או ברשתות. העימוד מחדש מונע הזזה אופקית, שעלולה להתנגש עם רכיבי גלילה בתצוגה מקוננת. |
זיהוי תנועות לשינוי קנה מידה ב-Jetpack Compose
כדי לתמוך בתוכן שניתן להתאמה על ידי המשתמש, קודם צריך לזהות תנועות עם מגע במספר נקודות. ב-Jetpack Compose, אפשר לעשות את זה באמצעות Modifier.transformable.
המשתנה transformable הוא API ברמה גבוהה שמספק את הדלתא של zoomChange מאז אירוע התנועה האחרון. כך הלוגיקה של עדכון המצב הופכת לצבירה ישירה (לדוגמה, scale *= zoomChange), וזה אידיאלי לאסטרטגיות של התאמה דינמית לעומס שמפורטות במדריך הזה.
הטמעות לדוגמה
בדוגמאות הבאות אפשר לראות איך מטמיעים את האסטרטגיות של שינוי קנה המידה של הצפיפות ושינוי קנה המידה של הגופן.
קנה מידה לצפיפות
הגישה הזו משנה את הגודל של ה-density הבסיסי של אזור בממשק המשתמש. כתוצאה מכך, כל המדידות שמבוססות על פריסה – כולל ריפוד, ריווח וגדלים של רכיבים – משתנות, כאילו גודל המסך או הרזולוציה השתנו. מכיוון שגודל הטקסט תלוי גם בצפיפות, הוא משתנה באופן יחסי. השיטה הזו יעילה כשרוצים להגדיל באופן אחיד את כל הרכיבים באזור מסוים, תוך שמירה על הקצב החזותי הכולל ועל הפרופורציות של ממשק המשתמש.
private class DensityScalingState( // Note: For accessibility, typical min/max values are ~0.75x and ~3.5x. private val minScale: Float = 0.75f, private val maxScale: Float = 3.5f, private val currentDensity: Density ) { val transformableState = TransformableState { zoomChange, _, _ -> scaleFactor.floatValue = (scaleFactor.floatValue * zoomChange).coerceIn(minScale, maxScale) } val scaleFactor = mutableFloatStateOf(1f) fun scaledDensity(): Density { return Density( currentDensity.density * scaleFactor.floatValue, currentDensity.fontScale ) } } @Composable fun DensityScalingSample() { val currentDensity = LocalDensity.current val scaleState = remember(currentDensity) { DensityScalingState(currentDensity = currentDensity) } Box( modifier = Modifier .fillMaxSize() .transformable(state = scaleState.transformableState), contentAlignment = Alignment.TopCenter ) { CompositionLocalProvider( LocalDensity provides scaleState.scaledDensity() ) { DemoCard() } } }
שינוי גודל הגופן
האסטרטגיה הזו ממוקדת יותר, ומשנה רק את הגורם fontScale. התוצאה היא שרק רכיבי הטקסט גדלים או קטנים, וכל שאר רכיבי הפריסה – כמו מאגרים, ריפוד וסמלים – נשארים בגודל קבוע. האסטרטגיה הזו מתאימה לשיפור קריאוּת הטקסט באפליקציות שכוללות הרבה קריאה.
class FontScaleState( // Note: For accessibility, typical min/max values are ~0.75x and ~3.5x. private val minScale: Float = 0.75f, private val maxScale: Float = 3.5f, private val currentDensity: Density ) { val transformableState = TransformableState { zoomChange, _, _ -> scaleFactor.floatValue = (scaleFactor.floatValue * zoomChange).coerceIn(minScale, maxScale) } val scaleFactor = mutableFloatStateOf(1f) fun scaledFont(): Density { return Density( currentDensity.density, currentDensity.fontScale * scaleFactor.floatValue ) } } @Composable fun FontScalingSample() { val currentDensity = LocalDensity.current val scaleState = remember { FontScaleState(currentDensity = currentDensity) } Box( modifier = Modifier .fillMaxSize() .transformable(state = scaleState.transformableState), contentAlignment = Alignment.TopCenter ) { CompositionLocalProvider( LocalDensity provides scaleState.scaledFont() ) { DemoCard() } } }
ממשק משתמש משותף להדגמה
זהו הרכיב הניתן לשיתוף DemoCard שמשמש את שתי הדוגמאות הקודמות כדי להדגיש את התנהגויות ההתאמה השונות.
@Composable private fun DemoCard() { Card( modifier = Modifier .width(360.dp) .padding(16.dp), shape = RoundedCornerShape(12.dp) ) { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text("Demo Card", style = MaterialTheme.typography.headlineMedium) var isChecked by remember { mutableStateOf(true) } Row(verticalAlignment = Alignment.CenterVertically) { Text("Demo Switch", Modifier.weight(1f), style = MaterialTheme.typography.bodyLarge) Switch(checked = isChecked, onCheckedChange = { isChecked = it }) } Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Filled.Person, "Icon", Modifier.size(32.dp)) Spacer(Modifier.width(8.dp)) Text("Demo Icon", style = MaterialTheme.typography.bodyLarge) } Row( Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Box( Modifier .width(100.dp) .weight(1f) .height(80.dp) .background(Color.Blue) ) Box( Modifier .width(100.dp) .weight(1f) .height(80.dp) .background(Color.Red) ) } Text( "Demo Text: Lorem ipsum dolor sit amet, consectetur adipiscing elit," + " sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", style = MaterialTheme.typography.bodyMedium, textAlign = TextAlign.Justify ) } } }
טיפים ושיקולים
כדי ליצור חוויה נגישה ומשופרת, כדאי לפעול לפי ההמלצות הבאות:
- כדאי להציע פקדי שינוי קנה מידה שלא מבוססים על תנועות: יכול להיות שחלק מהמשתמשים יתקשו להשתמש בתנועות. כדי לתמוך במשתמשים האלה, כדאי לספק דרך חלופית להתאמה או לאיפוס של קנה המידה שלא מסתמכת על תנועות.
- התאמה לכל קנה מידה: בודקים את ממשק המשתמש גם בהתאמה לקנה המידה בתוך האפליקציה וגם בהגדרות הגופן או התצוגה בכל המערכת. מוודאים שהפריסות של האפליקציה מותאמות בצורה נכונה בלי לשבש, לחפוף או להסתיר את התוכן. איך יוצרים פריסות דינמיות