تطوير التطبيقات

المواد التالية مخصصة لمطوري التطبيقات.

لجعل دعم تطبيقك دوارًا، يجب عليك:

  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 OS (AAOS)، وفي بعض الحالات، يمنع هذا ظهور لوحة المفاتيح على الشاشة.
  • استخدم userdebug أو eng build.
  • تمكين تصفية الأحداث الرئيسية:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • انظر الجدول أدناه للعثور على المفتاح المقابل لكل إجراء:
    مفتاح عمل دوار
    س تدوير عكس اتجاه عقارب الساعة
    ه تدوير في اتجاه عقارب الساعة
    أ دفع اليسار
    د ادفع لليمين
    دبليو ادفع للأعلى
    س ادفع للأسفل
    F أو فاصلة زر الوسط
    ص أو خروج زر العودة

أوامر بنك التنمية الآسيوي

يمكنك استخدام أوامر car_service لإدخال أحداث الإدخال الدوارة. يمكن تشغيل هذه الأوامر على الأجهزة التي تعمل بنظام التشغيل Android Automotive OS (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 هو عرض شفاف في مكتبة واجهة مستخدم السيارة (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 على المفهوم الحالي لإطار عمل Android الخاص بتركيز العرض، والذي يعود تاريخه إلى الوقت الذي كانت فيه الهواتف تحتوي على لوحات مفاتيح فعلية ولوحات D. تمت إعادة استخدام السمة android:nextFocusForward الحالية للاستخدام الدوار (راجع تخصيص FocusArea )، لكن android:nextFocusLeft و android:nextFocusRight و android:nextFocusUp و android:nextFocusDown ليست كذلك.

تركز RotaryService فقط على طرق العرض القابلة للتركيز. عادةً ما تكون بعض طرق العرض، مثل Button s، قابلة للتركيز. البعض الآخر، مثل TextView s و ViewGroup s، عادةً لا يكون كذلك. تكون العروض القابلة للنقر قابلة للتركيز تلقائيًا وتكون العروض قابلة للنقر عليها تلقائيًا عندما يكون بها مستمع للنقرات. إذا أدى هذا المنطق التلقائي إلى إمكانية التركيز المطلوبة، فلن تحتاج إلى تعيين إمكانية التركيز على العرض بشكل صريح. إذا لم ينتج عن المنطق التلقائي إمكانية التركيز المطلوبة، فاضبط السمة 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. لتتبع هذه الحالة، أضف متغير مثيل إلى طريقة العرض الخاصة بك مع طرق الوصول:
    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 في مكتبة واجهة المستخدم للسيارة. عند تمكين هذه الميزة، تقوم 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 في التخطيط الخاص بك، فسيتم التعامل مع العرض الجذر كمنطقة تركيز ضمنية. لا يمكن للمستخدم دفعه للتنقل في التطبيق. بدلاً من ذلك، سيتم تدويرها عبر جميع طرق العرض القابلة للتركيز، والتي قد تكون كافية لمربعات الحوار.

تخصيص منطقة التركيز

يمكن استخدام سمتين قياسيتين للعرض لتخصيص التنقل الدوار:

  • android:nextFocusForward يسمح لمطوري التطبيقات بتحديد ترتيب التناوب في منطقة التركيز. هذه هي نفس السمة المستخدمة للتحكم في ترتيب علامات التبويب للتنقل عبر لوحة المفاتيح. لا تستخدم هذه السمة لإنشاء حلقة. بدلاً من ذلك، استخدم app:wrapAround (انظر أدناه) لإنشاء حلقة.
  • android:focusedByDefault يسمح لمطوري التطبيقات بتحديد عرض التركيز الافتراضي في النافذة. لا تستخدم هذه السمة و app:defaultFocus (انظر أدناه) في نفس FocusArea .

تحدد FocusArea أيضًا بعض السمات لتخصيص التنقل الدوار. لا يمكن تخصيص مناطق التركيز الضمنية باستخدام هذه السمات.

  1. ( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 12 )
    يمكن استخدام app:defaultFocus لتحديد معرف العرض التنازلي القابل للتركيز، والذي يجب التركيز عليه عندما يدفع المستخدم إلى FocusArea .
  2. ( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 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. ( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 12 )
    لتمكين التدوير للالتفاف في FocusArea هذه، يمكن ضبط app:wrapAround على true . يتم استخدام هذا عادةً عندما يتم ترتيب العروض في شكل دائرة أو بيضاوية.
  5. ( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 12 )
    لضبط مساحة التمييز في FocusArea هذه، استخدم app:highlightPaddingStart و app:highlightPaddingEnd و app:highlightPaddingTop و app:highlightPaddingBottom و app:highlightPaddingHorizontal و app:highlightPaddingVertical .
  6. ( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 12 )
    لضبط الحدود المتصورة FocusArea هذه للعثور على هدف دفع، استخدم app:startBoundOffset app:endBoundOffset و app:topBoundOffset و app:bottomBoundOffset app:horizontalBoundOffset و app:verticalBoundOffset .
  7. ( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 12 )
    لتحديد معرف FocusArea (أو المناطق) المجاورة بشكل صريح في الاتجاهات المحددة، استخدم app:nudgeLeft و app:nudgeRight و app:nudgeUp و app:nudgeDown . استخدم هذا عندما لا يجد البحث الهندسي المستخدم افتراضيًا الهدف المطلوب.

يتنقل الدفع عادةً بين مناطق التركيز. ولكن باستخدام اختصارات الدفع، أحيانًا ما يتم التنقل أولاً داخل FocusArea بحيث قد يحتاج المستخدم إلى الدفع مرتين للانتقال إلى FocusArea التالية. تكون اختصارات الدفع مفيدة عندما تحتوي FocusArea على قائمة طويلة متبوعة بزر إجراء عائم ، كما في المثال أدناه:

دفع الاختصار
الشكل 3. اختصار الدفع

بدون اختصار الدفع، سيتعين على المستخدم التنقل عبر القائمة بأكملها للوصول إلى FAB.

تخصيص تسليط الضوء على التركيز

كما هو مذكور أعلاه، تعتمد RotaryService على مفهوم التركيز على العرض الحالي لإطار عمل Android. عندما يقوم المستخدم بالتدوير والدفع، تقوم RotaryService بتحريك التركيز، مع التركيز على عرض واحد وإلغاء التركيز على عرض آخر. في Android، عندما يتم التركيز على العرض، إذا كان العرض:

  • قام Android بتحديد تسليط الضوء على التركيز الخاص به، حيث يقوم Android برسم تسليط الضوء على العرض.
  • لا يحدد تمييز التركيز، ولا يتم تعطيل تمييز التركيز الافتراضي، ويرسم Android تمييز التركيز الافتراضي للعرض.

عادةً لا تحدد التطبيقات المصممة للمس نقاط التركيز المناسبة.

يتم توفير تمييز التركيز الافتراضي بواسطة إطار عمل Android ويمكن تجاوزه بواسطة OEM. يتلقاها مطورو التطبيقات عندما يكون السمة التي يستخدمونها مشتقة من Theme.DeviceDefault .

للحصول على تجربة مستخدم متسقة، اعتمد على تمييز التركيز الافتراضي كلما أمكن ذلك. إذا كنت بحاجة إلى تمييز تركيز ذو شكل مخصص (على سبيل المثال، دائري أو على شكل قرص)، أو إذا كنت تستخدم سمة غير مشتقة من Theme.DeviceDefault ، فاستخدم موارد مكتبة واجهة المستخدم للسيارة لتحديد تمييز التركيز الخاص بك لـ كل عرض.

لتحديد تمييز تركيز مخصص لعرض ما، قم بتغيير الخلفية أو المقدمة الرسومية للعرض إلى رسم يختلف عندما يتم التركيز على العرض. عادة، يمكنك تغيير الخلفية. الرسم التالي، إذا تم استخدامه كخلفية لعرض مربع، سينتج تمييزًا دائريًا للتركيز:

<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. القيم الافتراضية للموارد الجريئة

بالإضافة إلى ذلك، يتم استدعاء تمييز التركيز المخصص عندما يتم إعطاء زر لون خلفية خالص لجذب انتباه المستخدم، كما في المثال أدناه. قد يؤدي ذلك إلى صعوبة رؤية التركيز البؤري. في هذه الحالة، حدد تمييز التركيز المخصص باستخدام الألوان الثانوية :

لون الخلفية الصلبة
  • ( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 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 s، فيجب عليك استخدام CarUiRecyclerView s بدلاً من ذلك. وهذا يضمن أن واجهة المستخدم الخاصة بك متوافقة مع الآخرين لأن تخصيص الشركة المصنعة للمعدات الأصلية (OEM) ينطبق على جميع برامج CarUiRecyclerView .

إذا كانت جميع العناصر الموجودة في قائمتك قابلة للتركيز، فلن تحتاج إلى القيام بأي شيء آخر. ينقل التنقل الدوار التركيز عبر العناصر الموجودة في القائمة ويتم تمرير القائمة لجعل العنصر الذي تم التركيز عليه حديثًا مرئيًا.

( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 12 )
إذا كان هناك مزيج من العناصر القابلة للتركيز والعناصر غير القابلة للتركيز، أو إذا كانت جميع العناصر غير قابلة للتركيز، فيمكنك تمكين التمرير الدوار، مما يسمح للمستخدم باستخدام وحدة التحكم الدوارة للتمرير تدريجيًا خلال القائمة دون تخطي العناصر غير القابلة للتركيز. لتمكين التمرير الدوار، قم بتعيين السمة app:rotaryScrollEnabled على true .

( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 12 )
يمكنك تمكين التمرير الدوار في أي عرض قابل للتمرير، بما في ذلك av CarUiRecyclerView ، باستخدام طريقة setRotaryScrollEnabled() في CarUiUtils . إذا قمت بذلك، فأنت بحاجة إلى:

  • اجعل العرض القابل للتمرير قابلاً للتركيز بحيث يمكن التركيز عليه عندما لا تكون أي من طرق العرض التابعة القابلة للتركيز مرئية،
  • قم بتعطيل تمييز التركيز الافتراضي في العرض القابل للتمرير عن طريق استدعاء setDefaultFocusHighlightEnabled(false) بحيث لا يبدو العرض القابل للتمرير مركّزًا،
  • تأكد من أن العرض القابل للتمرير يتم التركيز عليه قبل العناصر التابعة له عن طريق استدعاء setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) .
  • استمع إلى MotionEvents باستخدام SOURCE_ROTARY_ENCODER وإما AXIS_VSCROLL أو AXIS_HSCROLL للإشارة إلى المسافة المطلوب التمرير والاتجاه (من خلال العلامة).

عندما يتم تمكين التمرير الدوار في CarUiRecyclerView ويدور المستخدم إلى منطقة لا توجد بها طرق عرض قابلة للتركيز، يتغير شريط التمرير من اللون الرمادي إلى الأزرق، كما لو كان للإشارة إلى تركيز شريط التمرير. يمكنك تنفيذ تأثير مماثل إذا أردت.

أحداث الحركة هي نفسها التي تم إنشاؤها بواسطة عجلة التمرير على الماوس، باستثناء المصدر.

وضع التلاعب المباشر

عادةً، تتنقل الدفعات والتدوير عبر واجهة المستخدم، بينما يضغط الزر الأوسط على اتخاذ إجراء، على الرغم من أن هذا ليس هو الحال دائمًا. على سبيل المثال، إذا أراد المستخدم ضبط مستوى صوت المنبه، فيمكنه استخدام وحدة التحكم الدوارة للانتقال إلى شريط تمرير مستوى الصوت، والضغط على الزر الأوسط، وتدوير وحدة التحكم لضبط مستوى صوت المنبه، ثم الضغط على الزر "رجوع" للعودة إلى التنقل . ويشار إلى هذا باسم وضع المعالجة المباشرة (DM) . في هذا الوضع، يتم استخدام وحدة التحكم الدوارة للتفاعل مع العرض مباشرة بدلاً من التنقل.

قم بتنفيذ DM بإحدى طريقتين. إذا كنت تحتاج فقط إلى التعامل مع التدوير وكان العرض الذي تريد معالجته يستجيب لـ ACTION_SCROLL_FORWARD و ACTION_SCROLL_BACKWARD AccessibilityEvent s بشكل مناسب، فاستخدم الآلية البسيطة . خلاف ذلك، استخدم الآلية المتقدمة .

الآلية البسيطة هي الخيار الوحيد في نوافذ النظام؛ يمكن للتطبيقات استخدام أي من الآليتين.

آلية بسيطة

( اندرويد 11 QPR3، اندرويد 11 سيارة، اندرويد 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 ويجب أن يخرج الزر الخلفي من وضع 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 s والحصول على عدد التدوير في 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 قابلاً للتركيز.
  • ( Android 11 QPR3، Android 11 Car، تم إهماله في Android 11 )
    يجب أن تحتوي محتويات ActivityView على FocusParkingView كأول عرض قابل للتركيز، ويجب أن تكون السمة app:shouldRestoreFocus الخاصة به false .
  • يجب ألا تحتوي محتويات ActivityView على طرق عرض android:focusByDefault .

بالنسبة للمستخدم، يجب ألا يكون لـ ActivityViews أي تأثير على التنقل باستثناء أن مناطق التركيز لا يمكن أن تمتد إلى ActivityViews. بمعنى آخر، لا يمكن أن يكون لديك منطقة تركيز واحدة تحتوي على محتوى داخل وخارج ActivityView . إذا لم تقم بإضافة أي مناطق تركيز إلى ActivityView ، فإن جذر التسلسل الهرمي للعرض في ActivityView يعتبر منطقة تركيز ضمنية.

الأزرار التي تعمل عند الضغط عليها

تتسبب معظم الأزرار في حدوث بعض الإجراءات عند النقر عليها. تعمل بعض الأزرار عند الضغط عليها بدلاً من ذلك. على سبيل المثال، يعمل الزران Fast Forward وRewind عادةً عند الضغط باستمرار عليهما. لجعل هذه الأزرار تدعم الدوارة، استمع إلى 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 إجراءً (مثل إعادة اللف) ويقوم بجدولة نفسه ليتم تشغيله بعد تأخير.

وضع اللمس

يمكن للمستخدمين استخدام وحدة التحكم الدوارة للتفاعل مع الوحدة الرئيسية في السيارة بطريقتين، إما باستخدام وحدة التحكم الدوارة أو عن طريق لمس الشاشة. عند استخدام وحدة التحكم الدوارة، يتم تمييز إحدى طرق العرض القابلة للتركيز. عند لمس الشاشة، لا يظهر أي تمييز للتركيز. يمكن للمستخدم التبديل بين أوضاع الإدخال هذه في أي وقت:

  • الروتاري → اللمس. عندما يلمس المستخدم الشاشة، يختفي تمييز التركيز.
  • المس ← دوارة. عندما يقوم المستخدم بدفع الزر الأوسط أو تدويره أو الضغط عليه، يظهر تمييز التركيز.

ليس لزري الرجوع والصفحة الرئيسية أي تأثير على وضع الإدخال.

ظهورات دوارة على مفهوم Android الحالي لوضع اللمس . يمكنك استخدام View.isInTouchMode() لتحديد وضع الإدخال الذي يستخدمه المستخدم. يمكنك استخدام OnTouchModeChangeListener للاستماع إلى التغييرات. بينما يمكن استخدام هذا لتخصيص واجهة المستخدم الخاصة بك لوضع الإدخال الحالي، تجنب أي تغييرات كبيرة لأنها قد تكون مربكة.

استكشاف الأخطاء وإصلاحها

في التطبيق المصمم للعمل باللمس، من الشائع أن تكون هناك طرق عرض متداخلة قابلة للتركيز. على سبيل المثال، قد يكون هناك FrameLayout حول ImageButton ، وكلاهما قابل للتركيز. لا يضر هذا باللمس ولكنه قد يؤدي إلى تجربة مستخدم سيئة بالنسبة للدوران لأنه يجب على المستخدم تدوير وحدة التحكم مرتين للانتقال إلى العرض التفاعلي التالي. للحصول على تجربة مستخدم جيدة، توصي Google بجعل العرض الخارجي أو العرض الداخلي قابلاً للتركيز، ولكن ليس كليهما.

إذا فقد زر أو مفتاح التركيز عند الضغط عليه من خلال وحدة التحكم الدوارة، فقد ينطبق أحد الشروط التالية:

  • يتم تعطيل الزر أو المفتاح (لفترة وجيزة أو إلى أجل غير مسمى) بسبب الضغط على الزر. وفي كلتا الحالتين، هناك طريقتان لمعالجة هذا:
    • اترك الحالة android:enabled على أنها true واستخدم حالة مخصصة لتظليل الزر أو التبديل باللون الرمادي كما هو موضح في الحالة المخصصة .
    • استخدم حاوية لإحاطة الزر أو المفتاح واجعل الحاوية قابلة للتركيز بدلاً من الزر أو المفتاح. (يجب أن يكون مستمع النقر موجودًا على الحاوية.)
  • يتم استبدال الزر أو المفتاح. على سبيل المثال، قد يؤدي الإجراء المتخذ عند الضغط على الزر أو تبديل المفتاح إلى تحديث الإجراءات المتاحة مما يؤدي إلى استبدال الأزرار الجديدة بالأزرار الموجودة. هناك طريقتان لمعالجة هذا:
    • بدلاً من إنشاء زر أو مفتاح تبديل جديد، قم بتعيين رمز و/أو نص الزر أو المفتاح الموجود.
    • كما هو مذكور أعلاه، قم بإضافة حاوية قابلة للتركيز حول الزر أو المفتاح.

ملعب الروتاري

RotaryPlayground هو تطبيق مرجعي للروتاري. استخدمه لتتعلم كيفية دمج الميزات الدوارة في تطبيقاتك. يتم تضمين RotaryPlayground في إصدارات المحاكي وفي تصميمات الأجهزة التي تعمل بنظام التشغيل Android Automotive OS (AAOS).

  • مستودع RotaryPlayground : packages/apps/Car/tests/RotaryPlayground/
  • الإصدارات: Android 11 QPR3 وAndroid 11 Car وAndroid 12

يعرض تطبيق RotaryPlayground علامات التبويب التالية على اليسار:

  • بطاقات. اختبر التنقل حول مناطق التركيز، وتخطي العناصر غير القابلة للتركيز وإدخال النص.
  • التلاعب المباشر. أدوات الاختبار التي تدعم وضع المعالجة المباشرة البسيط والمتقدم. علامة التبويب هذه مخصصة للمعالجة المباشرة داخل نافذة التطبيق.
  • التلاعب بواجهة مستخدم النظام. أدوات الاختبار التي تدعم المعالجة المباشرة في نوافذ النظام حيث يتم دعم وضع المعالجة المباشرة البسيطة فقط.
  • شبكة. اختبار التنقل الدوار بنمط z مع التمرير.
  • إشعار. اختبار الدفع داخل وخارج الإخطارات الفردية.
  • قم بالتمرير. اختبر التمرير عبر مزيج من المحتوى القابل للتركيز وغير القابل للتركيز.
  • عرض ويب. اختبار التنقل عبر الروابط في WebView .
  • FocusArea المخصصة تخصيص FocusArea الاختبارية:
    • التفاف حولها.
    • android:focusedByDefault و app:defaultFocus
    • .
    • أهداف تحفيزية صريحة.
    • اختصارات الدفع.
    • FocusArea بدون طرق عرض قابلة للتركيز.