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

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

כדי להפוך את התמיכה באפליקציה שלך לסיבובית, עליך:

  1. הצב FocusParkingView בפריסת הפעילות המתאימה.
  2. ודא שהתצוגות ניתנות (או שאינן) ניתנות למיקוד.
  3. השתמש FocusArea s כדי לעטוף את כל התצוגות הניתנות למיקוד, מלבד 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 (AAOS), במקרים מסוימים, הדבר מונע את הופעת המקלדת על המסך.
  • השתמש ב- userdebug או eng build.
  • אפשר סינון אירועי מפתח:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • עיין בטבלה למטה כדי למצוא את המפתח המתאים לכל פעולה:
    מַפְתֵחַ פעולה רוטרית
    ש סובב נגד כיוון השעון
    ה סובב עם כיוון השעון
    א דחף שמאלה
    ד דחף ימינה
    W דחף למעלה
    ס דחף למטה
    F או פסיק כפתור מרכז
    R או Esc כפתור חזרה

פקודות ADB

אתה יכול להשתמש בפקודות car_service כדי להחדיר אירועי קלט סיבוביים. ניתן להפעיל פקודות אלו במכשירים המריצים את מערכת ההפעלה Android Automotive (AAOS) או באמולטור.

פקודות שירות_מכונית קלט סיבובי
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 היא תצוגה שקופה בספריית ממשק המשתמש של הרכב (ספריית רכב-UI) . RotaryService משתמש בו כדי לתמוך בניווט בקר סיבובי. FocusParkingView חייבת להיות התצוגה הניתנת למיקוד הראשונה בפריסה. זה חייב להיות ממוקם מחוץ לכל FocusArea s. לכל חלון חייב להיות FocusParkingView אחד. אם אתה כבר משתמש בפריסת הבסיס של ספריית רכב-UI, המכילה 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. אנדרואיד לא מנקה את המיקוד באופן אוטומטי כאשר המיקוד מוגדר בחלון אחר. אם תנסה לנקות את המיקוד בחלון הקודם, אנדרואיד ממקד מחדש תצוגה בחלון זה, מה שגורם להתמקדות בשני חלונות בו זמנית. הוספת FocusParkingView לכל חלון יכולה לפתור בעיה זו. תצוגה זו שקופה והדגשת המיקוד המוגדרת כברירת מחדל מושבתת, כך שהיא בלתי נראית למשתמש, לא משנה אם ממוקד או לא. זה יכול לקחת פוקוס כך ש- RotaryService יוכל להחנות את הפוקוס עליו כדי להסיר את הדגשת המיקוד.
  2. אם יש רק FocusArea אחד בחלון הנוכחי, סיבוב הבקר ב- FocusArea גורם RotaryService להעביר את הפוקוס מהתצוגה מימין לתצוגה משמאל (ולהיפך). הוספת תצוגה זו לכל חלון יכולה לפתור את הבעיה. כאשר RotaryService קובע שמיקוד המיקוד הוא FocusParkingView , הוא יכול לקבוע שעומד להתרחש עיטוף בנקודה שבה הוא נמנע מהגלישה על ידי אי הזזת הפוקוס.
  3. כאשר הבקרה הסיבובית מפעילה אפליקציה, אנדרואיד ממקדת את התצוגה הניתנת למיקוד הראשונה, שהיא תמיד ה- FocusParkingView . ה- FocusParkingView קובע את התצוגה האופטימלית להתמקד ולאחר מכן מחיל מיקוד.

תצוגות ניתנות למיקוד

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

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

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

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

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

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

  1. כדי להוסיף תכונה מותאמת אישית לתצוגה שלך. לדוגמה, כדי להוסיף מצב מותאם אישית state_rotary_enabled למחלקת התצוגה CustomView , השתמש ב:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. כדי לעקוב אחר מצב זה, הוסף משתנה מופע לתצוגה שלך יחד עם שיטות עזר:
    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. הפוך את מטפל הקליקים של התצוגה לביצועים שונים בהתאם למצבו. לדוגמה, המטפל בלחיצה עשוי לא לעשות דבר או שהוא עשוי להופיע כוסית כאשר 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 בספריית המכוניות-UI. כאשר תכונה זו מופעלת, 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 בפריסה שלך, תצוגת השורש תטופל כאזור מיקוד מרומז. המשתמש לא יכול לזרז לנווט באפליקציה. במקום זאת, הם יסתובבו בין כל התצוגות הניתנות למיקוד, מה שעשוי להתאים לדיאלוגים.

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

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

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

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

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

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

צבע רקע מוצק
  • ( Android 11 QPR3, Android 11 Car, Android 12 )
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • ( אנדרואיד 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 )
אתה יכול לאפשר גלילה סיבובית בכל תצוגה ניתנת לגלילה, כולל av CarUiRecyclerView , עם שיטת 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 כאשר המשתמש לוחץ על כפתור המרכז בזמן שהתצוגה ממוקדת. במצב DM, סיבובים מבצעים ACTION_SCROLL_FORWARD או ACTION_SCROLL_BACKWARD ויוצאים ממצב DM כאשר המשתמש לוחץ על הלחצן 'הקודם'. המנגנון הפשוט מחליף את המצב הנבחר של התצוגה בעת כניסה ויציאה ממצב DM.

כדי לספק רמז חזותי לכך שהמשתמש נמצא במצב DM, הפוך את התצוגה שלך להראות שונה כאשר נבחר. לדוגמה, שנה את הרקע כאשר android:state_selected הוא true .

מנגנון מתקדם

האפליקציה קובעת מתי RotaryService נכנס ויוצא ממצב DM. לחוויית משתמש עקבית, לחיצה על הלחצן המרכזי עם תצוגת DM ממוקדת אמורה להיכנס למצב DM והלחצן Back אמור לצאת ממצב DM. אם הלחצן המרכזי ו/או הדחיפה אינם בשימוש, הם יכולים להיות דרכים חלופיות ליציאה ממצב DM. עבור אפליקציות כגון מפות, ניתן להשתמש בלחצן המייצג DM כדי להיכנס למצב DM.

כדי לתמוך במצב DM מתקדם, תצוגה:

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

  • ה- ActivityView לא אמור להיות ניתן למיקוד.
  • ( Android 11 QPR3, Android 11 Car, הוצא משימוש ב-Android 11 )
    התוכן של ActivityView חייב להכיל FocusParkingView בתור התצוגה הראשונה שניתן להתמקד, והתכונה app:shouldRestoreFocus שלה חייבת להיות false .
  • לתוכן של ActivityView לא צריך להיות תצוגות android:focusByDefault .

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

לחצנים שפועלים כשמחזיקים אותם לחוץ

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

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 נוקט פעולה (כגון החזרה לאחור) ומתזמן את עצמו להפעלה לאחר עיכוב.

מצב מגע

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

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

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

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

פתרון תקלות

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

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

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

RotaryPlayground

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

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

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

  • קלפים. בדוק ניווט סביב אזורי מיקוד, דילוג על אלמנטים בלתי ניתנים למיקוד וקלט טקסט.
  • מניפולציה ישירה. בדוק ווידג'טים התומכים במצב מניפולציה ישירה פשוטה ומתקדמת. כרטיסייה זו מיועדת במיוחד למניפולציה ישירה בתוך חלון האפליקציה.
  • מניפולציה של Sys UI. בדוק ווידג'טים התומכים במניפולציה ישירה בחלונות מערכת שבהם נתמך רק במצב מניפולציה ישירה פשוטה.
  • רֶשֶׁת. בדוק ניווט סיבובי בתבנית z עם גלילה.
  • הוֹדָעָה. בדוק דחיפה פנימה והחוצה של התראות ראש-אפ.
  • גְלִילָה. בדוק גלילה דרך שילוב של תוכן שניתן למיקוד ולא ניתן למיקוד.
  • WebView. בדוק ניווט דרך קישורים ב- WebView .
  • FocusArea מותאם אישית. בדוק התאמה אישית FocusArea :
    • לעטוף.
    • android:focusedByDefault ו- app:defaultFocus
    • .
    • יעדי דחיפה מפורשים.
    • דחף קיצורי דרך.
    • FocusArea ללא תצוגות ניתנות למיקוד.