התוכן הבא מיועד למפתחי אפליקציות.
כדי לאפשר לאפליקציה לתמוך ב-Rotary, חובה:
- מוסיפים את הערך
FocusParkingView
בפריסה המתאימה של הפעילות. - מוודאים אילו תצוגות ניתנות (או לא ניתנות) למיקוד.
- משתמשים ב-
FocusArea
כדי לעטוף את כל התצוגות שניתן להתמקד בהן, מלבדFocusParkingView
.
כל אחת מהמשימות האלה מפורטת בהמשך, אחרי שתגדירו את הסביבה לפיתוח אפליקציות עם תמיכה ב-Rotary.
הגדרת חוגה
כדי להתחיל לפתח אפליקציות עם רכיב רוטורי, צריך בקר רוטורי או תחליף. יש לכם את האפשרויות שמתוארות בהמשך.
מכשיר הדמיה
source build/envsetup.sh && lunch car_x86_64-userdebug m -j emulator -wipe-data -no-snapshot -writable-system
אפשר גם להשתמש ב-aosp_car_x86_64-userdebug
.
כדי לגשת לשלט האלקטרוני המבוסס על תנועה סיבובית:
- מקישים על שלוש הנקודות בתחתית סרגל הכלים:
איור 1. גישה לשלט חוגה ממומש - בוחרים באפשרות Car rotary בחלון הפקדים המורחבים:
איור 2. בוחרים באפשרות 'תחנת טעינה לרכב מסתובב'
מקלדת USB
- מחברים מקלדת USB למכשיר שבו פועלת מערכת ההפעלה Android Automotive OS (AAOS). במקרים מסוימים, הפעולה הזו מונעת את ההצגה של המקלדת במסך.
- משתמשים ב-build של
userdebug
אוeng
. - מפעילים סינון של אירועים מרכזיים:
adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
- בטבלה הבאה מופיע המפתח המתאים לכל פעולה:
מפתח פעולה סיבובית Q סיבוב נגד כיוון השעון E סיבוב בכיוון השעון A הסט שמאלה D הסט ימינה W הסט כלפי מעלה S הסט כלפי מטה F או פסיק הכפתור המרכזי R או Esc לחצן 'הקודם'
פקודות ADB
אפשר להשתמש בפקודות car_service
כדי להחדיר אירועי קלט של גלגלות. אפשר להריץ את הפקודות האלה במכשירים עם Android Automotive OS (AAOS) או במהדמ.
פקודות car_service | חוגה להזנת נתונים |
---|---|
adb shell cmd car_service inject-rotary |
סיבוב נגד כיוון השעון |
adb shell cmd car_service inject-rotary -c true |
סיבוב בכיוון השעון |
adb shell cmd car_service inject-rotary -dt 100 50 |
סיבוב נגד כיוון השעון כמה פעמים (לפני 100 אלפיות השנייה ולפני 50 אלפיות השנייה) |
adb shell cmd car_service inject-key 282 |
הסט שמאלה |
adb shell cmd car_service inject-key 283 |
הסט ימינה |
adb shell cmd car_service inject-key 280 |
הסט כלפי מעלה |
adb shell cmd car_service inject-key 281 |
הסט כלפי מטה |
adb shell cmd car_service inject-key 23 |
לחיצה על הכפתור המרכזי |
adb shell input keyevent inject-key 4 |
לחיצה על לחצן 'הקודם' |
בקר חוגה של OEM
כשחומרת הבקר האלקטרוני פועלת, זו האפשרות הכי מציאותית. האפשרות הזו שימושית במיוחד לבדיקת רוטציה מהירה.
FocusParkingView
FocusParkingView
היא תצוגה שקופה בספריית ממשק המשתמש ברכב (car-ui-library).
RotaryService
משתמש בו כדי לתמוך בניווט באמצעות בקר רוטורי.
FocusParkingView
חייבת להיות התצוגה הראשונה בפריסה שאפשר להתמקד בה. צריך למקם אותו מחוץ לכל ה-FocusArea
. לכל חלון צריך להיות FocusParkingView
אחד. אם אתם כבר משתמשים בפריסה הבסיסית של car-ui-library, שמכילה FocusParkingView
, אין צורך להוסיף עוד FocusParkingView
. בהמשך מוצגת דוגמה ל-FocusParkingView
ב-RotaryPlayground
.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.android.car.ui.FocusParkingView android:layout_width="wrap_content" android:layout_height="wrap_content"/> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>
אלה הסיבות שבגללן אתם צריכים FocusParkingView
:
- מערכת Android לא מבטלת את המיקוד באופן אוטומטי כשהמיקוד מוגדר בחלון אחר. אם תנסו לבטל את המיקוד בחלון הקודם, מערכת Android תתמקד מחדש בתצוגה בחלון הזה, וכתוצאה מכך שני החלונות ימוקדו בו-זמנית. הוספת
FocusParkingView
לכל חלון יכולה לפתור את הבעיה הזו. התצוגה הזו שקופה וההדגשה של מוקד ההקלדה שמוגדרת כברירת מחדל מושבתת, כך שהיא לא גלויה למשתמש גם אם הוא מתמקד בה וגם אם לא. הוא יכול לקבל את המיקוד כדי ש-RotaryService
יוכל להעביר את המיקוד אליו כדי להסיר את ההדגשה של המיקוד. - אם יש רק
FocusArea
אחד בחלון הנוכחי, סיבוב הבקר ב-FocusArea
גורם ל-RotaryService
להעביר את המיקוד מהתצוגה בצד שמאל לתצוגה בצד ימין (ולהפך). הוספת התצוגה הזו לכל חלון עשויה לפתור את הבעיה. כש-RotaryService
קובע שהיעד של המיקוד הואFocusParkingView
, הוא יכול לקבוע שעומד להתרחש מעגל חוזר, ובשלב הזה הוא ימנע את המעגל החוזר על ידי כך שלא יעביר את המיקוד. - כשמפעילים אפליקציה באמצעות הלחצן המסתובב, מערכת Android מתמקדת בתצוגה הראשונה שאפשר להתמקד בה, תמיד
FocusParkingView
. הפונקציהFocusParkingView
קובעת את התצוגה האופטימלית להתמקד בה, ואז מחילה את המיקוד.
תצוגות שניתנות למיקוד
RotaryService
מבוסס על המושג הקיים של Android לגבי התמקדות בתצוגה, שהחל עוד כשלטלפונים היו מקלדות פיזיות ומשטחי D-pad.
המאפיין הקיים android:nextFocusForward
משמש לצורך גלילה (ראו התאמה אישית של FocusArea), אבל המאפיינים android:nextFocusLeft
, android:nextFocusRight
, android:nextFocusUp
ו-android:nextFocusDown
לא משמשים לכך.
RotaryService
מתמקדת רק בתצוגות שאפשר להתמקד בהן. בדרך כלל אפשר להתמקד בתצוגות מסוימות, כמו Button
. אחרים, כמו TextView
ו-ViewGroup
, בדרך כלל לא. אפשר להתמקד באופן אוטומטי בתצוגות שניתן ללחוץ עליהן, וניתן ללחוץ באופן אוטומטי על תצוגות שיש להן מאזין לקליק. אם הלוגיקה האוטומטית הזו מובילה ליכולת המיקוד הרצויה, אין צורך להגדיר במפורש את יכולת המיקוד של התצוגה. אם הלוגיקה האוטומטית לא מובילה ליכולת המיקוד הרצויה, מגדירים את המאפיין android:focusable
לערך true
או false
, או מגדירים באופן פרוגרמטי את יכולת המיקוד של התצוגה באמצעות View.setFocusable(boolean)
. כדי ש-RotaryService
יתרכז בה, התצוגה חייבת לעמוד בדרישות הבאות:
- ניתן למיקוד
- מופעל
- גלוי
- יש להם ערכים שאינם אפס לרוחב ולגובה
אם תצוגה לא עומדת בכל הדרישות האלה, למשל לחצן שאפשר להתמקד בו אבל הוא מושבת, המשתמש לא יכול להשתמש בלחצן ההזזה כדי להתמקד בו. אם רוצים להתמקד בתצוגות מושבתות, כדאי להשתמש במצב מותאם אישית במקום ב-android:state_enabled
כדי לקבוע איך התצוגה תופיע בלי לציין שמערכת Android צריכה להתייחס אליה כמושבתת. האפליקציה יכולה להודיע למשתמש למה התצוגה מושבתת כשהוא מקייש עליה. בקטע הבא נסביר איך עושים את זה.
מצב מותאם אישית
כדי להוסיף מצב מותאם אישית:
- כדי להוסיף מאפיין מותאם אישית לתצוגה. לדוגמה, כדי להוסיף מצב
state_rotary_enabled
בהתאמה אישית לשיעור התצוגהCustomView
, משתמשים בקוד:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- כדי לעקוב אחרי המצב הזה, מוסיפים משתנה מופע לתצוגה יחד עם שיטות גישה:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- כדי לקרוא את הערך של המאפיין בזמן יצירת התצוגה המפורטת:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- בכיתה של התצוגה, משנים את השיטה
onCreateDrawableState()
ואז מוסיפים את המצב המותאם אישית, במקרים הרלוונטיים. לדוגמה:@Override protected int[] onCreateDrawableState(int extraSpace) { if (mRotaryEnabled) extraSpace++; int[] drawableState = super.onCreateDrawableState(extraSpace); if (mRotaryEnabled) { mergeDrawableStates(drawableState, { R.attr.state_rotary_enabled }); } return drawableState; }
- לגרום לטיפול בקליק של התצוגה לפעול בצורה שונה בהתאם למצב שלה. לדוגמה, ייתכן שמנהל האירועים של הקליקים לא יבצע שום פעולה או שיציג הודעה קופצת כש
mRotaryEnabled
הואfalse
. - כדי שהלחצן יופיע מושבת, צריך להשתמש ב-
app:state_rotary_enabled
במקום ב-android:state_enabled
ב-drawable של הרקע בתצוגה. אם עדיין לא הוספתם את האפשרות הזו, תצטרכו להוסיף את האפשרויות הבאות:xmlns:app="http://schemas.android.com/apk/res-auto"
- אם התצוגה מושבתת בפריסות מסוימות, מחליפים את
android:enabled="false"
ב-app:state_rotary_enabled="false"
ואז מוסיפים את מרחב השמותapp
, כפי שמתואר למעלה. - אם התצוגה מושבתת באופן פרוגרמטי, צריך להחליף את הקריאות ל-
setEnabled()
בקריאות ל-setRotaryEnabled()
.
FocusArea
אפשר להשתמש ב-FocusAreas
כדי לפצל את התצוגות שאפשר להתמקד בהן לבלוקים, כדי להקל על הניווט ולשמור על עקביות עם אפליקציות אחרות. לדוגמה, אם יש באפליקציה סרגל כלים, סרגל הכלים צריך להיות ב-FocusArea
נפרד משאר האפליקציה. גם סרחי הכרטיסיות ורכיבי ניווט אחרים צריכים להיות מופרדים משאר האפליקציה. בדרך כלל, לרשימות גדולות צריך להיות FocusArea
משלהם. אחרת, המשתמשים יצטרכו לעבור דרך כל הרשימה כדי לגשת לתצוגות מסוימות.
FocusArea
הוא תת-סוג של LinearLayout
ב-car-ui-library.
כשהתכונה הזו מופעלת, FocusArea
מצייר הדגשה כשאחד מהצאצאים שלו נמצא בפוקוס. למידע נוסף, ראו התאמה אישית של הדגשת המיקוד.
כשיוצרים בלוק ניווט בקובץ הפריסה, אם רוצים להשתמש ב-LinearLayout
כקונטיינר של הבלוק הזה, צריך להשתמש ב-FocusArea
במקום זאת.
אחרת, צריך לעטוף את הבלוק ב-FocusArea
.
אין להטמיע FocusArea
ב-FocusArea
אחר.
הפעולה הזו מובילה להתנהגות ניווט לא מוגדרת. חשוב לוודא שכל התצוגות שניתן להתמקד בהן מוטמעות ב-FocusArea
.
דוגמה ל-FocusArea
ב-RotaryPlayground
:
<com.android.car.ui.FocusArea android:layout_margin="16dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true"> </EditText> </com.android.car.ui.FocusArea>
FocusArea
פועל באופן הבא:
- כשמפעילים פעולות של סיבוב ודחיפה,
RotaryService
מחפשת מופעים שלFocusArea
בהיררכיית התצוגה. - כשמקבלים אירוע רוטציה,
RotaryService
מעביר את המיקוד לתצוגה אחרת שיכולה לקבל את המיקוד באותוFocusArea
. - כשמקבלים אירוע של דחיפה,
RotaryService
מעבירים את המיקוד לתצוגה אחרת שיכולה לקבל את המיקוד ב-FocusArea
אחר (בדרך כלל צמוד).
אם לא תכללו את FocusAreas
בפריסה, תצוגת הבסיס תטופל כאזור התמקדות משתמע. המשתמש לא יכול להשתמש בהקשה קלה כדי לנווט באפליקציה. במקום זאת, הוא יוכל לעבור בין כל התצוגות שניתן להתמקד בהן, וזה יכול להתאים לדו-שיחות.
התאמה אישית של FocusArea
אפשר להשתמש בשני מאפייני View רגילים כדי להתאים אישית את הניווט המסתובב:
android:nextFocusForward
מאפשר למפתחי אפליקציות לציין את סדר הסיבוב באזור הפוקוס. זהו אותו מאפיין שמשמש לניהול סדר הלחצן Tab לניווט במקלדת. אין להשתמש במאפיין הזה כדי ליצור לולאה. במקום זאת, משתמשים ב-app:wrapAround
(ראו בהמשך) כדי ליצור לולאה.android:focusedByDefault
מאפשר למפתחי אפליקציות לציין את תצוגת המיקוד שמוגדרת כברירת מחדל בחלון. אין להשתמש במאפיין הזה וב-app:defaultFocus
(ראו בהמשך) באותוFocusArea
.
FocusArea
מגדיר גם מאפיינים מסוימים להתאמה אישית של ניווט רוטורי.
אי אפשר להתאים אישית אזורי התמקדות משתמעים באמצעות המאפיינים האלה.
- (Android 11 QPR3, Android 11 Car, Android 12)
אפשר להשתמש ב-app:defaultFocus
כדי לציין את המזהה של תצוגת צאצא שניתן להתמקד בה, ועליה להתמקד כשהמשתמש מצביע עלFocusArea
הזה. - (Android 11 QPR3, Android 11 Car, Android 12)
app:defaultFocusOverridesHistory
אפשר להגדיר ל-true
כדי שהתצוגה שצוינה למעלה תקבל את המיקוד, גם אם היתה מיקוד בתצוגה אחרת ב-FocusArea
הזה. - (Android 12)
משתמשים ב-app:nudgeLeftShortcut
, ב-app:nudgeRightShortcut
, ב-app:nudgeUpShortcut
וב-app:nudgeDownShortcut
כדי לציין את המזהה של תצוגת הצאצא שניתן להתמקד בה, ועליה צריך להתמקד כשהמשתמש מזיז את האצבע בכיוון נתון. מידע נוסף זמין בקטע קיצורי דרך להנעה שבהמשך.(Android 11 QPR3, Android 11 Car, הוצא משימוש ב-Android 12)
app:nudgeShortcut
ו-app:nudgeShortcutDirection
תמכו רק בקיצור דרך אחד להנעה. - (Android 11 QPR3, Android 11 Car, Android 12)
כדי לאפשר סיבוב ב-FocusArea
הזה, אפשר להגדיר אתapp:wrapAround
לערךtrue
. בדרך כלל משתמשים באפשרות הזו כשהתצוגות המפורטות מסודרות בצורה של עיגול או אליפסה. - (Android 11 QPR3, Android 11 Car, Android 12)
כדי לשנות את הריווח של הרגעים המיוחדים ב-FocusArea
הזה, משתמשים ב-app:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
ו-app:highlightPaddingVertical
. - (Android 11 QPR3, Android 11 Car, Android 12)
כדי לשנות את הגבולות הנתפסים שלFocusArea
הזה כדי למצוא יעד של דחיפה, אפשר להשתמש ב-app:startBoundOffset
, ב-app:endBoundOffset
, ב-app:topBoundOffset
, ב-app:bottomBoundOffset
, ב-app:horizontalBoundOffset
וב-app:verticalBoundOffset
. - (Android 11 QPR3, Android 11 Car, Android 12)
כדי לציין באופן מפורש את המזהה שלFocusArea
(או אזורים) סמוכים בהוראות הנתונות, משתמשים ב-app:nudgeLeft
, ב-app:nudgeRight
, ב-app:nudgeUp
וב-app:nudgeDown
. משתמשים באפשרות הזו כשהחיפוש הגיאומטרי שמוגדר כברירת מחדל לא מוצא את היעד הרצוי.
בדרך כלל, דחיפת המשתמשים מתבצעת בין אזורי התמקדות. עם זאת, כשמשתמשים בקיצורי דרך להנעה, לפעמים ההנעה מתבצעת קודם בתוך FocusArea
, כך שהמשתמש עשוי להזדקק לשתי הנעות כדי לנווט ל-FocusArea
הבא. מקשי הקיצור של דחיפת התפריט שימושים כשה-FocusArea
מכיל רשימה ארוכה ואחריה לחצן פעולה צף, כמו בדוגמה הבאה:

בלי מקש הקיצור של הדחיפה, המשתמש יצטרך לעבור את כל הרשימה כדי להגיע ללחצן ה-FAB.
התאמה אישית של הדגשת המיקוד
כפי שצוין למעלה, RotaryService
מבוסס על המושג הקיים של Android לגבי התמקדות בתצוגה. כשהמשתמש מסובב ומזיז, RotaryService
מעביר את המיקוד, ומתמקד בתצוגה אחת ומבטל את המיקוד בתצוגה אחרת. ב-Android, כשהתצוגה ממוקדת, אם התצוגה:
- הוגדר לה הדגשת התמקדות משלה, Android מצייר את הדגשת התמקדות של התצוגה.
- לא צוינה הדגשת מוקד, וההדגשה שמוגדרת כברירת מחדל לא מושבתת, מערכת Android תיצור את ההדגשה שמוגדרת כברירת מחדל בתצוגה.
אפליקציות שמיועדות למגע בדרך כלל לא מציינות את הקטעים המתאימים להדגשת המיקוד.
הדגשת המיקוד שמוגדרת כברירת מחדל מסופקת על ידי מסגרת Android, ואפשר לשנות אותה על ידי יצרן הציוד המקורי. מפתחי אפליקציות מקבלים אותו כשהעיצוב שבו הם משתמשים נגזר מ-Theme.DeviceDefault
.
כדי לשמור על עקביות בחוויית המשתמש, מומלץ להשתמש בהדגשת המיקוד שמוגדרת כברירת מחדל בכל הזדמנות אפשרית.
אם אתם צריכים להדגיש את מוקד העניין בצורה בהתאמה אישית (למשל, עגולה או בצורת גלולה), או אם אתם משתמשים בנושא שלא נגזר מ-Theme.DeviceDefault
, תוכלו להשתמש במשאבים של car-ui-library כדי לציין את ההדגשה של מוקד העניין שלכם לכל תצוגה.
כדי לציין הדגשה מותאמת אישית של מיקוד בתצוגה, משנים את ה-drawable של הרקע או החזית של התצוגה ל-drawable שונה כשהתצוגה ממוקדת. בדרך כלל משנים את הרקע. אם משתמשים באובייקט ה-drawable הבא כרקע לתצוגה ריבועית, הוא יוצר הדגשה עגולה של מוקד התמונה:
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" android:state_pressed="true"> <shape android:shape="oval"> <solid android:color="@color/car_ui_rotary_focus_pressed_fill_color"/> <stroke android:width="@dimen/car_ui_rotary_focus_pressed_stroke_width" android:color="@color/car_ui_rotary_focus_pressed_stroke_color"/> </shape> </item> <item android:state_focused="true"> <shape android:shape="oval"> <solid android:color="@color/car_ui_rotary_focus_fill_color"/> <stroke android:width="@dimen/car_ui_rotary_focus_stroke_width" android:color="@color/car_ui_rotary_focus_stroke_color"/> </shape> </item> <item> <ripple...> ... </ripple> </item> </selector>
(Android 11 QPR3, Android 11 Car, Android 12) הפניות המשאבים מודגשות בדוגמה שלמעלה מזהות משאבים שהוגדרו על ידי car-ui-library. ה-OEM מבטל את ההגדרות האלה כדי שתהיינה עקביות עם הגדרת ברירת המחדל של הדגשת מוקד ההקלדה שהוא מציין. כך, צבע ההדגשה של האזור הממוקד, רוחב הקו וכו' לא ישתנו כשהמשתמש מנווט בין תצוגה עם הדגשה מותאמת אישית של האזור הממוקד לבין תצוגה עם הדגשה שמוגדרת כברירת מחדל של האזור הממוקד. הפריט האחרון הוא רטט שמשמש למגע. ערכי ברירת המחדל המשמשים למשאבים המודגשים מופיעים באופן הבא:

בנוסף, אפשר להשתמש בהדגשה מותאמת אישית של מוקד כשנותנים ללחצן צבע רקע אחיד כדי למשוך את תשומת הלב של המשתמש, כמו בדוגמה שבהמשך. לכן, יכול להיות שקשה לראות את ההדגשה של האזור שבמרכז. במקרה כזה, מציינים הדגשה בהתאמה אישית של מוקד באמצעות צבעים משניים:
![]() |
- (Android 11 QPR3, Android 11 Car, Android 12)
car_ui_rotary_focus_fill_secondary_color
car_ui_rotary_focus_stroke_secondary_color
- (Android 12)
car_ui_rotary_focus_pressed_fill_secondary_color
car_ui_rotary_focus_pressed_stroke_secondary_color
לדוגמה:
![]() |
![]() |
|
מוקדת, לא לוחצים עליה | מוקד, לחוץ |
גלילה סיבובית
אם האפליקציה שלכם משתמשת ב-RecyclerView
, עליכם להשתמש ב-CarUiRecyclerView
במקום זאת. כך תוכלו לוודא שהממשק המשתמש שלכם יהיה עקבי עם אחרים, כי ההתאמה האישית של יצרן הציוד המקורי חלה על כל ה-CarUiRecyclerView
.
אם כל הרכיבים ברשימה ניתנים להדגשה, אין צורך לעשות שום דבר נוסף. הניווט באמצעות לחצן הרוטור מעביר את המיקוד בין הרכיבים ברשימה, והרשימה גוללת כדי להציג את הרכיב החדש שבו מופעל המיקוד.
(Android 11 QPR3, Android 11 Car, Android 12)
אם יש שילוב של רכיבים שאפשר להתמקד בהם ורכיבים שלא, או אם אי אפשר להתמקד בכל הרכיבים, אפשר להפעיל גלילה באמצעות חוגה. כך המשתמש יוכל להשתמש בלחצן החוגה כדי לגלול בהדרגה ברשימה בלי לדלג על פריטים שלא ניתן להתמקד בהם. כדי להפעיל גלילה סיבובית, מגדירים את המאפיין app:rotaryScrollEnabled
לערך true
.
(Android 11 QPR3, Android 11 Car, Android 12)
אפשר להפעיל גלילה באמצעות תנועה סיבובית בכל תצוגה שניתן לגלול בה, כולל avCarUiRecyclerView
, באמצעות השיטה setRotaryScrollEnabled()
בקובץ CarUiUtils
. אם תעשו זאת, תצטרכו:
- להפוך את התצוגה שניתן לגלול בה לניתנת למיקוד, כדי שאפשר יהיה להתמקד בה כשאף אחת מהתצוגות הצאצאים שניתן להתמקד בהן לא גלויה,
- משביתים את הדגשת המיקוד שמוגדרת כברירת מחדל בתצוגה שניתן לגלול בה, על ידי קריאה ל-
setDefaultFocusHighlightEnabled(false)
כדי שהתצוגה שניתן לגלול בה לא תופיע כמיקוד. - כדי לוודא שהתצוגה שאפשר לגלול בה ממוקדת לפני הצאצאים שלה, צריך להפעיל את הפונקציה
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
. - מקשיבים ל-MotionEvents עם
SOURCE_ROTARY_ENCODER
ועםAXIS_VSCROLL
אוAXIS_HSCROLL
כדי לציין את המרחק לגלילה ואת הכיוון (דרך השלט).
כשהגלילה באמצעות תנועה סיבובית מופעלת ב-CarUiRecyclerView
והמשתמש מסובב לאזור שבו אין תצוגות שאפשר להתמקד בהן, סרגל הגלילה משתנה מאפור לכחול, כאילו כדי לציין שסרגל הגלילה ממוקד. אם תרצו, תוכלו להטמיע אפקט דומה.
אירועי MotionEvents זהים לאלה שנוצרים על ידי גלגלת גלילה בעכבר, מלבד המקור.
מצב מניפולציה ישירה
בדרך כלל, תנועות דחיפה וסיבוב מאפשרות לנווט בממשק המשתמש, בעוד שלחיצות על לחצן המרכז מאפשרות לבצע פעולות, אבל זה לא תמיד המצב. לדוגמה, אם משתמש רוצה לשנות את עוצמת ההתראה, הוא יכול להשתמש בבורר כדי לנווט לפס ההזזה של עוצמת הקול, ללחוץ על הלחצן במרכז, לסובב את הבורר כדי לשנות את עוצמת ההתראה ואז ללחוץ על הלחצן 'הקודם' כדי לחזור לניווט. המצב הזה נקרא מניפולציה ישירה (DM). במצב הזה, הלחצן הدوار משמש לאינטראקציה ישירה עם התצוגה במקום לניווט.
אפשר להטמיע את DM באחת משתי הדרכים הבאות. אם אתם צריכים לטפל רק בכיוון, והתצוגה שאתם רוצים לשנות מגיבה בצורה מתאימה ל-ACTION_SCROLL_FORWARD
ול-ACTION_SCROLL_BACKWARD
AccessibilityEvent
, השתמשו במנגנון הפשוט. אחרת, צריך להשתמש במנגנון מתקדם.
המנגנון הפשוט הוא האפשרות היחידה בחלונות מערכת. אפליקציות יכולות להשתמש בכל אחד מהמנגנונים.
מנגנון פשוט
(Android 11 QPR3, Android 11 Car, Android 12)
האפליקציה צריכה לבצע קריאה ל-DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
.
RotaryService
מזהה מתי המשתמש נמצא במצב DM, ונכנס למצב DM כשהמשתמש לוחץ על לחצן המרכז בזמן שהתצוגה ממוקדת. במצב צ'אט, הלחצן ACTION_SCROLL_FORWARD
או ACTION_SCROLL_BACKWARD
מבצע רוטציה, והמשתמש יוצא ממצב הצ'אט כשהוא לוחץ על הלחצן 'הקודם'. המנגנון הפשוט מאפשר להחליף את המצב שנבחר בתצוגה כשנכנסים למצב צ'אט פרטי ויוצאים ממנו.
כדי לספק רמז חזותי לכך שהמשתמש נמצא במצב צ'אט פרטי, כדאי לשנות את המראה של התצוגה כשהוא נבחר. לדוגמה, שינוי הרקע כש-android:state_selected
הוא true
.
מנגנון מתקדם
האפליקציה קובעת מתי RotaryService
נכנס למצב DM ומתי יוצא ממנו. כדי שחוויית השימוש תהיה עקבית, לחיצה על הלחצן האמצעי כשהתצוגה של הצ'אט האישי מוגדרת כמרכזית אמורה להעביר אתכם למצב צ'אט אישי, ולחיצה על הלחצן 'הקודם' אמורה להוציא אתכם ממצב צ'אט אישי. אם לא משתמשים בלחצן 'מרכז' ו/או בהנעה, אפשר להשתמש בהם כדרכים חלופיות לצאת ממצב צ'אט פרטי. באפליקציות כמו מפות Google, אפשר להשתמש בכפתור שמייצג הודעה ישירה כדי להיכנס למצב של הודעה ישירה.
כדי לתמוך במצב DM מתקדם, התצוגה צריכה:
- (Android 11 QPR3, Android 11 Car, Android 12) חייבים להאזין לאירוע
KEYCODE_DPAD_CENTER
כדי להיכנס למצב DM, ולהאזין לאירועKEYCODE_BACK
כדי לצאת ממצב DM, ולהפעיל אתDirectManipulationHelper.enableDirectManipulationMode()
בכל מקרה. כדי להאזין לאירועים האלה, מבצעים אחת מהפעולות הבאות:- רושמים
OnKeyListener
.
או
- מרחיבים את התצוגה ואז משנים את השיטה
dispatchKeyEvent()
שלה.
- רושמים
- צריך להאזין לאירועי נדנוד (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
אוKEYCODE_DPAD_RIGHT
) אם התצוגה אמורה לטפל בנדנודים. - צריך להאזין לאירועים של
MotionEvent
ולקבל את ספירת הסיבובים ב-AXIS_SCROLL
אם התצוגה רוצה לטפל בסיבוב. יש כמה דרכים לעשות זאת:- רושמים
OnGenericMotionListener
. - מרחיבים את התצוגה ומחליפים את השיטה
dispatchTouchEvent()
שלה.
- רושמים
- כדי לא להיתקע במצב DM, חובה לצאת ממצב DM כשהקטע או הפעילות שהתצוגה שייכת אליהם לא אינטראקטיביים.
- צריך לספק סימן חזותי כדי לציין שהתצוגה נמצאת במצב DM.
בהמשך מופיעה דוגמה לתצוגה בהתאמה אישית שמשתמשת במצב DM כדי להזיז ולשנות את מרחק התצוגה במפה:
/** Whether this view is in DM mode. */ private boolean mInDirectManipulationMode;
/** Initializes the view. Called by the constructors. */ private void init() { setOnKeyListener((view, keyCode, keyEvent) -> { boolean isActionUp = keyEvent.getAction() == KeyEvent.ACTION_UP; switch (keyCode) { // Always consume KEYCODE_DPAD_CENTER and KEYCODE_BACK events. case KeyEvent.KEYCODE_DPAD_CENTER: if (!mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = true; DirectManipulationHelper.enableDirectManipulationMode(this, true); setSelected(true); // visually indicate DM mode } return true; case KeyEvent.KEYCODE_BACK: if (mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); setSelected(false); } return true; // Consume controller nudge events only when in DM mode. // When in DM mode, nudges pan the map. case KeyEvent.KEYCODE_DPAD_UP: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, -10f); return true; case KeyEvent.KEYCODE_DPAD_DOWN: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, 10f); return true; case KeyEvent.KEYCODE_DPAD_LEFT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(-10f, 0f); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(10f, 0f); return true; // Don't consume other key events. default: return false; } });
// When in DM mode, rotation zooms the map. setOnGenericMotionListener(((view, motionEvent) -> { if (!mInDirectManipulationMode) return false; float scroll = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL); zoom(10 * scroll); return true; })); }
@Override public void onPause() { if (mInDirectManipulationMode) { // To ensure that the user doesn't get stuck in DM mode, disable DM mode // when the fragment is not interactive (e.g., a dialog shows up). mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); } super.onPause(); }
דוגמאות נוספות זמינות בפרויקט RotaryPlayground
.
ActivityView
כשמשתמשים בתצוגת פעילות:
- לא ניתן להתמקד ב-
ActivityView
. - (Android 11 QPR3, Android 11 Car, הוחלפה ב-Android 11)
התוכן שלActivityView
חייב לכלולFocusParkingView
בתור התצוגה הראשונה שניתן להתמקד בה, והמאפייןapp:shouldRestoreFocus
חייב להיותfalse
. - לתוכן של
ActivityView
לא צריכות להיות צפיות ב-android:focusByDefault
.
מבחינת המשתמש, ל-ActivityViews לא אמורה להיות השפעה על הניווט, מלבד העובדה שאזורי התמקדות לא יכולים לחול על כמה ActivityViews. במילים אחרות, אי אפשר להגדיר אזור התמקדות אחד שכולל תוכן וגם מחוץ ל-ActivityView
. אם לא מוסיפים אזור התמקדות ל-ActivityView
, שורש היררכיית התצוגה ב-ActivityView
נחשב לאזור התמקדות משתמע.
לחצנים שפועלים כשמקישים עליהם לחיצה ארוכה
רוב הלחצנים גורמים לביצוע פעולה כלשהי כשמקישים עליהם. לחצנים מסוימים פועלים רק כשמקישים עליהם לחיצה ארוכה.
לדוגמה, בדרך כלל צריך ללחוץ לחיצה ארוכה על לחצני ההתקדמות המהירה וההשהיה כדי שהם יפעלו. כדי לאפשר לחצנים כאלה לתמוך בלחצן רוטורי, צריך להאזין ל-KEYCODE_DPAD_CENTER
KeyEvents
באופן הבא:
mButton.setOnKeyListener((v, keyCode, event) -> { if (keyCode != KEYCODE_DPAD_CENTER) { return false; } if (event.getAction() == ACTION_DOWN) { mButton.setPressed(true); mHandler.post(mRunnable); } else { mButton.setPressed(false); mHandler.removeCallbacks(mRunnable); } return true; });
mRunnable
מבצע פעולה (כמו חזרה אחורה) ומתזמן את עצמו להפעלה לאחר השהיה.
מצב מגע
המשתמשים יכולים להשתמש בבורר כדי לבצע פעולות במכשיר הראשי ברכב בשתי דרכים: באמצעות הבורר או באמצעות מגע במסך. כשמשתמשים בשלט הרוטורי, אחת מהתצוגות שאפשר להתמקד בהן תודגש. כשנוגעים במסך, לא מופיעה הדגשה של האזור שבו מופיע המיקוד. המשתמש יכול לעבור בין מצבי הקלט האלה בכל שלב:
- סיבובי → מגע. כשהמשתמש נוגע במסך, ההדגשה של האזור שבו המשתמש מתמקד נעלמת.
- מקישים על ← רוטורי. כשהמשתמש מזיז, מסובב או לוחץ על לחצן המיקוד, מופיע ההדגשה של האזור שבו המשתמש מתמקד.
לחצני 'הקודם' ו'דף הבית' לא משפיעים על מצב הקלט.
Rotary מתבסס על הקונספט הקיים של Android במצב מגע.
אפשר להשתמש ב-View.isInTouchMode()
כדי לקבוע באיזה מצב קלט המשתמש משתמש. אפשר להשתמש ב-OnTouchModeChangeListener
כדי להאזין לשינויים. אפשר להשתמש באפשרות הזו כדי להתאים אישית את ממשק המשתמש למצב הקלט הנוכחי, אבל כדאי להימנע משינויים משמעותיים כי הם עלולים להטעות.
פתרון בעיות
באפליקציה שמיועדת למגע, בדרך כלל יש תצוגות מוערמות שניתן להתמקד בהן.
לדוגמה, יכול להיות ש-FrameLayout
מקיף את ImageButton
, ושאפשר להתמקד בשניהם. זה לא מזיק למגע, אבל יכול לגרום לחוויית משתמש גרועה במכשירים עם גלגלת, כי המשתמש צריך לסובב את השלט פעמיים כדי לעבור לתצוגה האינטראקטיבית הבאה. כדי לספק חוויית משתמש טובה, Google ממליצה להגדיר שאפשר להתמקד רק בתצוגה החיצונית או בתצוגה הפנימית, אבל לא בשתיהן.
אם הלחצן או המתג מאבדים את המיקוד כשמקישים עליהם באמצעות הלחצן המסתובב, יכול להיות שאחד מהמצבים הבאים רלוונטי:
- הלחצן או המתג מושבתים (לזמן קצר או ללא הגבלת זמן) בגלל לחיצה על הלחצן. בכל מקרה, יש שתי דרכים לטפל בבעיה:
- משאירים את המצב
android:enabled
כ-true
ומשתמשים במצב בהתאמה אישית כדי להפוך את הלחצן או המתג לאפור, כפי שמתואר בקטע מצב בהתאמה אישית. - משתמשים בקונטיינר כדי להקיף את הלחצן או המתג, ומגדירים את הקונטיינר כרכיב שאפשר להתמקד בו במקום הלחצן או המתג. (הקוד למעקב אחר קליקים חייב להיות בקונטיינר).
- משאירים את המצב
- הלחצן או המתג מוחלפים. לדוגמה, הפעולה שמתבצעת כשלוחצים על הלחצן או מעבירים את המתג למצב אחר עשויה להפעיל רענון של הפעולות הזמינות, וכתוצאה מכך לחצנים חדשים יחליפו לחצנים קיימים. יש שתי דרכים לטפל בבעיה הזו:
- במקום ליצור לחצן או מתג חדשים, מגדירים את הסמל ו/או הטקסט של הלחצן או המתג הקיימים.
- כמו למעלה, מוסיפים מאגר שאפשר להתמקד בו סביב הלחצן או המתג.
RotaryPlayground
RotaryPlayground
היא אפליקציית עזר ל-rotary. תוכלו להיעזר בו כדי ללמוד איך לשלב תכונות של תנועה סיבובית באפליקציות שלכם. RotaryPlayground
נכלל בגרסאות build של מכונות וירטואליות ובגרסאות build למכשירים עם Android Automotive OS (AAOS).
- מאגר
RotaryPlayground
:packages/apps/Car/tests/RotaryPlayground/
- גרסאות: Android 11 QPR3, Android 11 Car ו-Android 12
באפליקציה RotaryPlayground
מופיעות הכרטיסיות הבאות בצד ימין:
- כרטיסים כדאי לבדוק את הניווט באזורי המיקוד, דילוג על רכיבים שלא ניתן להתמקד בהם והזנת טקסט.
- מניפולציה ישירה בדיקת ווידג'טים שתומכים במצב פשוט ומתקדם של מניפולציה ישירה. הכרטיסייה הזו מיועדת במיוחד לפעולות ישירות בתוך חלון האפליקציה.
- מניפולציה בממשק המשתמש של המערכת בדיקת ווידג'טים שתומכים בפעולות ישירות בחלונות מערכת שבהם יש תמיכה רק במצב פשוט של פעולות ישירות.
- רשת. בדיקת ניווט סיבובי בתנועת Z עם גלילה.
- הודעה אפשר לבדוק את האפשרות להצגת התראות 'שימו לב' או להסתיר אותן.
- גלילה. כדאי לבדוק את הגלילה דרך שילוב של תוכן שאפשר להתמקד בו ותוכן שלא ניתן להתמקד בו.
- WebView בודקים את הניווט דרך קישורים ב-
WebView
. - התאמה אישית
FocusArea
. בדיקת ההתאמה האישית שלFocusArea
:- גול בהקפת שער.
android:focusedByDefault
וגםapp:defaultFocus
.
- יעדים של דחיפה מפורשת.
- קיצורי דרך להנעה.
FocusArea
ללא תצוגות שניתן להתמקד בהן.