विकासशील ऐप्स

निम्नलिखित सामग्री ऐप डेवलपर्स के लिए है।

अपने ऐप को रोटरी बनाने के लिए, आपको यह करना होगा:

  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. कार रोटरी का चयन करें

यूएसबी कीबोर्ड

  • अपने डिवाइस में एक यूएसबी कीबोर्ड प्लग करें जो एंड्रॉइड ऑटोमोटिव ओएस (एएओएस) चलाता है, कुछ मामलों में, यह ऑन-स्क्रीन कीबोर्ड को प्रदर्शित होने से रोक सकता है।
  • userdebug या eng बिल्ड का उपयोग करें।
  • मुख्य ईवेंट फ़िल्टरिंग सक्षम करें:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • प्रत्येक क्रिया के लिए संबंधित कुंजी खोजने के लिए नीचे दी गई तालिका देखें:
    चाभी रोटरी क्रिया
    क्यू घड़ी की सूई के विपरित दिशा में घुमायें
    घड़ी की सुई की दिशा में घुमाओ
    कुहनी छोड़ दिया
    डी दाईं ओर कुहनी मारें
    वू कुहनी मारना
    एस नीचा दिखाना
    एफ या अल्पविराम केंद्र बटन
    आर या एएससी पिछला बटन

एडीबी आदेश

रोटरी इनपुट ईवेंट को इंजेक्ट करने के लिए आप 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 ms पहले और 50 ms पहले)
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 व्यू कार यूआई लाइब्रेरी (कार-यूआई-लाइब्रेरी) में एक पारदर्शी दृश्य है। 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. जब रोटरी कंट्रोल एक ऐप लॉन्च करता है, तो एंड्रॉइड पहले फोकस करने योग्य दृश्य को केंद्रित करता है, जो हमेशा FocusParkingView होता है। FocusParkingView व्यू फोकस करने के लिए इष्टतम दृश्य निर्धारित करता है और फिर फोकस लागू करता है।

ध्यान देने योग्य दृश्य

RotaryService एंड्रॉइड फ्रेमवर्क की मौजूदा अवधारणा पर ध्यान केंद्रित करती है, जब फोन में भौतिक कीबोर्ड और डी-पैड थे। मौजूदा android:nextFocusForward विशेषता को रोटरी के लिए फिर से तैयार किया गया है ( फोकसएरिया कस्टमाइज़ेशन देखें), लेकिन 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 यह नियंत्रित करने के लिए कि एंड्रॉइड को इसे अक्षम माना जाना चाहिए, यह नियंत्रित करने के लिए कि दृश्य कैसा दिखता है। आपका ऐप उपयोगकर्ता को सूचित कर सकता है कि टैप करने पर दृश्य अक्षम क्यों है। अगला भाग बताता है कि यह कैसे करना है।

कस्टम स्थिति

एक कस्टम स्थिति जोड़ने के लिए:

  1. अपने दृश्य में एक कस्टम विशेषता जोड़ने के लिए। उदाहरण के लिए, CustomView व्यू क्लास में state_rotary_enabled कस्टम स्थिति जोड़ने के लिए, उपयोग करें:
    <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. बटन को अक्षम दिखाने के लिए, अपने दृश्य की पृष्ठभूमि को खींचने योग्य बनाने के लिए, android:state_enabled state_enabled के बजाय app:state_rotary_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 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
    app:defaultFocus का उपयोग फोकस करने योग्य वंशज दृश्य की आईडी निर्दिष्ट करने के लिए किया जा सकता है, जिसे इस पर ध्यान केंद्रित किया जाना चाहिए जब उपयोगकर्ता इस फोकस एरिया पर FocusArea है।
  2. ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
    app:defaultFocusOverridesHistory को true पर सेट किया जा सकता है ताकि ऊपर निर्दिष्ट दृश्य को फ़ोकस किया जा सके, भले ही इतिहास के साथ इस FocusArea में किसी अन्य दृश्य को इंगित करने पर ध्यान केंद्रित किया गया हो।
  3. ( एंड्रॉयड 12 )
    एक फोकस करने योग्य वंशज दृश्य की आईडी निर्दिष्ट करने के लिए app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut , और app:nudgeDownShortcut का उपयोग करें, जो इस बात पर केंद्रित होना चाहिए कि उपयोगकर्ता किसी दिशा में कुहनी मारता है। अधिक जानने के लिए, नीचे कुहनी से हलका शॉर्टकट के लिए सामग्री देखें।

    ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 में बहिष्कृत ) app:nudgeShortcut और app:nudgeShortcutDirection ने केवल एक कुहनी से हलका शॉर्टकट का समर्थन किया।

  4. ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
    इस FocusArea में घूमने के लिए रोटेशन को सक्षम करने के लिए, app:wrapAround को true पर सेट किया जा सकता है। यह आमतौर पर तब उपयोग किया जाता है जब दृश्यों को एक वृत्त या अंडाकार में व्यवस्थित किया जाता है।
  5. ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
    इस फोकस क्षेत्र में हाइलाइट की पैडिंग को समायोजित करने के लिए, app:highlightPaddingEnd : FocusArea , app:highlightPaddingStart हाइलाइटपैडिंगएंड, ऐप: app:highlightPaddingTop , ऐप: app:highlightPaddingBottom , app:highlightPaddingHorizontal : हाइलाइटपैडिंग हॉरिज़ॉन्टल, और app:highlightPaddingVertical : हाइलाइटपैडिंग वर्टिकल का उपयोग करें।
  6. ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
    एक कुहनी लक्ष्य खोजने के लिए इस FocusArea की कथित सीमा को समायोजित करने के लिए, ऐप: app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , ऐप:बॉटमबाउंडऑफ़सेट, app:horizontalBoundOffset app:bottomBoundOffset , और app:verticalBoundOffset :वर्टिकलबाउंडऑफ़सेट का उपयोग करें।
  7. ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
    दिए गए दिशा-निर्देशों में निकटवर्ती FocusArea (या क्षेत्रों) की आईडी को स्पष्ट रूप से निर्दिष्ट करने के लिए, ऐप app:nudgeLeft , app:nudgeRight , app:nudgeUp और app:nudgeDown का उपयोग करें। इसका उपयोग तब करें जब डिफ़ॉल्ट रूप से उपयोग की जाने वाली ज्यामितीय खोज को वांछित लक्ष्य न मिले।

न्यूडिंग आमतौर पर फोकस एरिया के बीच नेविगेट करता है। लेकिन न्यूड शॉर्टकट के साथ, कभी-कभी पहले फोकस एरिया में नेविगेट किया जाता है ताकि उपयोगकर्ता को अगले FocusArea पर नेविगेट करने के लिए दो बार कुहनी FocusArea की आवश्यकता हो। जब FocusArea में फ्लोटिंग एक्शन बटन के बाद एक लंबी सूची होती है, तो न्यूड शॉर्टकट उपयोगी होते हैं, जैसा कि नीचे दिए गए उदाहरण में है:

कुहनी मारना शॉर्टकट
चित्रा 3. कुहनी से हलका धक्का शॉर्टकट

नोज शॉर्टकट के बिना, उपयोगकर्ता को FAB तक पहुंचने के लिए पूरी सूची को घुमाना होगा।

फ़ोकस हाइलाइट कस्टमाइज़ेशन

जैसा कि ऊपर उल्लेख किया गया है, RotaryService एंड्रॉइड फ्रेमवर्क की मौजूदा फोकस की अवधारणा पर आधारित है। जब उपयोगकर्ता घूमता है और कुहनी मारता है, RotaryService एक दृश्य पर ध्यान केंद्रित करते हुए और दूसरे पर ध्यान केंद्रित करते हुए फ़ोकस को इधर-उधर करता है। एंड्रॉइड में, जब कोई दृश्य केंद्रित होता है, यदि दृश्य:

  • अपने स्वयं के फोकस हाइलाइट निर्दिष्ट किया है, एंड्रॉइड दृश्य का फोकस हाइलाइट करता है।
  • फ़ोकस हाइलाइट निर्दिष्ट नहीं करता है, और डिफ़ॉल्ट फ़ोकस हाइलाइट अक्षम नहीं है, Android दृश्य के लिए डिफ़ॉल्ट फ़ोकस हाइलाइट खींचता है।

स्पर्श के लिए डिज़ाइन किए गए ऐप्स आमतौर पर उपयुक्त फ़ोकस हाइलाइट निर्दिष्ट नहीं करते हैं।

डिफ़ॉल्ट फोकस हाइलाइट एंड्रॉइड फ्रेमवर्क द्वारा प्रदान किया जाता है और इसे ओईएम द्वारा ओवरराइड किया जा सकता है। ऐप डेवलपर इसे तब प्राप्त करते हैं जब वे जिस थीम का उपयोग कर रहे हैं वह 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>

( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 ) ऊपर दिए गए नमूने में बोल्ड संसाधन संदर्भ कार-यूआई-लाइब्रेरी द्वारा परिभाषित संसाधनों की पहचान करते हैं। OEM इन्हें उनके द्वारा निर्दिष्ट डिफ़ॉल्ट फ़ोकस हाइलाइट के अनुरूप होने के लिए ओवरराइड करता है। यह सुनिश्चित करता है कि जब उपयोगकर्ता कस्टम फ़ोकस हाइलाइट वाले दृश्य और डिफ़ॉल्ट फ़ोकस हाइलाइट वाले दृश्य के बीच नेविगेट करता है, तो फ़ोकस हाइलाइट रंग, स्ट्रोक की चौड़ाई, इत्यादि में कोई बदलाव नहीं आता है। अंतिम वस्तु स्पर्श के लिए प्रयुक्त एक तरंग है। बोल्ड संसाधनों के लिए उपयोग किए गए डिफ़ॉल्ट मान निम्नानुसार दिखाई देते हैं:

बोल्ड संसाधनों के लिए डिफ़ॉल्ट मान
चित्र 4. बोल्ड संसाधनों के लिए डिफ़ॉल्ट मान

इसके अलावा, जब एक बटन को उपयोगकर्ता के ध्यान में लाने के लिए एक ठोस पृष्ठभूमि रंग दिया जाता है, तो एक कस्टम फ़ोकस हाइलाइट की आवश्यकता होती है, जैसा कि नीचे दिए गए उदाहरण में है। इससे फ़ोकस हाइलाइट को देखना मुश्किल हो सकता है। इस स्थिति में, द्वितीयक रंगों का उपयोग करके कस्टम फ़ोकस हाइलाइट निर्दिष्ट करें:

ठोस पृष्ठभूमि रंग
  • ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 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 का उपयोग करना चाहिए। यह सुनिश्चित करता है कि आपका UI दूसरों के अनुरूप है क्योंकि OEM का अनुकूलन सभी CarUiRecyclerView s पर लागू होता है।

यदि आपकी सूची के सभी तत्व ध्यान देने योग्य हैं, तो आपको कुछ और करने की आवश्यकता नहीं है। रोटरी नेविगेशन सूची में तत्वों के माध्यम से फ़ोकस को स्थानांतरित करता है और सूची नए फ़ोकस किए गए तत्व को दृश्यमान बनाने के लिए स्क्रॉल करती है।

( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
यदि फोकस करने योग्य और फोकस करने योग्य तत्वों का मिश्रण है, या यदि सभी तत्व फोकस करने योग्य नहीं हैं, तो आप रोटरी स्क्रॉलिंग को सक्षम कर सकते हैं, जो उपयोगकर्ता को रोटरी कंट्रोलर का उपयोग करने की अनुमति देता है ताकि बिना फोकस योग्य वस्तुओं को छोड़े धीरे-धीरे सूची में स्क्रॉल किया जा सके। रोटरी स्क्रॉलिंग को सक्षम करने के लिए, app:rotaryScrollEnabled विशेषता को true पर सेट करें।

( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
आप CarUiUtils में CarUiUtils setRotaryScrollEnabled() विधि के साथ av CarUiRecyclerView सहित किसी भी स्क्रॉल करने योग्य दृश्य में रोटरी स्क्रॉलिंग को सक्षम कर सकते हैं। यदि आप ऐसा करते हैं, तो आपको यह करना होगा:

  • स्क्रॉल करने योग्य दृश्य को फ़ोकस करने योग्य बनाएं ताकि उस पर ध्यान केंद्रित किया जा सके जब उसका कोई भी फ़ोकस करने योग्य वंश दृश्य दिखाई न दे,
  • स्क्रॉल करने योग्य दृश्य पर डिफ़ॉल्ट फ़ोकस हाइलाइट को अक्षम करने के लिए setDefaultFocusHighlightEnabled(false) को कॉल करें ताकि स्क्रॉल करने योग्य दृश्य फ़ोकस न हो,
  • सुनिश्चित करें कि स्क्रॉल करने योग्य दृश्य setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) पर कॉल करके उसके वंशजों के सामने केंद्रित है।
  • स्क्रॉल करने की दूरी और दिशा (चिह्न के माध्यम से) को इंगित करने के लिए SOURCE_ROTARY_ENCODER और AXIS_VSCROLL या AXIS_HSCROLL के साथ MotionEvents को सुनें।

जब CarUiRecyclerView पर रोटरी स्क्रॉलिंग सक्षम होती है और उपयोगकर्ता ऐसे क्षेत्र में घूमता है जहां कोई फोकस करने योग्य दृश्य मौजूद नहीं है, तो स्क्रॉलबार ग्रे से नीले रंग में बदल जाता है, जैसे कि स्क्रॉलबार केंद्रित है। आप चाहें तो एक समान प्रभाव लागू कर सकते हैं।

MotionEvents वही हैं जो स्रोत को छोड़कर, माउस पर स्क्रॉल व्हील द्वारा उत्पन्न होते हैं।

प्रत्यक्ष हेरफेर मोड

आम तौर पर, कुहनी और घुमाव उपयोगकर्ता इंटरफ़ेस के माध्यम से नेविगेट करते हैं, जबकि केंद्र बटन प्रेस कार्रवाई करते हैं, हालांकि हमेशा ऐसा नहीं होता है। उदाहरण के लिए, यदि कोई उपयोगकर्ता अलार्म वॉल्यूम समायोजित करना चाहता है, तो वे वॉल्यूम स्लाइडर पर नेविगेट करने के लिए रोटरी नियंत्रक का उपयोग कर सकते हैं, केंद्र बटन दबा सकते हैं, अलार्म वॉल्यूम समायोजित करने के लिए नियंत्रक को घुमा सकते हैं, और फिर नेविगेशन पर वापस जाने के लिए बैक बटन दबा सकते हैं। . इसे प्रत्यक्ष हेरफेर (डीएम) मोड के रूप में जाना जाता है। इस मोड में, रोटरी नियंत्रक का उपयोग नेविगेट करने के बजाय सीधे दृश्य के साथ बातचीत करने के लिए किया जाता है।

डीएम को दो में से एक तरीके से लागू करें। यदि आपको केवल रोटेशन को संभालने की आवश्यकता है और जिस दृश्य में आप हेरफेर करना चाहते हैं, वह ACTION_SCROLL_FORWARD और ACTION_SCROLL_BACKWARD AccessibilityEvent s को उचित रूप से प्रतिक्रिया देता है, तो सरल तंत्र का उपयोग करें। अन्यथा, उन्नत तंत्र का उपयोग करें।

सिस्टम विंडो में सरल तंत्र ही एकमात्र विकल्प है; ऐप्स किसी भी तंत्र का उपयोग कर सकते हैं।

सरल तंत्र

( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 )
आपके ऐप को DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) को कॉल करना चाहिए। जब उपयोगकर्ता डीएम मोड में होता है तो RotaryService पहचानता है और डीएम मोड में प्रवेश करता है जब उपयोगकर्ता केंद्र बटन दबाता है जबकि एक दृश्य केंद्रित होता है। जब डीएम मोड में, रोटेशन ACTION_SCROLL_FORWARD या ACTION_SCROLL_BACKWARD है और जब उपयोगकर्ता बैक बटन दबाता है तो डीएम मोड से बाहर निकल जाता है। डीएम मोड में प्रवेश करने और बाहर निकलने पर सरल तंत्र दृश्य की चयनित स्थिति को टॉगल करता है।

एक दृश्य संकेत प्रदान करने के लिए कि उपयोगकर्ता डीएम मोड में है, चयनित होने पर अपने विचार को अलग दिखाएं। उदाहरण के लिए, पृष्ठभूमि बदलें जब android:state_selected true

उन्नत तंत्र

ऐप निर्धारित करता है कि RotaryService डीएम मोड में कब प्रवेश करती है और बाहर निकलती है। एक सुसंगत उपयोगकर्ता अनुभव के लिए, डीएम दृश्य के साथ केंद्र बटन दबाकर डीएम मोड में प्रवेश करना चाहिए और बैक बटन को डीएम मोड से बाहर निकलना चाहिए। यदि केंद्र बटन और/या कुहनी का उपयोग नहीं किया जाता है, तो वे डीएम मोड से बाहर निकलने के वैकल्पिक तरीके हो सकते हैं। मैप्स जैसे ऐप्स के लिए, डीएम मोड में प्रवेश करने के लिए डीएम का प्रतिनिधित्व करने के लिए एक बटन का उपयोग किया जा सकता है।

उन्नत डीएम मोड का समर्थन करने के लिए, एक दृश्य:

  1. ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 12 ) डीएम मोड में प्रवेश करने के लिए एक KEYCODE_DPAD_CENTER ईवेंट सुनना चाहिए और प्रत्येक मामले में DirectManipulationHelper.enableDirectManipulationMode() को कॉल करते हुए DM मोड से बाहर निकलने के लिए KEYCODE_BACK ईवेंट सुनना चाहिए। इन घटनाओं को सुनने के लिए, निम्न में से कोई एक कार्य करें:
    • एक 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. डीएम मोड में फंसने से बचने के लिए, डीएम मोड से बाहर निकलना चाहिए, जब फ्रैगमेंट या गतिविधि जो दृश्य से संबंधित है, वह इंटरैक्टिव नहीं है।
  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 व्यू फोकस करने योग्य नहीं होना चाहिए।
  • ( एंड्रॉइड 11 क्यूपीआर3, एंड्रॉइड 11 कार, एंड्रॉइड 11 में पदावनत )
    ActivityView व्यू की सामग्री में पहले फोकस करने योग्य दृश्य के रूप में FocusParkingView व्यू होना चाहिए, और इसकी app:shouldRestoreFocus विशेषता false होनी चाहिए।
  • ActivityView व्यू की सामग्री में कोई android:focusByDefault दृश्य।

उपयोगकर्ता के लिए, एक्टिविटी व्यू का नेविगेशन पर कोई प्रभाव नहीं होना चाहिए, सिवाय इसके कि फोकस क्षेत्र एक्टिविटी व्यू का विस्तार नहीं कर सकते। दूसरे शब्दों में, आपके पास एक भी फोकस क्षेत्र नहीं हो सकता है जिसमें ActivityView के अंदर और बाहर सामग्री हो। यदि आप अपने ActivityView व्यू में कोई फोकस एरिया नहीं जोड़ते हैं, तो ActivityView व्यू में व्यू पदानुक्रम की जड़ को एक निहित फोकस क्षेत्र माना जाता है।

दबाए जाने पर काम करने वाले बटन

अधिकांश बटन क्लिक करने पर कुछ क्रिया का कारण बनते हैं। इसके बजाय दबाए जाने पर कुछ बटन काम करते हैं। उदाहरण के लिए, फास्ट फॉरवर्ड और रिवाइंड बटन आमतौर पर दबाए जाने पर काम करते हैं। ऐसे बटनों को रोटरी का समर्थन करने के लिए, KEYCODE_DPAD_CENTER KeyEvents को निम्नानुसार सुनें:

mButton.setOnKeyListener((v, keyCode, event) ->
{
    if (keyCode != KEYCODE_DPAD_CENTER) {
        return false;
    }
    if (event.getAction() == ACTION_DOWN) {
        mButton.setPressed(true);
        mHandler.post(mRunnable);
    } else {
        mButton.setPressed(false);
        mHandler.removeCallbacks(mRunnable);
    }
    return true;
});

जिसमें mRunnable एक क्रिया (जैसे रिवाइंडिंग) करता है और देरी के बाद खुद को चलाने के लिए शेड्यूल करता है।

टच मोड

उपयोगकर्ता रोटरी कंट्रोलर का उपयोग कार में हेड यूनिट के साथ दो तरह से इंटरैक्ट करने के लिए कर सकते हैं, या तो रोटरी कंट्रोलर का उपयोग करके या स्क्रीन को छूकर। रोटरी नियंत्रक का उपयोग करते समय, फोकस करने योग्य दृश्यों में से एक को हाइलाइट किया जाएगा। स्क्रीन को स्पर्श करते समय, कोई फ़ोकस हाइलाइट नहीं दिखाई देता है। उपयोगकर्ता किसी भी समय इन इनपुट मोड के बीच स्विच कर सकता है:

  • रोटरी → स्पर्श करें। जब उपयोगकर्ता स्क्रीन को स्पर्श करता है, तो फ़ोकस हाइलाइट गायब हो जाता है।
  • स्पर्श करें → रोटरी। जब उपयोगकर्ता केंद्र बटन को कुहनी से दबाता है, घुमाता है या दबाता है, तो फ़ोकस हाइलाइट प्रकट होता है।

बैक और होम बटन का इनपुट मोड पर कोई प्रभाव नहीं पड़ता है।

एंड्रॉइड के टच मोड की मौजूदा अवधारणा पर रोटरी पिगीबैक। उपयोगकर्ता किस इनपुट मोड का उपयोग कर रहा है यह निर्धारित करने के लिए आप View.isInTouchMode() का उपयोग कर सकते हैं। परिवर्तनों को सुनने के लिए आप OnTouchModeChangeListener का उपयोग कर सकते हैं। हालांकि इसका उपयोग वर्तमान इनपुट मोड के लिए आपके यूजर इंटरफेस को अनुकूलित करने के लिए किया जा सकता है, किसी भी बड़े बदलाव से बचें क्योंकि वे विचलित करने वाले हो सकते हैं।

समस्या निवारण

स्पर्श के लिए डिज़ाइन किए गए ऐप में, नेस्टेड फ़ोकस करने योग्य दृश्य होना असामान्य नहीं है। उदाहरण के लिए, ImageButton के आस-पास FrameLayout हो सकता है, जो दोनों फोकस करने योग्य हैं। यह स्पर्श के लिए कोई नुकसान नहीं करता है, लेकिन इसके परिणामस्वरूप रोटरी के लिए खराब उपयोगकर्ता अनुभव हो सकता है क्योंकि उपयोगकर्ता को अगले इंटरैक्टिव दृश्य में जाने के लिए नियंत्रक को दो बार घुमाना होगा। एक अच्छे उपयोगकर्ता अनुभव के लिए, Google अनुशंसा करता है कि आप बाहरी दृश्य या आंतरिक दृश्य को फ़ोकस करने योग्य बनाएं, लेकिन दोनों को नहीं।

यदि रोटरी नियंत्रक के माध्यम से दबाए जाने पर कोई बटन या स्विच फोकस खो देता है, तो इनमें से कोई एक शर्त लागू हो सकती है:

  • बटन दबाए जाने के कारण बटन या स्विच अक्षम किया जा रहा है (संक्षिप्त या अनिश्चित काल के लिए)। किसी भी मामले में, इसे संबोधित करने के दो तरीके हैं:
    • android:enabled स्थिति को true के रूप में छोड़ दें और कस्टम स्थिति में बताए अनुसार बटन को धूसर करने या स्विच करने के लिए कस्टम स्थिति का उपयोग करें।
    • बटन को घेरने या स्विच करने के लिए कंटेनर का उपयोग करें और बटन या स्विच के बजाय कंटेनर को फ़ोकस करने योग्य बनाएं। (क्लिक श्रोता कंटेनर पर होना चाहिए।)
  • बटन या स्विच को बदला जा रहा है। उदाहरण के लिए, जब बटन दबाया जाता है या स्विच को टॉगल किया जाता है तो की गई कार्रवाई उपलब्ध क्रियाओं का एक ताज़ा ट्रिगर ट्रिगर कर सकती है जिससे मौजूदा बटनों को बदलने के लिए नए बटन हो सकते हैं। इसे संबोधित करने के दो तरीके हैं:
    • एक नया बटन या स्विच बनाने के बजाय, मौजूदा बटन या स्विच का आइकन और/या टेक्स्ट सेट करें।
    • ऊपर के रूप में, बटन या स्विच के चारों ओर एक फोकस करने योग्य कंटेनर जोड़ें।

रोटरी खेल का मैदान

RotaryPlayground रोटरी के लिए एक संदर्भ ऐप है। अपने ऐप्स में रोटरी सुविधाओं को एकीकृत करने का तरीका जानने के लिए इसका उपयोग करें। RotaryPlayground एंड्रॉइड ऑटोमोटिव ओएस (एएओएस) चलाने वाले उपकरणों के लिए एमुलेटर बिल्ड और बिल्ड में शामिल है।

  • RotaryPlayground रिपॉजिटरी: packages/apps/Car/tests/RotaryPlayground/
  • संस्करण: Android 11 QPR3, Android 11 कार और Android 12

RotaryPlayground ऐप बाईं ओर निम्नलिखित टैब दिखाता है:

  • पत्ते। फोकस क्षेत्रों के आसपास नेविगेट करने का परीक्षण करें, ध्यान न देने योग्य तत्वों और टेक्स्ट इनपुट को छोड़ दें।
  • प्रत्यक्ष हेरफेर। परीक्षण विजेट जो सरल और उन्नत प्रत्यक्ष हेरफेर मोड का समर्थन करते हैं। यह टैब विशेष रूप से ऐप विंडो के भीतर सीधे हेरफेर के लिए है।
  • Sys UI मैनिपुलेशन। टेस्ट विजेट जो सिस्टम विंडो में सीधे हेरफेर का समर्थन करते हैं जहां केवल सरल प्रत्यक्ष हेरफेर मोड समर्थित है।
  • जाल। स्क्रॉलिंग के साथ z-पैटर्न रोटरी नेविगेशन का परीक्षण करें।
  • अधिसूचना। हेड-अप नोटिफिकेशन के अंदर और बाहर की जांच करें।
  • स्क्रॉल करें। फ़ोकस करने योग्य और फ़ोकस न करने योग्य सामग्री के मिश्रण के माध्यम से स्क्रॉलिंग का परीक्षण करें।
  • वेबव्यू। WebView में लिंक के माध्यम से नेविगेट करने का परीक्षण करें।
  • कस्टम FocusArea FocusArea अनुकूलन का परीक्षण करें:
    • चारों ओर लपेट दो।
    • android:focusedByDefault और app:defaultFocus
    • .
    • स्पष्ट कुहनी से निशाना साधा।
    • कुतरना शॉर्टकट।
    • FocusArea बिना फोकस करने योग्य विचारों वाला।
,

निम्नलिखित सामग्री ऐप डेवलपर्स के लिए है।

अपने ऐप को रोटरी बनाने के लिए, आपको यह करना होगा:

  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. कार रोटरी का चयन करें

यूएसबी कीबोर्ड

  • अपने डिवाइस में एक यूएसबी कीबोर्ड प्लग करें जो एंड्रॉइड ऑटोमोटिव ओएस (एएओएस) चलाता है, कुछ मामलों में, यह ऑन-स्क्रीन कीबोर्ड को प्रदर्शित होने से रोक सकता है।
  • userdebug या eng बिल्ड का उपयोग करें।
  • मुख्य ईवेंट फ़िल्टरिंग सक्षम करें:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • प्रत्येक क्रिया के लिए संबंधित कुंजी खोजने के लिए नीचे दी गई तालिका देखें:
    चाभी रोटरी क्रिया
    क्यू घड़ी की सूई के विपरित दिशा में घुमायें
    घड़ी की सुई की दिशा में घुमाओ
    कुहनी छोड़ दिया
    डी दाईं ओर कुहनी मारें
    वू कुहनी मारना
    एस नीचा दिखाना
    एफ या अल्पविराम केंद्र बटन
    आर या एएससी पिछला बटन

एडीबी आदेश

रोटरी इनपुट ईवेंट को इंजेक्ट करने के लिए आप 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 ms पहले और 50 ms पहले)
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 व्यू कार यूआई लाइब्रेरी (कार-यूआई-लाइब्रेरी) में एक पारदर्शी दृश्य है। 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. जब रोटरी कंट्रोल एक ऐप लॉन्च करता है, तो एंड्रॉइड पहले फोकस करने योग्य दृश्य को केंद्रित करता है, जो हमेशा FocusParkingView होता है। FocusParkingView व्यू फोकस करने के लिए इष्टतम दृश्य निर्धारित करता है और फिर फोकस लागू करता है।

ध्यान देने योग्य दृश्य

RotaryService एंड्रॉइड फ्रेमवर्क की मौजूदा अवधारणा पर ध्यान केंद्रित करती है, जब फोन में भौतिक कीबोर्ड और डी-पैड थे। मौजूदा android:nextFocusForward विशेषता को रोटरी के लिए फिर से तैयार किया गया है ( फोकसएरिया कस्टमाइज़ेशन देखें), लेकिन 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 यह नियंत्रित करने के लिए कि एंड्रॉइड को इसे अक्षम माना जाना चाहिए, यह नियंत्रित करने के लिए कि दृश्य कैसा दिखता है। आपका ऐप उपयोगकर्ता को सूचित कर सकता है कि टैप करने पर दृश्य अक्षम क्यों है। अगला भाग बताता है कि यह कैसे करना है।

कस्टम स्थिति

एक कस्टम स्थिति जोड़ने के लिए:

  1. अपने दृश्य में एक कस्टम विशेषता जोड़ने के लिए। उदाहरण के लिए, CustomView व्यू क्लास में state_rotary_enabled कस्टम स्थिति जोड़ने के लिए, उपयोग करें:
    <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. बटन को अक्षम दिखाने के लिए, अपने दृश्य की पृष्ठभूमि को खींचने योग्य बनाने के लिए, android:state_enabled state_enabled के बजाय app:state_rotary_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. When handling rotate and nudge actions, RotaryService looks for instances of FocusArea in the view hierarchy.
  2. When receiving a rotation event, RotaryService moves focus to another View that can take focus in the same FocusArea .
  3. When receiving a nudge event, RotaryService move focus to another view that can take focus in another (typically adjacent) FocusArea .

If you don't include any FocusAreas in your layout, the root view is treated as an implicit focus area. The user can't nudge to navigate in the app. Instead, they'll rotate through all focusable views, which may be adequate for dialogs.

FocusArea customization

Two standard View attributes can be used to customize rotary navigation:

  • android:nextFocusForward allows app developers to specify the rotation order in a focus area. This is the same attribute used to control the Tab order for keyboard navigation. Do NOT use this attribute to create a loop. Instead, use app:wrapAround (see below) to create a loop.
  • android:focusedByDefault allows app developers to specify the default focus view in the window. Do NOT use this attribute and app:defaultFocus (see below) in the same FocusArea .

FocusArea also defines some attributes to customize rotary navigation. Implicit focus areas can't be customized with these attributes.

  1. ( Android 11 QPR3, Android 11 Car, Android 12 )
    app:defaultFocus can be used to specify the ID of a focusable descendant view, which should be focused on when the user nudges to this FocusArea .
  2. ( Android 11 QPR3, Android 11 Car, Android 12 )
    app:defaultFocusOverridesHistory can be set to true to make the view specified above take focus even if with history to indicate another view in this FocusArea had been focused on.
  3. ( Android 12 )
    Use app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut , and app:nudgeDownShortcut to specify the ID of a focusable descendant view, which should be focused on when the user nudges in a given direction. To learn more, see the content for nudge shortcuts below.

    ( Android 11 QPR3, Android 11 Car, deprecated in Android 12 ) app:nudgeShortcut and app:nudgeShortcutDirection supported only one nudge shortcut.

  4. ( Android 11 QPR3, Android 11 Car, Android 12 )
    To enable rotation to wrap around in this FocusArea , app:wrapAround can be set to true . This is most typically used when views are arranged in a circle or oval.
  5. ( Android 11 QPR3, Android 11 Car, Android 12 )
    To adjust the padding of the highlight in this FocusArea , use app:highlightPaddingStart , app:highlightPaddingEnd , app:highlightPaddingTop , app:highlightPaddingBottom , app:highlightPaddingHorizontal , and app:highlightPaddingVertical .
  6. ( Android 11 QPR3, Android 11 Car, Android 12 )
    To adjust the perceived bounds of this FocusArea to find a nudge target, use app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , app:bottomBoundOffset , app:horizontalBoundOffset , and app:verticalBoundOffset .
  7. ( Android 11 QPR3, Android 11 Car, Android 12 )
    To explicitly specify the ID of an adjacent FocusArea (or areas) in the given directions, use app:nudgeLeft , app:nudgeRight , app:nudgeUp , and app:nudgeDown . Use this when the geometric search used by default doesn't find the desired target.

Nudging usually navigates between FocusAreas. But with nudge shortcuts, nudging sometimes first navigates within a FocusArea so that the user may need to nudge twice to navigate to the next FocusArea . Nudge shortcuts are useful when a FocusArea contains a long list followed by a Floating Action Button , as in the example below:

Nudge shortcut
Figure 3. Nudge shortcut

Without the nudge shortcut, the user would have to rotate through the entire list to reach the FAB.

Focus highlight customization

As noted above, RotaryService builds upon the Android framework's existing concept of view focus. When the user rotates and nudges, RotaryService moves the focus around, focusing one view and unfocusing another. In Android, when a view is focused, if the view:

  • has specified its own focus highlight, Android draws the view's focus highlight.
  • doesn't specify a focus highlight, and the default focus highlight is not disabled, Android draws the default focus highlight for the view.

Apps designed for touch usually don't specify the appropriate focus highlights.

The default focus highlight is provided by the Android framework and can be overridden by the OEM. App developers receive it when the theme they're using is derived from Theme.DeviceDefault .

For a consistent user experience, rely on the default focus highlight whenever possible. If you need a custom-shaped (for example, round or pill-shaped) focus highlight, or if you're using a theme not derived from Theme.DeviceDefault , use the car-ui-library resources to specify your own focus highlight for each view.

To specify a custom focus highlight for a view, change the background or foreground drawable of the view to a drawable that differs when the view is focused on. Typically, you'd change the background. The following drawable, if used as the background for a square view, produces a round focus highlight:

<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 ) Bold resource references in the sample above identify resources defined by the car-ui-library. The OEM overrides these to be consistent with the default focus highlight they specify. This ensures that the focus highlight color, stroke width, and so on don't change when the user navigates between a view with a custom focus highlight and a view with the default focus highlight. The last item is a ripple used for touch. Default values used for the bold resources appear as follows:

Default values for bold resources
Figure 4. Default values for bold resources

In addition, a custom focus highlight is called for when a button is given a solid background color to bring it to the user's attention, as in the example below. This can make the focus highlight difficult to see. In this situation, specify a custom focus highlight using secondary colors:

Solid background color
  • ( 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

उदाहरण के लिए:

Focused, not pressedFocused, pressed
Focused, not pressed Focused, pressed

Rotary scrolling

If your app uses RecyclerView s, you SHOULD use CarUiRecyclerView s instead. This ensures that your UI is consistent with others because an OEM's customization applies to all CarUiRecyclerView s.

If the elements in your list are all focusable, you needn't do anything else. Rotary navigation moves the focus through the elements in the list and the list scrolls to make the newly focused element visible.

( Android 11 QPR3, Android 11 Car, Android 12 )
If there is a mix of focusable and unfocusable elements, or if all the elements are unfocusable, you can enable rotary scrolling, which allows the user to use the rotary controller to gradually scroll through the list without skipping unfocusable items. To enable rotary scrolling, set the app:rotaryScrollEnabled attribute to true .

( Android 11 QPR3, Android 11 Car, Android 12 )
You can enable rotary scrolling in any scrollable view, including av CarUiRecyclerView , with the setRotaryScrollEnabled() method in CarUiUtils . If you do so, you need to:

  • Make the scrollable view focusable so that it can be focused on when none of its focusable descendant views are visible,
  • Disable the default focus highlight on the scrollable view by calling setDefaultFocusHighlightEnabled(false) so that the scrollable view doesn't appear to be focused,
  • Ensure that the scrollable view is focused on before its descendants by calling setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) .
  • Listen for MotionEvents with SOURCE_ROTARY_ENCODER and either AXIS_VSCROLL or AXIS_HSCROLL to indicate the distance to scroll and the direction (through the sign).

When rotary scrolling is enabled on a CarUiRecyclerView and the user rotates to an area where no focusable views are present, the scrollbar changes from gray to blue, as if to indicate the scrollbar is focused. You can implement a similar effect if you like.

The MotionEvents are the same as those generated by a scroll wheel on a mouse, except for the source.

Direct manipulation mode

Normally, nudges and rotation navigate through the user interface, while Center button presses take action, though this isn't always the case. For example, if a user wants to adjust the alarm volume, they might use the rotary controller to navigate to the volume slider, press the Center button, rotate the controller to adjust the alarm volume, and then press the Back button to return to navigation. This is referred to as direct manipulation (DM) mode. In this mode, the rotary controller is used to interact with the view directly rather than to navigate.

Implement DM in one of two ways. If you only need to handle rotation and the view you want to manipulate responds to ACTION_SCROLL_FORWARD and ACTION_SCROLL_BACKWARD AccessibilityEvent s appropriately, use the simple mechanism. Otherwise, use the advanced mechanism.

The simple mechanism is the only option in system windows; apps can use either mechanism.

Simple mechanism

( Android 11 QPR3, Android 11 Car, Android 12 )
Your app should call DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) . RotaryService recognizes when the user is in DM mode and enters DM mode when the user presses the Center button while a view is focused. When in DM mode, rotations perform ACTION_SCROLL_FORWARD or ACTION_SCROLL_BACKWARD and exits DM mode when the user presses the Back button. The simple mechanism toggles the selected state of the view when entering and exiting DM mode.

To provide a visual cue that the user is in DM mode, make your view appear different when selected. For example, change the background when android:state_selected is true .

Advanced mechanism

The app determines when RotaryService enters and exits DM mode. For a consistent user experience, pressing the Center button with a DM view focused should enter DM mode and the Back button should exit DM mode. If the Center button and/or nudge aren't used, they can be alternative ways to exit DM mode. For apps such as Maps, a button to represent DM can be used to enter DM mode.

To support advanced DM mode, a view:

  1. ( Android 11 QPR3, Android 11 Car, Android 12 ) MUST listen for a KEYCODE_DPAD_CENTER event to enter DM mode and listen for a KEYCODE_BACK event to exit DM mode, calling DirectManipulationHelper.enableDirectManipulationMode() in each case. To listen for these events, do one of the following:
    • Register an OnKeyListener .
    • or,
    • Extend the view and then override its dispatchKeyEvent() method.
  2. SHOULD listen for nudge events ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT , or KEYCODE_DPAD_RIGHT ) if the view should handle nudges.
  3. SHOULD listen to MotionEvent s and get rotation count in AXIS_SCROLL if the view wants to handle rotation. There are several ways to do this:
    1. Register an OnGenericMotionListener .
    2. Extend the view and override its dispatchTouchEvent() method.
  4. To avoid being stuck in DM mode, MUST exit DM mode when the Fragment or Activity the view belongs to is not interactive.
  5. SHOULD provide a visual cue to indicate that the view is in DM mode.

A sample of a custom view that uses DM mode to pan and zoom a map is provided below:

/** 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(); }

More examples can be found in the RotaryPlayground project.

ActivityView

When using an ActivityView:

  • The ActivityView should not be focusable.
  • ( Android 11 QPR3, Android 11 Car, deprecated in Android 11 )
    The contents of the ActivityView MUST contain a FocusParkingView as the first focusable view, and its app:shouldRestoreFocus attribute MUST be false .
  • The contents of the ActivityView should have no android:focusByDefault views.

For the user, ActivityViews should have no effect on navigation except that focus areas can't span ActivityViews. In other words, you can't have a single focus area that has content inside and outside an ActivityView . If you don't add any FocusAreas to your ActivityView , the root of the view hierarchy in the ActivityView is considered an implicit focus area.

Buttons that operate when held down

Most buttons cause some action when clicked. Some buttons operate when held down instead. For example, the Fast Forward and Rewind buttons typically operate when held down. To make such buttons support rotary, listen for KEYCODE_DPAD_CENTER KeyEvents as follows:

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;
});

In which mRunnable takes an action (such as rewinding) and schedules itself to be run after a delay.

Touch mode

Users can use a rotary controller to interact with the head unit in a car in two ways, either by using the rotary controller or by touching the screen. When using the rotary controller, one of the focusable views will be highlighted. When touching the screen, no focus highlight appears. The user can switch between these input modes at any time:

  • Rotary → touch. When the user touches the screen, the focus highlight disappears.
  • Touch → rotary. When the user nudges, rotates, or presses the Center button, the focus highlight appears.

The Back and Home buttons have no effect on the input mode.

Rotary piggybacks on Android's existing concept of touch mode . You can use View.isInTouchMode() to determine which input mode the user is using. You can use OnTouchModeChangeListener to listen for changes. While this can be used to customize your user interface for the current input mode, avoid any major changes as they can be disconcerting.

समस्या निवारण

In an app designed for touch, it's not uncommon to have nested focusable views. For example, there may be a FrameLayout around an ImageButton , both of which are focusable. This does no harm for touch but it can result in a poor user experience for rotary because the user must rotate the controller twice to move to the next interactive view. For a good user experience, Google recommends you make either the outer view or the inner view focusable, but not both.

If a button or switch loses focus when pressed through the rotary controller, one of these conditions may apply:

  • The button or switch is being disabled (briefly or indefinitely) due to the button being pressed. In either case, there are two ways to address this:
    • Leave the android:enabled state as true and use a custom state to gray out the button or switch as described in Custom State .
    • Use a container to surround the button or switch and make the container focusable instead of the button or switch. (The click listener must be on the container.)
  • The button or switch is being replaced. For example, the action taken when the button is pressed or the switch is toggled may trigger a refresh of the available actions causing new buttons to replace existing buttons. There are two ways to address this:
    • Instead of creating a new button or switch, set the icon and/or text of the existing button or switch.
    • As above, add a focusable container around the button or switch.

RotaryPlayground

RotaryPlayground is a reference app for rotary. Use it to learn how to integrate rotary features into your apps. RotaryPlayground is included in emulator builds and in builds for devices that run Android Automotive OS (AAOS).

  • RotaryPlayground repository: packages/apps/Car/tests/RotaryPlayground/
  • Versions: Android 11 QPR3, Android 11 Car, and Android 12

The RotaryPlayground app shows the following tabs on the left:

  • Cards. Test navigating around focus areas, skipping unfocusable elements and text input.
  • Direct Manipulation. Test widgets that support simple and advanced direct manipulation mode. This tab is specifically for direct manipulation within the app window.
  • Sys UI Manipulation. Test widgets that support direct manipulation in system windows where only simple direct manipulation mode is supported.
  • Grid. Test z-pattern rotary navigation with scrolling.
  • Notification. Test nudging in and out of heads-up notifications.
  • Scroll. Test scrolling through a mix of focusable and unfocusable content.
  • WebView. Test navigating through links in a WebView .
  • Custom FocusArea . Test FocusArea customization:
    • Wrap-around.
    • android:focusedByDefault and app:defaultFocus
    • .
    • Explicit nudge targets.
    • Nudge shortcuts.
    • FocusArea with no focusable views.