לפעמים יש צורך לעקוף את התנהגות ברירת המחדל של המיקוד של הרכיבים שבמסך. לדוגמה, יכול להיות שתרצו לקבץ תכנים קומפוזביליים, להתמקד בתוכן קומפוזבילי מסוים, שמבקשים התמקדות בתוכן אחד, לכוד או לשחרר את המיקוד, או לנתב מחדש את המיקוד בכניסה או ביציאה. הזה בקטע הזה מוסבר איך לשנות את התנהגות המיקוד כשברירות המחדל הן לא צריכים.
יצירת ניווט ברור באמצעות קבוצות מיקוד
לפעמים, 'Jetpack פיתוח נייטיב' לא מנחש מיד את הפריט הבא
ניווט באמצעות כרטיסיות, במיוחד כשרכיב הורה מורכב, Composables
, כמו כרטיסיות
הרשימות נכנסות לתוקף.
אמנם החיפוש ממוקד בדרך כלל תואם לסדר ההצהרה של Composables
,
במקרים מסוימים, הדבר בלתי אפשרי, למשל כאשר אחד מהComposables
ב-
ההיררכיה היא פורמט אופקי שניתן לגלילה שאינה גלויה במלואה. מוצג ב
בדוגמה הבאה.
'Jetpack פיתוח נייטיב' עשוי להחליט להתמקד בפריט הבא הקרוב ביותר לתחילת במסך, כפי שמוצג למטה, במקום להמשיך בנתיב הצפוי. ניווט חד-כיווני:
![אנימציה של אפליקציה שמציגה ניווט אופקי בחלק העליון ורשימת פריטים למטה.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_1.gif?authuser=1&hl=he)
בדוגמה הזו, ברור שהמפתחים לא התכוונו שהמיקוד מהכרטיסייה שוקולד לתמונה הראשונה למטה, ואז חזרה אל בכרטיסייה מאפים. במקום זאת, הם רצו שהמיקוד ימשיך בכרטיסיות עד בכרטיסייה האחרונה, ואז מתמקדים בתוכן הפנימי:
![אנימציה של אפליקציה שמציגה ניווט אופקי בחלק העליון ורשימת פריטים למטה.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_2.gif?authuser=1&hl=he)
במצבים שבהם חשוב שקבוצה של תכנים קומפוזביליים יתמקדו
ברצף, כמו בשורת ה-Tab מהדוגמה הקודמת, צריך
Composable
בהורה עם הצירוף focusGroup()
:
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
ניווט דו-כיווני מחפש את התוכן הקומפוזבילי הקרוב ביותר
כיוון - אם רכיב מקבוצה אחרת קרוב יותר מקבוצה שאינה גלויה במלואה
הפריט בקבוצה הנוכחית, הניווט בוחר את הפריט הקרוב ביותר. כדי להימנע מכך
ההתנהגות הזו, אפשר להחיל את מקש הצירוף focusGroup()
.
FocusGroup
גורם לקבוצה שלמה להיראות כמו ישות אחת מבחינת המיקוד,
אבל הקבוצה עצמה לא תמשוך את תשומת הלב, אלא הילד הקרוב ביותר
להתמקד במקום זאת. באופן הזה, הניווט יידע לעבור לפריט שאינו גלוי במלואו
לפני עזיבת הקבוצה.
במקרה הזה, שלושת המופעים של FilterChip
יתמקדו לפני
SweetsCard
פריטים, גם אם SweetsCards
גלוי באופן מלא
משתמש מסוים וייתכן שחלק מ-FilterChip
מוסתרים. הסיבה לכך היא
מקש הצירוף focusGroup
מורה למנהל המיקוד לשנות את הסדר שבו הפריטים
כדי שהניווט יהיה קל ועקבי יותר עם ממשק המשתמש.
בלי מקש הצירוף focusGroup
, אם FilterChipC
לא היה גלוי, המיקוד
הוא ישפר אותו בפעם האחרונה. אבל, הוספת התאמה כזו לא מונעת
רק ניתן לגילוי, אבל הוא תתמקד גם מיד לאחר FilterChipB
, כמו
המשתמשים יצפו לכך.
יצירת תוכן קומפוזבילי
יש תכנים קומפוזביליים שאפשר להתמקד בהם בעיצוב, כמו 'לחצן' או תוכן קומפוזבילי עם
צירוף הצירוף clickable
. אם רוצים להוסיף
אפשר להתמקד בפורמט של תוכן קומפוזבילי, צריך להשתמש בתכונת הצירוף focusable
:
var color by remember { mutableStateOf(Green) } Box( Modifier .background(color) .onFocusChanged { color = if (it.isFocused) Blue else Green } .focusable() ) { Text("Focusable 1") }
להפוך פריט קומפוזבילי לבלתי ניתן למיקוד
עשויים להיות מצבים שבהם חלק מהאלמנטים לא ייכללו
בתוך הפוקוס. במקרים הנדירים האלה, תוכלו להשתמש ב-canFocus property
כדי להחריג Composable
כך שלא יהיה ניתן למיקוד.
var checked by remember { mutableStateOf(false) } Switch( checked = checked, onCheckedChange = { checked = it }, // Prevent component from being focused modifier = Modifier .focusProperties { canFocus = false } )
בקשת מיקוד המקלדת באמצעות FocusRequester
במקרים מסוימים, ייתכן שתרצו לבקש מיקוד באופן מפורש כתגובה לאינטראקציה של המשתמשים. לדוגמה, אפשר לשאול את המשתמש אם הוא רוצה להפעיל מחדש מילוי טופס, ואם הם לחצו על 'כן' אתם רוצים למקד מחדש את השדה הראשון של הטופס הזה.
הדבר הראשון שצריך לעשות הוא לשייך אובייקט FocusRequester
אל
תוכן קומפוזבילי שאליו רוצים להעביר את מיקוד המקלדת. בקוד הבא
קטע קוד, אובייקט FocusRequester
משויך ל-TextField
על ידי הגדרה של
מקש הצירוף Modifier.focusRequester
:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
אפשר לקרוא לשיטת requestFocus
של FocusRequester כדי לשלוח בקשות למיקוד בפועל. צריך להפעיל את השיטה הזו מחוץ להקשר Composable
(אחרת, הוא מופעל מחדש בכל יצירה מחדש). קטע הקוד הבא
מראה איך לבקש מהמערכת להזיז את מיקוד המקלדת כשהלחצן
לחצת:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) ) Button(onClick = { focusRequester.requestFocus() }) { Text("Request focus on TextField") }
צילום ושחרור המיקוד
אפשר למנף את המיקוד כדי להנחות את המשתמשים לספק את הנתונים המתאימים לאפליקציה לבצע את המשימה שלו, כמו קבלת כתובת אימייל או מספר טלפון תקינים מספר. למרות שמצבי שגיאה מיידעים את המשתמשים לגבי מה שקורה, אולי יזדקק לשדה עם מידע שגוי כדי להישאר ממוקדים עד שהוא תוקן.
כדי לצלם את המיקוד, אפשר להפעיל את השיטה captureFocus()
.
אחר כך משחררים אותו ב-method freeFocus()
, כמו בדוגמה הבאה
דוגמה:
val textField = FocusRequester() TextField( value = text, onValueChange = { text = it if (it.length > 3) { textField.captureFocus() } else { textField.freeFocus() } }, modifier = Modifier.focusRequester(textField) )
הקדימות של מגבילי המיקוד
Modifiers
יכול להופיע כרכיבים שיש להם רק צאצא אחד, כך שכאשר הם נמצאים בתור
כל Modifier
בצד שמאל (או למעלה) כולל את ה-Modifier
שעוקב
הימני (או מתחתיו). המשמעות היא שהModifier
השני נמצא בתוך
הראשון, כך שכשמצהירים על שני focusProperties
, רק החלק העליון
אחת עובדת, כי הערכים הבאים נמצאים בחלק העליון.
כדי להבהיר את הנושא יותר, כדאי לעיין בקוד הבא:
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
במקרה הזה, focusProperties
שמציין את item2
כמיקוד הנכון
לא להשתמש בו, כפי שהוא נכלל בתנאי הקודם; ולכן, item1
יהיה
שבו נעשה שימוש.
בעזרת הגישה הזו, ההורים יכולים לאפס את תבנית ההתנהגות לברירת המחדל:
באמצעות FocusRequester.Default
:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
ההורה לא חייב להיות חלק מאותה שרשרת מגביל. הורה
תוכן קומפוזבילי יכול להחליף מאפיין מיקוד של תוכן צאצא קומפוזבילי. לדוגמה,
נבחן את הFancyButton
הבא שלא מאפשר להתמקד בלחצן:
@Composable fun FancyButton(modifier: Modifier = Modifier) { Row(modifier.focusProperties { canFocus = false }) { Text("Click me") Button(onClick = { }) { Text("OK") } } }
המשתמש יכול להפוך את הלחצן הזה שוב למיקוד באמצעות הגדרה של canFocus
לערך true
:
FancyButton(Modifier.focusProperties { canFocus = true })
כמו בכל Modifier
, מילות המפתח שקשורות למיקוד מתנהגות באופן שונה בהתאם לסדר
שאתם מצהירים עליהם. לדוגמה, קוד כמו הבא הופך את הפונקציה Box
ניתן למיקוד, אבל ה-FocusRequester
לא משויך לתוכן הזה כי הוא
מוצהר אחרי שניתן למיקוד.
Box( Modifier .focusable() .focusRequester(Default) .onFocusChanged {} )
חשוב לזכור ש-focusRequester
משויך
שניתן להתמקד בו מתחתיה בהיררכיה, כך שהfocusRequester
הזה מפנה אל
ילדים שאפשר להתמקד בהם. אם אף אחת מהאפשרויות לא זמינה, היא לא תצביע על שום דבר.
עם זאת, מכיוון שניתן להתמקד בBox
(הודות למקש הצירוף focusable()
),
תוכל לנווט אליו באמצעות ניווט דו-כיווני.
דוגמה נוספת: כל אחד מהאפשרויות הבאות יעבוד, כמו onFocusChanged()
מתייחס לאלמנט הראשון שניתן למיקוד שמופיע אחרי
focusable()
או focusTarget()
מוסיפים.
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
הפניה אוטומטית של המיקוד בכניסה או ביציאה
לפעמים תצטרכו לספק סוג ניווט ספציפי מאוד, כמו שמוצגת באנימציה הבאה:
![אנימציה של מסך שבו מוצגות שתי עמודות של לחצנים שמוצבים זה לצד זה ואנימציה של המיקוד מעמודה אחת לשנייה.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_1.gif?authuser=1&hl=he)
לפני שנתעמק באופן יצירתי, חשוב להבין מהי ברירת המחדל
התנהגות החיפוש של המיקוד. ללא שינויים, ברגע שהמיקוד הוא
מגיע לפריט Clickable 3
, לחיצה על DOWN
בלחצני החיצים (או מקבילה)
מקש חץ) יעביר את המוקד למה שמוצג מתחת ל-Column
,
לעזוב את הקבוצה ומתעלמים מהקבוצה שמשמאל. אם אין
יש פריטים שניתן להתמקד בהם, המיקוד לא זז לשום מקום אלא נשאר
Clickable 3
כדי לשנות את ההתנהגות הזו ולספק את הניווט המיועד, אפשר להשתמש
מגביל focusProperties
, שעוזר לנהל מה יקרה כשהמיקוד
החיפוש נכנס אל Composable
או יוצא ממנו:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
אפשר להפנות את המוקד ל-Composable
ספציפי בכל פעם שהוא נכנס
או יוצא מחלק מסוים של ההיררכיה — לדוגמה, כשבממשק המשתמש שלך יש
ואתם רוצים לוודא שבכל פעם שהעיבוד של העמודה הראשונה יסתיים,
המיקוד עובר לשנייה:
![אנימציה של מסך שבו מוצגות שתי עמודות של לחצנים שמוצבים זה לצד זה ואנימציה של המיקוד מעמודה אחת לשנייה.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_2.gif?authuser=1&hl=he)
ב-GIF הזה, כשהמיקוד מגיע ל-Clickable 3 Composable
ב-Column
1,
הפריט הבא שעליו מתמקדים הוא Clickable 4
בColumn
אחר. ההתנהגות הזו
ניתן להשיג באמצעות שילוב של focusDirection
עם enter
ו-exit
ערכים בתוך מקש הצירוף focusProperties
. לשניהם צריך lambda
בתור פרמטר, הכיוון שממנו מגיע המוקד ומחזיר
FocusRequester
ה-lambda הזה יכול להתנהג בשלוש דרכים שונות:
FocusRequester.Cancel
מפסיק את הפוקוס להמשיך, אבל
FocusRequester.Default
לא משנה את ההתנהגות שלו. במקום זאת, מספקת את
FocusRequester
מצורף אל Composable
אחר, והמיקוד עובר אליו
ספציפי Composable
.
שינוי כיוון ההתקדמות של המיקוד
כדי להעביר את המיקוד לפריט הבא או לכיוון מדויק,
להשתמש בתכונת השינוי onPreviewKey
ולרמז על LocalFocusManager
כדי
מקדמים את המיקוד בעזרת ההתאמה moveFocus
.
הדוגמה הבאה מציגה את התנהגות ברירת המחדל של מנגנון ההתמקדות:
זוהתה הקשה אחת (tab
), המיקוד יתקדם לרכיב הבא בפוקוס
חדשה. אומנם לא צריך להגדיר את זה בדרך כלל, אבל
לדעת את הפעולות הפנימיות של המערכת,
ולשנות את ברירת המחדל
או התנהגות המשתמשים.
val focusManager = LocalFocusManager.current var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.onPreviewKeyEvent { when { KeyEventType.KeyUp == it.type && Key.Tab == it.key -> { focusManager.moveFocus(FocusDirection.Next) true } else -> false } } )
בדוגמה הזו, הפונקציה focusManager.moveFocus()
מקדמת את המיקוד
של הפריט שצוין, או של הכיוון המשתמע בפרמטר של הפונקציה.
מומלץ עבורך
- הערה: טקסט הקישור מוצג כאשר JavaScript מושבת
- תגובה למיקוד
- התמקדות בכתיבה
- שינוי סדר המעבר למיקוד