פיתוח אפליקציות

החומר הבא מיועד למפתחי אפליקציות.

כדי שהאפליקציה תתמוך בחוגה, צריך:

  1. צריך להציב FocusParkingView בפריסת הפעילות המתאימה.
  2. ודאו שהתצוגות שניתן להתמקד בהן (או שלא ניתן להתמקד בהן).
  3. אפשר להשתמש ב-FocusArea כדי להקיף את כל התצוגות שניתן להתמקד בהן, חוץ מאשר FocusParkingView.

כל אחת מהמשימות האלה מפורטת בהמשך, אחרי שמגדירים את הסביבה לפתח אפליקציות שתומכות בחוגה.

הגדרה של חוגה

כדי להתחיל לפתח אפליקציות שתומכות בחוגה, צריך שלט רחוק או סטנד-אפ. עומדות לרשותכם האפשרויות שמתוארות בהמשך.

מכשיר הדמיה

source build/envsetup.sh && lunch car_x86_64-userdebug
m -j
emulator -wipe-data -no-snapshot -writable-system

אפשר גם להשתמש ב-aosp_car_x86_64-userdebug.

כדי לגשת לאמולציה של בקר החוגה:

  1. לוחצים על סמל שלוש הנקודות שמוצג בתחתית סרגל הכלים:

    גישה לבקר החוגה
    איור 1. גישה לאמולציה של בקר חוגה
  2. בוחרים באפשרות חוגה של מכונית בחלון הפקדים המורחבים:

    בחירה של תצוגת חוגה של מכונית
    איור 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 אם אתם כבר משתמשים בפריסה הבסיסית של הספרייה ברכב, שמכיל 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:

  1. מערכת Android לא מסירה את המיקוד באופן אוטומטי כשהמיקוד מוגדר בחלון אחר. אם לנסות לנקות את המיקוד בחלון הקודם, Android למקד מחדש תצוגה בחלון זה, התוצאה היא שני חלונות להתמקד בהם בו-זמנית. הוספה של FocusParkingView כל חלון יכול לפתור את הבעיה. התצוגה הזו שקופה והדגשת ברירת המחדל של התצוגה מושבת, כך שהמשתמש לא יכול לראות אותו, גם אם הוא ממוקד וגם אם לא. אפשר להעביר את המיקוד לRotaryService כדי להציב בו את המיקוד כדי להסיר את ההדגשה.
  2. אם יש רק FocusArea אחד בחלון הנוכחי, מתבצע סיבוב של השלט רחוק בFocusArea גורם ל-RotaryService להזיז את המיקוד מהתצוגה מימין לתצוגה שמשמאל (ולהפך). הוספת התצוגה הזו כל חלון יכול לפתור את הבעיה. מתי המיקוד נקבע על ידי RotaryService היעד הוא FocusParkingView, הוא יכול לקבוע שהעקיפה עומדת על ובשלב הזה הוא מונע את הסיבוב על ידי אי הזזת המיקוד.
  3. כאשר החוגה לחוגה מפעילה אפליקציה, Android מתמקדת בתצוגה הראשונה שניתנת למיקוד. שהוא תמיד FocusParkingView. FocusParkingView קובע את התצוגה האופטימלית שבה צריך להתמקד ואז מפעיל את המיקוד.

תצוגות שניתן להתמקד בהן

RotaryService מתבסס על קיים התמקדות בתצוגה, עוד מהימים שבהם היו לטלפונים מקלדות פיזיות ומחיצות D. המאפיין android:nextFocusForward הקיים משמש מחדש לחוגה (ראו התאמה אישית של אזור המיקוד), אבל android:nextFocusLeft, android:nextFocusRight, android:nextFocusUp ו-android:nextFocusDown לא.

RotaryService מתמקד רק בתצוגות שניתן להתמקד בהן. חלק מהצפיות כמו Button, בדרך כלל ניתן להתמקד בהם. סוגים אחרים, כמו TextView וViewGroup, בדרך כלל לא. תצוגות שניתנות ללחיצה ניתנות למיקוד באופן אוטומטי והצפיות קליקביליות כשיש להם האזנה לקליקים. אם הלוגיקה האוטומטית הזאת אתם לא צריכים להגדיר בצורה מפורשת את יכולת המיקוד של התצוגה. אם הלוגיקה האוטומטית לא כדי להשיג את רמת המיקוד הרצויה, צריך להגדיר את המאפיין android:focusable true או false, או להגדיר באופן פרוגרמטי את יכולת המיקוד של התצוגה באמצעות View.setFocusable(boolean) כדי להתמקד ב-RotaryService, התצוגה חייבת לעמוד בדרישות הבאות:

  • ניתן למיקוד
  • מופעל
  • גלוי
  • לכלול ערכים של רוחב וגובה שאינם אפס.

אם תצוגה מסוימת לא עומדת בכל הדרישות האלה, למשל לחצן שניתן להתמקד בו אבל מושבת, המשתמש לא יכול להשתמש בחוגה כדי להתמקד בו. אם אתם רוצים להתמקד בתצוגות מושבתות, כדאי להשתמש במצב מותאם אישית במקום ב-android:state_enabled כדי לקבוע איך התצוגה מופיעה בלי לציין ש-Android צריך להגדיר אותה כמושבת. האפליקציה יכולה לספק מידע למשתמש למה התצוגה מושבתת כשמקישים עליה. בקטע הבא מוסבר איך לעשות זאת.

מצב מותאם אישית

כדי להוסיף מצב מותאם אישית:

  1. כדי להוסיף מאפיין מותאם אישית לתצוגה שלך. לדוגמה, כדי להוסיף מצב מותאם אישית של state_rotary_enabled למצב שיעור צפייה אחד (CustomView), יש להשתמש ב:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. כדי לעקוב אחרי המצב הזה, מוסיפים לתצוגה משתנה של מופע בשילוב עם methods של Accessor:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. כדי לקרוא את ערך המאפיין בזמן יצירת התצוגה המפורטת:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. בכיתה של התצוגה המפורטת, משנים את השיטה 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;
    }
    
  5. שינוי הביצועים של מטפל הקליקים בתצוגה המפורטת בהתאם למצב שבו הוא נמצא. לדוגמה, יכול להיות שה-handler של הקליקים לא יבצע כל פעולה או שהוא יקפוץ חלון קופץ mRotaryEnabled היא false.
  6. כדי שהלחצן יופיע במצב מושבת, ניתן לצייר ברקע של התצוגה app:state_rotary_enabled במקום android:state_enabled. אם עדיין לא עשיתם זאת, תצטרכו להוסיף:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. אם התצוגה שלך מושבתת בפריסות כלשהן, מחליפים את android:enabled="false" ב: app:state_rotary_enabled="false", ואז מוסיפים את מרחב השמות app, כפי שמתואר למעלה.
  8. אם התצוגה שלך מושבתת באופן פרוגרמטי, צריך להחליף את הקריאות אל setEnabled() עם שיחות אל setRotaryEnabled().

אזור מיקוד

שימוש ב-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 פועל באופן הבא:

  1. במהלך טיפול בפעולות של סיבוב ותזכורת, RotaryService מחפש מופעים של FocusArea בהיררכיית התצוגות.
  2. בעת קבלת אירוע סבב, RotaryService מעביר את המיקוד למקום אחר תצוגה שניתן להתמקד בה באותו FocusArea.
  3. כשמקבלים אירוע התראה, RotaryService מעביר את המיקוד לתצוגה אחרת שיכול להתמקד בFocusArea אחר (בדרך כלל בסמוך).

אם לא כוללים FocusAreas בפריסה, התצוגה הבסיסית (root) תטופל כתחום התמקדות מרומז. המשתמש לא יכול להזיז את המשתמש לנווט באפליקציה. במקום זאת, להסתובב בין כל התצוגות הניתנות למיקוד, שעשויות להתאים לתיבות דו-שיח.

התאמה אישית של FocusArea

אפשר להשתמש בשני מאפיינים רגילים של תצוגה כדי להתאים אישית את החוגה של הניווט:

  • android:nextFocusForward מאפשר למפתחי אפליקציות לציין את הסבב בסדר יורד. זה אותו מאפיין שמשמש לשליטה בסדר הכרטיסיות של ניווט באמצעות המקלדת. אין להשתמש במאפיין הזה כדי ליצור לולאה. במקום זאת, משתמשים ב-app:wrapAround (ראו בהמשך) כדי ליצור לולאה.
  • android:focusedByDefault מאפשר למפתחי אפליקציות לציין תצוגת ברירת המחדל בחלון. לא להשתמש במאפיין הזה. app:defaultFocus (ראו בהמשך) באותו FocusArea.

FocusArea גם מגדיר כמה מאפיינים להתאמה אישית של החוגה לניווט. לא ניתן להתאים אישית אזורים מרומזים של התמקדות באמצעות המאפיינים האלה.

  1. (Android 11 QPR3, Android 11 Car, Android 12)
    אפשר להשתמש ב-app:defaultFocus כדי לציין את המזהה של תצוגת צאצא שניתן להתמקד בה, שבה צריך להתמקד כשהמשתמש נדנודים אל FocusArea הזה.
  2. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocusOverridesHistory אפשר להגדיר ל-true כדי שהתצוגה שצוינה למעלה תתמקד גם אם כדי לציין שצפייה אחרת בFocusArea הזו התמקדה.
  3. (Android 12)
    שימוש ב-app:nudgeLeftShortcut, app:nudgeRightShortcut, app:nudgeUpShortcut, app:nudgeDownShortcut כדי לציין את המזהה של תצוגת צאצא שניתן להתמקד בה, שבו יש להתמקד כאשר שהמשתמש נדנוד בכיוון מסוים. למידע נוסף, אפשר לצפות בתוכן של נדנודים בהמשך.

    (Android 11 QPR3, Android 11 Car, הוצא משימוש ב-Android 12) app:nudgeShortcut ו-app:nudgeShortcutDirection תומכים רק במקש קיצור אחד של נדנוד.

  4. (Android 11 QPR3, Android 11 Car, Android 12)
    כדי להפעיל את הסיבוב כדי לנוע בתוך FocusArea, app:wrapAround יכול להיות true. בדרך כלל משתמשים באפשרות הזו כשהתצוגות מסודרות לפי מעגל או אליפסה.
  5. (Android 11 QPR3, Android 11 Car, Android 12)
    כדי לכוון את המרווח הפנימי של ההדגשה FocusArea הזה, יש להשתמש ב-app:highlightPaddingStart, app:highlightPaddingEnd, app:highlightPaddingTop, app:highlightPaddingBottom, app:highlightPaddingHorizontal, ו-app:highlightPaddingVertical.
  6. (Android 11 QPR3, Android 11 Car, Android 12)
    כדי לשנות את הגבולות הנתפסים של FocusArea כדי למצוא יעד נדנוד, להשתמש ב-app:startBoundOffset, app:endBoundOffset, app:topBoundOffset, app:bottomBoundOffset, app:horizontalBoundOffset ו-app:verticalBoundOffset.
  7. (Android 11 QPR3, Android 11 Car, Android 12)
    כדי לציין במפורש את המזהה של FocusArea (או אזורים) סמוכים בכיוון הנתון, השתמשו app:nudgeLeft, app:nudgeRight, app:nudgeUp וגם app:nudgeDown. יש להשתמש באפשרות הזו כאשר החיפוש הגאומטרי המשמש כברירת מחדל לא מוצא את היעד הרצוי.

נדנוד בדרך כלל מנווט בין FocusAreas. אבל עם מקשי קיצור של נדנודים, אם נדנודים, לפעמים הם מנווטים תחילה בתוך FocusArea כך שהמשתמש עשוי להזדקק כדי לדחות פעמיים כדי לנווט אל FocusArea הבא. מקשי קיצור יכולים להיות שימושיים כשFocusArea מכיל רשימה ארוכה ואחריה לחצן פעולה צף, כמו בדוגמה הבאה:

הסטת קיצור דרך
איור 3. הסטת מקש קיצור

בלי קיצור הדרך של הנדנוד, המשתמשים יצטרכו לסובב את כל הרשימה כדי להגיע FAB.

מיקוד ההתאמה האישית של ההדגשה

כפי שצוין למעלה, RotaryService מתבסס על התפיסה הקיימת של Android Framework להתמקד בתצוגה. כשהמשתמש מסובב ונדנוד את המשתמש, RotaryService מזיז את המיקוד. להתמקד בתצוגה אחת ולבטל התמקדות בתצוגה אחרת. ב-Android, כשמתמקדים בתצוגה, אם היא:

  • מערכת Android מדגישה את הפוקוס של התצוגה עצמה, אבל היא מדגישה את התצוגה עצמה.
  • בלי הדגשה של המיקוד, והדגשת ברירת המחדל של ההדגשה לא מושבתת. מערכת Android מצייר את הדגשת ברירת המחדל של המיקוד בתצוגה.

באפליקציות שמיועדות למגע בדרך כלל לא צוינו נקודות המיקוד המתאימות.

ההדגשה הזו של ברירת המחדל מסופקת על ידי מסגרת Android ואפשר לשנות אותה על ידי ה-OEM. מפתחי אפליקציות מקבלים אותו כשהעיצוב שהם משתמשים בו נגזר Theme.DeviceDefault

כדי ליהנות מחוויית משתמש עקבית, כדאי להשתמש ככל האפשר בהדגשת ברירת המחדל של המיקוד. אם אתם צריכים הדגשה של צורה מותאמת אישית (למשל, עגולה או בצורת גלולה), או אם באמצעות עיצוב שלא נגזר מ-Theme.DeviceDefault, השתמשו ב-car-ui-library משאבים שיעזרו לכם לציין את הדגשת המיקוד שלכם בכל תצוגה.

כדי לציין הדגשת מוקד מותאם אישית בתצוגה, צריך לשנות את הרקע או את העיצוב הקדמי. של התצוגה למכשיר שניתן להזזה, ששונה כאשר מתמקדים בו. בדרך כלל משנים את את הרקע. פריט הגרפיקה הבא, אם משמש כרקע לתצוגה ריבועית, יוצרת הדגשה עגולה של המיקוד:

<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) הפניות למשאבים מודגשות בדוגמה לזהות משאבים שמוגדרים על ידי ספריית המכונית לקרוא. ה-OEM (יצרן הציוד המקורי) מבטל את ההגדרות האלה כדי לשמור על עקביות באמצעות הדגשת המיקוד שהם מציינים כברירת מחדל. זה מבטיח שצבע ההדגשה של המוקד רוחב הקו, וכן הלאה לא ישתנו כשהמשתמשים עוברים בין תצוגה עם מיקוד מותאם אישית הדגשה ותצוגה עם הדגשת ברירת המחדל של המיקוד. הפריט האחרון הוא גלים שמשמשים למגע. ערכי ברירת המחדל המשמשים למשאבים המודגשים מופיעים כך:

ערכי ברירת מחדל למשאבים מודגשים
איור 4. ערכי ברירת מחדל למשאבים מודגשים

כמו כן, מתבצעת קריאה להדגשה של מוקד מותאם אישית כאשר על הלחצן מוצג הדגשה קבועה כדי למשוך את תשומת הלב של המשתמש, כמו בדוגמה הבאה. הפעולה הזו יכולה להפוך את מודגשת המיקוד שקשה לראות. במצב כזה, תוכלו לציין הדגשת מיקוד מותאם אישית באמצעות צבעים משניים:

צבע רקע רציף
  • (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 במקום זאת. הדבר מבטיח שממשק המשתמש שלך תואם ל- אחרים, כי ההתאמה האישית של ה-OEM חלה על כל ה-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). כאן בקר החוגה משמש לאינטראקציה ישירה עם התצוגה במקום לניווט.

אפשר להטמיע צ'אט באחת משתי דרכים. אם צריך רק לטפל בסיבוב ובתצוגה הרצויה כדי להשפיע על תשובות לACTION_SCROLL_FORWARD ACTION_SCROLL_BACKWARD AccessibilityEvent כמו שצריך, יש להשתמש ב מנגנון פשוט. אחרת, יש להשתמש במנגנון המתקדם.

המנגנון הפשוט הוא האפשרות היחידה בחלונות המערכת. האפליקציות יכולות להשתמש בכל אחד מהמנגנונים.

מנגנון פשוט

(Android 11 QPR3, Android 11 Car, Android 12)
האפליקציה צריכה להתקשר DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) התכונה RotaryService מזהה כשהמשתמש נמצא במצב צ'אט ועובר למצב צ'אט כשהמשתמש לוחץ על לחצן המרכז בזמן שהתצוגה מתמקדת. במצב צ'אט, הסיבובים מבצעים ACTION_SCROLL_FORWARD או ACTION_SCROLL_BACKWARD ויציאה ממצב צ'אט כשהמשתמש לוחץ על לחצן 'הקודם'. המנגנון הפשוט מחליף את המצב שנבחר התצוגה בזמן הכניסה למצב צ'אט וביציאה ממנו.

כדי לסמן סימן ויזואלי שהמשתמש נמצא במצב צ'אט, אתם צריכים לשנות את התצוגה כשנבחר. לדוגמה, אתם יכולים לשנות את הרקע android:state_selected היא true.

מנגנון מתקדם

האפליקציה קובעת מתי RotaryService נכנס למצב צ'אט ואיך יוצא ממנו. כדי לשמור על עקביות חוויית משתמש, לחיצה על הלחצן המרכזי כשהתצוגה של הצ'אט מתמקדת אמורה להיכנס למצב צ'אט. ולחצן 'הקודם' אמור לצאת ממצב צ'אט. אם לא משתמשים בלחצן המרכזי או בנדנוד, הן יכולות להיות דרכים חלופיות ליציאה ממצב צ'אט. לאפליקציות כמו מפות Google, לחצן לייצוג אפשר להשתמש בצ'אטים כדי להיכנס למצב צ'אט.

כדי לתמוך במצב צ'אט מתקדם:

  1. (Android 11 QPR3, Android 11 Car, Android 12) חייבים להאזין ל-KEYCODE_DPAD_CENTER האירוע כדי להיכנס למצב צ'אט והאזנה לאירוע KEYCODE_BACK כדי לצאת ממצב צ'אט, קוראים לפונקציה DirectManipulationHelper.enableDirectManipulationMode() בכל אחד מהמקרים. כדי להאזין לאירועים האלה, מבצעים אחת מהפעולות הבאות:
    • רישום OnKeyListener.
    • או,
    • הרחבת התצוגה וביטול ה-method dispatchKeyEvent() של התצוגה.
  2. צריכה להאזין לאירועי נדנודים (KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT או KEYCODE_DPAD_RIGHT) אם התצוגה צריכה לטפל בנדנודים.
  3. צריכה להאזין ל-MotionEvent ולקבל את מספר הסיבובים ב-AXIS_SCROLL אם התצוגה רוצה לטפל בסבב. יש כמה דרכים לעשות זאת:
    1. רישום OnGenericMotionListener.
    2. הרחבת התצוגה וביטול ה-method dispatchTouchEvent() שלה.
  4. כדי שלא תיתקע במצב צ'אט, חובה לצאת ממצב DM כשהתצוגה 'מקטע' או 'פעילות' מופיעה. שייך ל- אינו אינטראקטיבי.
  5. אמור לספק סימן חזותי שמציין שהתצוגה היא במצב צ'אט.

למטה מוצגת דוגמה של תצוגה מותאמת אישית שמשתמשת במצב צ'אט כדי להזיז ולשנות את מרחק התצוגה של המפה:

/** 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 צפיות.

עבור המשתמש, ל'תצוגות פעילות' לא תהיה כל השפעה על הניווט מלבד המיקוד הזה אזורים לא יכולים להתפרס על 'תצוגות פעילות'. במילים אחרות, אין אפשרות להתמקד בתחום אחד יש תוכן בתוך וגם מחוצה לו ActivityView. אם לא תוסיפו כל אזור FocusAres ב-ActivityView, שהוא הרמה הבסיסית (root) של היררכיית התצוגות המפורטות ב- 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 מבצע פעולה (כמו הרצה אחורה) ומתזמן את עצמו יופעלו לאחר עיכוב.

מצב מגע

המשתמשים יכולים להשתמש בבקר סיבובי כדי לקיים אינטראקציה עם היחידה הראשית (HU) ברכב בשתי דרכים: באמצעות השלט הרחוק של החוגה או על ידי נגיעה במסך. כשמשתמשים בבקר החוגה, אחת מהתצוגות הניתנות למיקוד מודגשת. כשנוגעים במסך, אין הדגשה של המיקוד מופיעה. המשתמשים יכולים לעבור בין שיטות הקלט האלה בכל שלב:

  • סיבובי ← מגע. כשהמשתמש נוגע במסך, הדגשת המיקוד נעלמת.
  • מקישים על ← חוגה. כשהמשתמש נדנד, מסובב או לוחץ על הלחצן 'מרכז', הדגשת המיקוד תופיע.

הלחצנים 'הקודם' ו'דף הבית' לא משפיעים על מצב הקלט.

תגים 'רוכבים' (piggybacking) מסתובבים על הקונספט הקיים של Android מצב מגע. אפשר להשתמש View.isInTouchMode() כדי לקבוע באיזה מצב קלט המשתמש משתמש. אפשר להשתמש OnTouchModeChangeListener כדי לזהות שינויים. אפשר להשתמש באפשרות הזו כדי להתאים אישית את ממשק המשתמש עבור במצב קלט, להימנע משינויים משמעותיים מכיוון שהם עלולים להיות מטרידים.

פתרון בעיות

באפליקציה שמיועדת למגע, פעמים רבות יש תצוגות מקוננות שניתן להתמקד בהן. לדוגמה, יכול להיות שיש FrameLayout מסביב ל-ImageButton, ובשניהם ניתן להתמקד. פעולה זו לא פוגעת במגע, אבל יכולה לגרום בחוויית המשתמש בחוגה כי המשתמש צריך לסובב את השלט רחוק פעמיים כדי לעבור בתצוגה האינטראקטיבית הבאה. כדי לספק חוויית משתמש טובה, Google ממליצה את התצוגה החיצונית או את התצוגה הפנימית, אבל לא את שתיהן.

אם לחצן או מתג מאבד את המיקוד כשלוחצים דרך החוגה דרך השלט הרחוק, אחת מהאפשרויות התנאים הבאים עשויים להתקיים:

  • הלחצן או המתג מושבתים (לזמן קצר או ללא הגבלת זמן) עקב שלוחצים על הלחצן. בכל מקרה, יש שתי דרכים לטפל בעניין:
    • יש להשאיר את המצב android:enabled בתור true ולהשתמש בהתאמה אישית כדי להציג את הלחצן או לעבור למצב אפור, כפי שמתואר מצב מותאם אישית.
    • צריך להשתמש במאגר כדי להקיף את הלחצן או את המתג ולהפוך את המאגר לניתן למיקוד במקום ללחוץ על הלחצן או על המתג. (ה-click listener חייב להיות בקונטיינר).
  • הלחצן או המתג מחליפים. לדוגמה, הפעולה שבוצעה כשהלחצן נלחץ או שהמתג במצב מופעל, יכול להפעיל רענון של הפעולות הזמינות ולגרום ללחצנים חדשים להחליף לחצנים קיימים. יש שתי דרכים לפתור את הבעיה:
    • במקום ליצור לחצן או מתג חדש, הגדירו את הסמל או הטקסט של לחצן או מתג קיים.
    • כפי שצוין למעלה, אפשר להוסיף מאגר שניתן להתמקד בו מסביב ללחצן או למתג הנגישות.

RotaryPlayground

RotaryPlayground היא אפליקציית עזר לחוגה. בעזרת הכלי הזה אפשר ללמוד איך לשלב בעזרת חוגה לאפליקציות. RotaryPlayground כלול בגרסאות build של אמולטור וב- מותאמות למכשירים עם מערכת ההפעלה Android Automotive OS (AAOS).

  • מאגר של RotaryPlayground: packages/apps/Car/tests/RotaryPlayground/
  • גרסאות: Android 11 QPR3, Android 11 Car, ו-Android 12

הכרטיסיות הבאות מוצגות באפליקציה RotaryPlayground בצד ימין:

  • כרטיסים. אפשר לנסות לנווט באזורי המיקוד ולדלג על רכיבים לא ממוקדים. וקלט טקסט.
  • מניפולציה ישירה. בדיקת ווידג'טים שתומכים בתהליך פשוט ומתקדם במצב של מניפולציה ישירה. הכרטיסייה הזו מיועדת במיוחד למניפולציה ישירה בתוך חלון האפליקציה.
  • מניפולציה של ממשק המשתמש של Sys. בדיקת ווידג'טים שתומכים במניפולציה ישירה בחלונות מערכת שבהם יש תמיכה רק במצב של מניפולציה ישירה פשוטה.
  • רשת. ניתן לבדוק את הניווט החוגה של תבנית z באמצעות גלילה.
  • התראה. אפשר לנסות נדנודים כדי להציג התראות 'שימו לב' ולצאת מהן.
  • גוללים. מומלץ לבדוק את הגלילה בשילוב של רכיבים שניתן להתמקד בהם ושלא ניתן להתמקד בהם תוכן.
  • WebView. מומלץ לבדוק את הניווט באמצעות קישורים ב-WebView.
  • בהתאמה אישית FocusArea. בדיקת ההתאמה האישית של FocusArea:
    • סיכום.
    • android:focusedByDefault וגם app:defaultFocus
    • .
    • טירגוטים מפורשים של נדנודים.
    • הסטה לקיצורי דרך.
    • FocusArea ללא תצוגות שניתן להתמקד בהן.