هذه المادة مخصّصة لمطوّري التطبيقات.
لكي يتوافق تطبيقك مع الشاشة الدوّارة، يجب استيفاء الشروط التالية:
- ضَع
FocusParkingView
في تنسيق النشاط المعنيّ. - تأكَّد من أنّ المشاهدات التي يمكن التركيز عليها (أو لا يمكن التركيز عليها) ظاهرة.
- استخدِم
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. الوصول إلى وحدة التحكّم الدوّارة المحاكية - اختَر Car rotary (التدوير في السيارة) في نافذة عناصر التحكّم الموسّعة:
الشكل 2. اختَر رمز السيارة الدوّارة
لوحة مفاتيح USB
- يُرجى توصيل لوحة مفاتيح USB بجهازك الذي يعمل بنظام التشغيل Android Automotive (AAOS). في بعض الحالات، يؤدي ذلك إلى منع ظهور لوحة المفاتيح على الشاشة.
- استخدِم إصدار
userdebug
أوeng
. - فعِّل فلترة الأحداث الرئيسية:
adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
- اطّلِع على الجدول التالي للعثور على المفتاح المقابل لكل إجراء:
المفتاح الإجراء الدوار Q تدوير عكس عقارب الساعة E تدوير في اتجاه عقارب الساعة A دفع إلى اليسار D دفع لليمين واط دفع لأعلى S دفع إلى الأسفل F أو فاصلة الزر الأوسط R أو Esc زر الرجوع
أوامر ADB
يمكنك استخدام أوامر car_service
لإدخال أحداث إدخال دوراني. يمكن تنفيذ هذه الأوامر
على الأجهزة التي تعمل بنظام التشغيل Android Automotive (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 |
النقر على زر الرجوع |
وحدة التحكّم الدوّارة من المصنّع الأصلي للجهاز
عندما يكون جهاز التحكّم الدوار مفعّلاً وقيد الاستخدام، هذا هو الخيار الأكثر واقعية. ويُعدّ ذلك مفيدًا بشكل خاص لاختبار السرعة العالية.
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
هو هدف التركيز ، يمكنه تحديد أنّه على وشك بدء لفّ الشاشة، وعندها يتجنّبRotaryService
لفّ الشاشة من خلال عدم نقل التركيز. - عندما يشغِّل جهاز التحكّم الدوّار أحد التطبيقات، يركّز Android على أول عرض يمكن التركيز عليه، وهو دائمًا
FocusParkingView
. يحدِّد الرمزFocusParkingView
العرض الأمثل للتركيز عليه، ثم يطبِّق التركيز.
طرق العرض التي يمكن التركيز عليها
تستند RotaryService
إلى المفهوم
الحالي
لتركيز العرض في إطار عمل Android، والذي يعود إلى الأيام التي كانت فيها الهواتف تحتوي على لوحات مفاتيح خارجية وأزرار تحكم ألعاب.
تم إعادة استخدام السمة الحالية 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
في الخلفية القابلة للرسم لعرضك. إذا لم يكن لديك حساب، عليك إضافة ما يلي: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
يمكن استخدام سمتَي عرض عاديتَين لتخصيص التنقّل الدوّري:
- تسمح
android:nextFocusForward
لمطوّري التطبيقات بتحديد ترتيب التدوير في منطقة التركيز. هذه هي السمة نفسها المستخدَمة للتحكّم في ترتيب مفتاح التبويب لأجل التنقّل باستخدام لوحة المفاتيح. لا تستخدِم هذه السمة لإنشاء حلقة. بدلاً من ذلك، استخدِم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
على قائمة طويلة متبوعة بزرّ عمل عائم،
كما هو موضّح في المثال أدناه:

بدون اختصار الإشعارات المنبثقة، على المستخدم التنقّل في القائمة بأكملها للوصول إلى العنصر النائب الكبير.
تخصيص تمييز التركيز
كما ذكرنا أعلاه، يستند RotaryService
إلى مفهوم "تركيز العرض" الحالي في إطار عمل Android. عندما يدير المستخدم الشاشة ويدفعها، ينقل RotaryService
التركيز حول الشاشة، ويشدّد على عرض معيّن ويزيل التركيز عن عرض آخر. في Android، عند التركيز على عرض، إذا كان العرض:
- إذا حدّدت عنصر تمييز للتركيز، يرسم Android عنصر تمييز التركيز للعرض.
- إذا لم يتم تحديد تمييز للتركيز ولم يتم إيقاف تمييز التركيز التلقائي، يرتسم تمييز التركيز التلقائي للعرض على نظام التشغيل Android.
لا تحدّد التطبيقات المصمّمة للاستخدام باللمس عادةً عناصر التركيز المناسبة.
يقدّم إطار عمل Android ميزة تمييز التركيز التلقائية، ويمكن للمصنع الأصلي للجهاز تجاوزها. يتلقّى مطوّرو التطبيقات هذا الإشعار عندما يكون المظهر الذي يستخدمونه مشتقًا من
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) تشير مراجع الموارد الغامقة في المثال أعلاه إلى الموارد التي تحدّدها مكتبة car-ui-library. ويلغيها المصنّع الأصلي للجهاز لتكون متسقة مع ميزة تسليط الضوء على العنصر الذي يتم التركيز عليه تلقائيًا التي يحدّدها. ويضمن ذلك عدم تغيُّر لون تمييز التركيز وعرضه وما إلى ذلك عندما ينتقل المستخدم بين عرض يتضمّن تمييزًا مخصّصًا للتركيز وعرض يتضمّن تمييز التركيز التلقائي. العنصر الأخير هو تموج يُستخدَم لللمس. تظهر القيم التلقائية المستخدَمة للموارد المميّزة بخط غامق على النحو التالي:

بالإضافة إلى ذلك، يتمّ استخدام تمييز مخصّص للتركيز عندما يتمّ منح زرّ لون خلفية جامد للفت انتباه المستخدِم إليه، كما هو موضّح في المثال أدناه. وقد يصعّب ذلك رؤية ميزة تمييز التركيز. في هذه الحالة، حدِّد تمييزًا مخصّصًا للتركيز باستخدام ألوان ثانوية:
![]() |
- (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). في هذا الوضع، يتم استخدام وحدة التحكّم الدوّارة للتفاعل مع العرض مباشرةً بدلاً من التنقّل.
يمكنك تنفيذ ميزة "الرسائل المباشرة" بطريقتَين: إذا كنت بحاجة فقط إلى التعامل مع عملية الدوران وكان العرض الذي تريد
التلاعب به يستجيب ل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"، يمكن استخدام زر يمثّل
الرسائل المباشرة للدخول إلى وضع الرسائل المباشرة.
لتتوافق مع وضع "إدارة العملاء" المتقدّم، يجب أن تستوفي طريقة العرض الشروط التالية:
- (Android 11 QPR3 وAndroid 11 Car
وAndroid 12) يجب أن يستمع الجهاز إلى حدث
KEYCODE_DPAD_CENTER
للدخول إلى وضع "وضع القيادة" والاستماع إلى حدثKEYCODE_BACK
للخروج من وضع "وضع القيادة"، مع استدعاءDirectManipulationHelper.enableDirectManipulationMode()
في كلتا الحالتَين. للانتباه إلى هذه الأحداث، نفِّذ أحد الإجراءات التالية:- سجِّل
OnKeyListener
.
أو
- وسِّع العرض ثم استبدِل طريقة
dispatchKeyEvent()
.
- سجِّل
- يجب الاستماع إلى أحداث التذكيرات التلقائية (
KEYCODE_DPAD_UP
أوKEYCODE_DPAD_DOWN
أوKEYCODE_DPAD_LEFT
أوKEYCODE_DPAD_RIGHT
) إذا كان من المفترض أن تتم إدارة التذكيرات التلقائية في طريقة العرض. - يجب الاستماع إلى
MotionEvent
والحصول على عدد مرات التناوب فيAXIS_SCROLL
إذا كان العرض يريد التعامل مع التناوب. هناك عدة طرق لإجراء ذلك:- سجِّل
OnGenericMotionListener
. - وسِّع العرض وألغِ طريقة
dispatchTouchEvent()
.
- سجِّل
- لتجنُّب البقاء في وضع "العرض المباشر"، يجب الخروج من هذا الوضع عندما يكون المقتطف أو النشاط الذي ينتمي إليه العرض غير تفاعلي.
- يجب أن يوفّر إشارة مرئية للإشارة إلى أنّ العرض في وضع المحادثة المباشرة.
في ما يلي عيّنة من طريقة عرض مخصّصة تستخدِم وضع "العرض على الشاشة" لتمرير خريطة وتحريكها وتكبيرها أو تصغيرها:
/** 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
منطقة تركيز ضمنية.
الأزرار التي تعمل عند الضغط عليها باستمرار
تؤدي معظم الأزرار إلى تنفيذ بعض الإجراءات عند النقر عليها. تعمل بعض الأزرار عند الضغط عليها مع الاستمرار بدلاً من ذلك.
على سبيل المثال، يعمل زرّا "تقديم سريع" و"الترجيع السريع" عادةً عند الضغط عليهما مع الاستمرار. لجعل هذه buttons
تتيح استخدام الشاشة الدوّارة، انتظِر ظهور 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
هو تطبيق مرجعي للأجهزة الدوّارة. يمكنك الاطّلاع عليه للتعرّف على كيفية دمج ميزات الالتفاف في تطبيقاتك. يتم تضمين RotaryPlayground
في إصدارات المحاكي وفي
إصدارات الأجهزة التي تعمل بنظام التشغيل Android Automotive (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
بدون ملفّات شخصية يمكن التركيز عليها