Twórz aplikacje

Poniższy materiał jest przeznaczony dla twórców aplikacji.

Aby Twoja aplikacja obsługiwała rotację, MUSISZ:

  1. Umieść FocusParkingView w odpowiednim układzie aktywności.
  2. Upewnij się, że widoki można (lub nie można) skupić.
  3. Użyj FocusArea s, aby objąć wszystkie widoki, na których można się skupić, z wyjątkiem FocusParkingView .

Każde z tych zadań opisano szczegółowo poniżej po skonfigurowaniu środowiska do tworzenia aplikacji obsługujących technologię rotacyjną.

Skonfiguruj kontroler obrotowy

Zanim zaczniesz tworzyć aplikacje obsługujące funkcję obrotową, potrzebujesz kontrolera obrotowego lub modułu zastępczego. Masz opcje opisane poniżej.

Emulator

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

Możesz także użyć aosp_car_x86_64-userdebug .

Aby uzyskać dostęp do emulowanego kontrolera obrotowego:

  1. Kliknij trzy kropki na dole paska narzędzi:

    Uzyskaj dostęp do emulowanego kontrolera obrotowego
    Rysunek 1. Dostęp do emulowanego kontrolera obrotowego
  2. Wybierz opcję Car obrotowy w rozszerzonym oknie sterowania:

    Wybierz opcję Samochód obrotowy
    Rysunek 2. Wybierz opcję Samochód obrotowy

Klawiatura USB

  • Podłącz klawiaturę USB do urządzenia z systemem Android Automotive OS (AAOS). W niektórych przypadkach uniemożliwia to wyświetlenie klawiatury ekranowej.
  • Użyj userdebug lub kompilacji eng .
  • Włącz filtrowanie kluczowych zdarzeń:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • W poniższej tabeli znajdziesz odpowiedni klucz dla każdej akcji:
    Klucz Akcja obrotowa
    Q Obróć przeciwnie do ruchu wskazówek zegara
    mi Obróć zgodnie z ruchem wskazówek zegara
    A Rusz w lewo
    D Przesuń w prawo
    W Podnieś się
    S Przesuń w dół
    F lub przecinek Środkowy przycisk
    R lub Esc Przycisk powrotu

Polecenia ADB

Za pomocą poleceń car_service można wstrzykiwać obrotowe zdarzenia wejściowe. Polecenia te można uruchamiać na urządzeniach z systemem Android Automotive OS (AAOS) lub na emulatorze.

polecenia car_service Wejście obrotowe
adb shell cmd car_service inject-rotary Obróć przeciwnie do ruchu wskazówek zegara
adb shell cmd car_service inject-rotary -c true Obróć zgodnie z ruchem wskazówek zegara
adb shell cmd car_service inject-rotary -dt 100 50 Obróć wielokrotnie w kierunku przeciwnym do ruchu wskazówek zegara (100 ms temu i 50 ms temu)
adb shell cmd car_service inject-key 282 Rusz w lewo
adb shell cmd car_service inject-key 283 Przesuń w prawo
adb shell cmd car_service inject-key 280 Podnieś się
adb shell cmd car_service inject-key 281 Przesuń w dół
adb shell cmd car_service inject-key 23 Kliknięcie środkowego przycisku
adb shell input keyevent inject-key 4 Kliknij przycisk Wstecz

Sterownik obrotowy OEM

Jest to najbardziej realistyczna opcja, gdy sprzęt kontrolera obrotowego jest uruchomiony. Jest to szczególnie przydatne do testowania szybkiego obrotu.

FocusParkingView

FocusParkingView to przezroczysty widok w bibliotece Car UI (car-ui-library) . RotaryService używa go do obsługi nawigacji za pomocą kontrolera obrotowego. FocusParkingView musi być pierwszym widokiem w układzie, na którym można ustawić fokus. Musi być umieszczony poza wszystkimi obszarami FocusArea . Każde okno musi mieć jeden FocusParkingView . Jeśli już korzystasz z układu podstawowego car-ui-library, który zawiera FocusParkingView , nie musisz dodawać kolejnego FocusParkingView . Poniżej pokazano przykład FocusParkingView w 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>

Oto powody, dla których potrzebujesz FocusParkingView :

  1. Android nie usuwa automatycznie fokusu, gdy fokus jest ustawiony w innym oknie. Jeśli spróbujesz usunąć fokus w poprzednim oknie, Android ponownie ustawi ostrość widoku w tym oknie, co spowoduje jednoczesne skupienie się na dwóch oknach. Dodanie FocusParkingView do każdego okna może rozwiązać ten problem. Ten widok jest przezroczysty, a jego domyślne podświetlenie fokusu jest wyłączone, dzięki czemu jest niewidoczne dla użytkownika, niezależnie od tego, czy jest on skupiony, czy nie. Może przejąć fokus, dzięki czemu RotaryService może ustawić na nim fokus w celu usunięcia podświetlenia fokusu.
  2. Jeśli w bieżącym oknie znajduje się tylko jeden FocusArea , obrócenie kontrolera w FocusArea powoduje, że RotaryService przenosi fokus z widoku po prawej stronie do widoku po lewej stronie (i odwrotnie). Dodanie tego widoku do każdego okna może rozwiązać problem. Gdy RotaryService ustali, że celem fokusu jest FocusParkingView , może określić, że wkrótce nastąpi zawijanie i w którym momencie unika zawijania, nie przesuwając fokusu.
  3. Gdy pokrętło uruchamia aplikację, system Android ustawia ostrość na pierwszym widoku, który można ustawić, czyli zawsze na widoku FocusParkingView . FocusParkingView określa optymalny widok, na którym należy się skupić, a następnie ustawia ostrość.

Możliwość skupienia widoków

RotaryService opiera się na istniejącej koncepcji skupienia widoku w środowisku Android, której początki sięgają czasów, gdy telefony miały fizyczne klawiatury i pady kierunkowe. Istniejący atrybut android:nextFocusForward został ponownie przystosowany do obsługi obrotowej (zobacz Dostosowywanie FocusArea ), ale android:nextFocusLeft , android:nextFocusRight , android:nextFocusUp i android:nextFocusDown nie są.

RotaryService skupia się wyłącznie na widokach, na których można się skupić. Niektóre widoki, takie jak Button s, zwykle można ustawić jako aktywne. Inne, takie jak TextView i ViewGroup , zwykle nie są. Klikalne widoki są automatycznie ustawiane jako aktywne, a widoki są automatycznie klikalne, gdy mają odbiornik kliknięcia. Jeśli ta automatyczna logika skutkuje żądaną możliwością skupienia, nie trzeba jawnie ustawiać możliwości skupiania widoku. Jeśli logika automatyczna nie zapewnia pożądanej możliwości skupienia, ustaw atrybut android:focusable na true lub false albo programowo ustaw możliwość skupienia widoku za pomocą View.setFocusable(boolean) . Aby RotaryService mogło się na tym skupić, widok MUSI spełniać następujące wymagania:

  • Możliwość ustawienia ostrości
  • Włączony
  • Widoczny
  • Mają niezerowe wartości szerokości i wysokości

Jeśli widok nie spełnia wszystkich tych wymagań, na przykład przycisk, na którym można ustawić fokus, ale jest wyłączony, użytkownik nie może użyć pokrętła, aby się na nim skupić. Jeśli chcesz skupić się na wyłączonych widokach, rozważ użycie stanu niestandardowego zamiast android:state_enabled aby kontrolować sposób wyświetlania widoku bez wskazywania, że ​​Android powinien uważać go za wyłączony. Twoja aplikacja może poinformować użytkownika, dlaczego po dotknięciu widok jest wyłączony. W następnej sekcji wyjaśniono, jak to zrobić.

Stan niestandardowy

Aby dodać stan niestandardowy:

  1. Aby dodać atrybut niestandardowy do widoku. Na przykład, aby dodać niestandardowy stan state_rotary_enabled do klasy widoku CustomView , użyj:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Aby śledzić ten stan, dodaj zmienną instancji do swojego widoku wraz z metodami dostępu:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. Aby odczytać wartość atrybutu podczas tworzenia widoku:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. W klasie widoku zastąp metodę onCreateDrawableState() , a następnie dodaj stan niestandardowy, jeśli to konieczne. Na przykład:
    @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. Spraw, aby moduł obsługi kliknięć w widoku działał inaczej w zależności od jego stanu. Na przykład moduł obsługi kliknięcia może nic nie robić lub może wyświetlić toast, gdy mRotaryEnabled ma false .
  6. Aby przycisk wyglądał na wyłączony, w tle widoku możesz rysować, użyj app:state_rotary_enabled zamiast android:state_enabled . Jeśli jeszcze go nie masz, musisz dodać:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Jeśli widok jest wyłączony w którymkolwiek układzie, zamień android:enabled="false" na app:state_rotary_enabled="false" , a następnie dodaj przestrzeń nazw app , jak powyżej.
  8. Jeśli Twój widok jest programowo wyłączony, zamień wywołania setEnabled() na wywołania setRotaryEnabled() .

Strefa zainteresowania

Użyj FocusAreas aby podzielić aktywne widoki na bloki, aby ułatwić nawigację i zachować spójność z innymi aplikacjami. Na przykład, jeśli aplikacja ma pasek narzędzi, powinien on znajdować się w oddzielnym obszarze FocusArea od reszty aplikacji. Paski zakładek i inne elementy nawigacyjne również powinny być oddzielone od reszty aplikacji. Duże listy powinny generalnie mieć swój własny FocusArea . Jeśli nie, użytkownicy muszą przeglądać całą listę, aby uzyskać dostęp do niektórych widoków.

FocusArea jest podklasą LinearLayout w bibliotece car-ui. Gdy ta funkcja jest włączona, FocusArea rysuje podświetlenie, gdy jeden z jego elementów podrzędnych jest skupiony. Aby dowiedzieć się więcej, zobacz Dostosowywanie podświetlenia fokusu .

Jeśli podczas tworzenia bloku nawigacyjnego w pliku układu zamierzasz używać LinearLayout jako kontenera dla tego bloku, zamiast tego użyj FocusArea . W przeciwnym razie zawiń blok w FocusArea .

NIE zagnieżdżaj FocusArea w innym FocusArea . Prowadzi to do niezdefiniowanego zachowania nawigacji. Upewnij się, że wszystkie widoki, na których można się skupić, są zagnieżdżone w FocusArea .

Przykład FocusArea w RotaryPlayground pokazano poniżej:

<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 działa w następujący sposób:

  1. Podczas obsługi akcji obracania i szturchania RotaryService szuka wystąpień FocusArea w hierarchii widoków.
  2. Po otrzymaniu zdarzenia rotacji RotaryService przenosi fokus do innego widoku, który może skupić się na tym samym FocusArea .
  3. Po otrzymaniu zdarzenia szturchnięcia RotaryService przenosi fokus do innego widoku, który może skupić się na innym (zazwyczaj sąsiadującym) FocusArea .

Jeśli w układzie nie uwzględnisz żadnych FocusAreas , widok główny będzie traktowany jako ukryty obszar fokusu. Użytkownik nie może naciskać, aby poruszać się po aplikacji. Zamiast tego będą się one obracać po wszystkich widokach, na których można się skupić, co może być odpowiednie w przypadku okien dialogowych.

Personalizacja obszaru FocusArea

Do dostosowania nawigacji obrotowej można użyć dwóch standardowych atrybutów widoku:

  • android:nextFocusForward umożliwia twórcom aplikacji określenie kolejności rotacji w obszarze fokusu. Jest to ten sam atrybut, który służy do kontrolowania kolejności tabulacji podczas nawigacji za pomocą klawiatury. NIE używaj tego atrybutu do tworzenia pętli. Zamiast tego użyj app:wrapAround (patrz poniżej), aby utworzyć pętlę.
  • android:focusedByDefault umożliwia twórcom aplikacji określenie domyślnego widoku fokusu w oknie. NIE używaj tego atrybutu i app:defaultFocus (patrz poniżej) w tym samym FocusArea .

FocusArea definiuje również pewne atrybuty umożliwiające dostosowanie nawigacji obrotowej. Za pomocą tych atrybutów nie można dostosowywać ukrytych obszarów fokusu.

  1. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    app:defaultFocus może służyć do określenia identyfikatora widoku potomnego, na którym można ustawić fokus, na którym należy się skupić, gdy użytkownik przejdzie do tego FocusArea .
  2. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    app:defaultFocusOverridesHistory można ustawić na true , aby widok określony powyżej był aktywny, nawet jeśli z historią wskazującą, że skupiono się na innym widoku w tym FocusArea .
  3. ( Android 12 )
    Użyj app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut i app:nudgeDownShortcut , aby określić identyfikator widoku potomnego, na którym można ustawić fokus, na którym powinien się skupiać, gdy użytkownik przesuwa w danym kierunku. Aby dowiedzieć się więcej, zapoznaj się z treścią skrótów przesuwania poniżej.

    ( Android 11 QPR3, Android 11 Car, przestarzałe w Androidzie 12 ) app:nudgeShortcut i app:nudgeShortcutDirection obsługiwały tylko jeden skrót szturchnięcia.

  4. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby umożliwić obrót w tym FocusArea , app:wrapAround można ustawić na true . Jest to najczęściej używane, gdy widoki są ułożone w okrąg lub owal.
  5. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby dostosować dopełnienie podświetlenia w tym FocusArea , użyj app:highlightPaddingStart , app:highlightPaddingEnd , app:highlightPaddingTop , app:highlightPaddingBottom , app:highlightPaddingHorizontal i app:highlightPaddingVertical .
  6. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby dostosować postrzegane granice tego FocusArea w celu znalezienia celu szturchnięcia, użyj app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , app:bottomBoundOffset , app:horizontalBoundOffset i app:verticalBoundOffset .
  7. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby jawnie określić identyfikator sąsiadującego FocusArea (lub obszarów) w podanych kierunkach, użyj app:nudgeLeft , app:nudgeRight , app:nudgeUp i app:nudgeDown . Użyj tej opcji, jeśli domyślnie używane wyszukiwanie geometryczne nie pozwala znaleźć żądanego celu.

Podsuwanie zwykle powoduje nawigację pomiędzy obszarami ostrości. Jednak w przypadku skrótów przesuwania przesuwanie czasami najpierw powoduje nawigację w obrębie FocusArea , więc może być konieczne dwukrotne szturchnięcie, aby przejść do następnego FocusArea . Skróty przesuwania są przydatne, gdy FocusArea zawiera długą listę, po której następuje pływający przycisk akcji , jak w poniższym przykładzie:

Przesuń skrót
Rysunek 3. Przesuń skrót

Bez skrótu szturchnięcia użytkownik musiałby przeglądać całą listę, aby dotrzeć do FAB.

Dostosowywanie podświetlenia ostrości

Jak wspomniano powyżej, RotaryService opiera się na istniejącej koncepcji skupienia widoku w środowisku Android. Kiedy użytkownik obraca się i szturcha, RotaryService przesuwa fokus, skupiając jeden widok i tracąc ostrość na innym. W systemie Android, gdy widok jest skupiony, jeśli widok:

  • Określił własne wyróżnienie fokusu, Android rysuje wyróżnienie fokusu widoku.
  • Nie określa podświetlenia fokusu, a domyślne wyróżnienie fokusu nie jest wyłączone, system Android rysuje domyślne wyróżnienie fokusu dla widoku.

Aplikacje przeznaczone do obsługi dotyku zwykle nie określają odpowiednich podświetleń.

Domyślne podświetlenie fokusu jest zapewniane przez platformę Android i może zostać zastąpione przez producenta OEM. Twórcy aplikacji otrzymują go, gdy motyw, którego używają, pochodzi z Theme.DeviceDefault .

Aby zapewnić spójne wrażenia użytkownika, w miarę możliwości polegaj na domyślnym podświetleniu fokusu. Jeśli potrzebujesz podświetlenia o niestandardowym kształcie (na przykład okrągłego lub pigułkowego) lub jeśli używasz motywu niepochodzącego z Theme.DeviceDefault , użyj zasobów car-ui-library, aby określić własne wyróżnienie fokusu każdy widok.

Aby określić niestandardowe podświetlenie widoku, zmień tło lub element rysunkowy pierwszego planu widoku na rysowalny, który różni się, gdy widok jest skupiony. Zwykle zmieniasz tło. Poniższy rysunek, jeśli zostanie użyty jako tło dla widoku kwadratowego, tworzy okrągłe podświetlenie:

<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 ) Pogrubione odniesienia do zasobów w powyższym przykładzie identyfikują zasoby zdefiniowane przez bibliotekę car-ui. Producent OEM zastępuje je, aby zachować zgodność z określonym przez nich domyślnym podświetleniem fokusu. Dzięki temu kolor podświetlenia fokusu, szerokość obrysu itd. nie zmienią się, gdy użytkownik będzie przechodzić między widokiem z niestandardowym podświetleniem fokusu a widokiem z domyślnym podświetleniem fokusu. Ostatnim elementem jest zmarszczka służąca do dotyku. Domyślne wartości użyte dla zasobów pogrubionych wyglądają następująco:

Domyślne wartości pogrubionych zasobów
Rysunek 4. Domyślne wartości pogrubionych zasobów

Ponadto niestandardowe wyróżnienie fokusu jest wymagane, gdy przycisk ma jednolity kolor tła, aby zwrócić na niego uwagę użytkownika, jak w przykładzie poniżej. Może to sprawić, że podkreślenie ostrości będzie trudne do zauważenia. W tej sytuacji określ niestandardowe wyróżnienie fokusu za pomocą kolorów dodatkowych :

Jednolity kolor tła
  • ( Android 11 QPR3, Android 11 Samochód, 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

Na przykład:

Skoncentrowany, a nie przyciśniętySkoncentrowany, przyciśnięty
Skoncentrowany, a nie przyciśnięty Skoncentrowany, przyciśnięty

Przewijanie obrotowe

Jeśli Twoja aplikacja używa RecyclerView s, zamiast tego powinieneś używać CarUiRecyclerView s. Gwarantuje to, że Twój interfejs użytkownika będzie spójny z innymi, ponieważ dostosowanie OEM dotyczy wszystkich CarUiRecyclerView .

Jeśli na wszystkich elementach listy można ustawić fokus, nie musisz robić nic więcej. Nawigacja obrotowa przesuwa fokus pomiędzy elementami na liście, a lista przewija się, aby nowo wybrany element był widoczny.

( Android 11 QPR3, Android 11 Samochód, Android 12 )
Jeśli występuje mieszanka elementów, na których można ustawić fokus i na których nie można ustawić ostrości, lub jeśli na wszystkich elementach nie można ustawić ostrości, można włączyć przewijanie obrotowe, co pozwala użytkownikowi używać kontrolera obrotowego do stopniowego przewijania listy bez pomijania elementów, na których nie można ustawić ostrości. Aby włączyć przewijanie obrotowe, ustaw atrybut app:rotaryScrollEnabled na true .

( Android 11 QPR3, Android 11 Samochód, Android 12 )
Możesz włączyć przewijanie obrotowe w dowolnym przewijanym widoku, w tym av CarUiRecyclerView , za pomocą metody setRotaryScrollEnabled() w CarUiUtils . Jeśli to zrobisz, musisz:

  • Skoncentruj przewijalny widok, aby można było się na nim skupić, gdy żaden z jego możliwych do skupienia widoków potomnych nie jest widoczny,
  • Wyłącz domyślne wyróżnienie fokusu w przewijanym widoku, wywołując setDefaultFocusHighlightEnabled(false) , aby przewijany widok nie wydawał się skupiony,
  • Upewnij się, że przewijany widok jest skupiony przed jego potomkami, wywołując setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) .
  • Nasłuchuj zdarzeń MotionEvents za pomocą SOURCE_ROTARY_ENCODER i AXIS_VSCROLL lub AXIS_HSCROLL , aby wskazać odległość do przewinięcia i kierunek (przez znak).

Gdy przewijanie obrotowe jest włączone w CarUiRecyclerView , a użytkownik obraca się do obszaru, w którym nie ma widoków, na których można ustawić fokus, pasek przewijania zmienia się z szarego na niebieski, jakby wskazywał, że pasek przewijania jest aktywny. Jeśli chcesz, możesz zastosować podobny efekt.

Zdarzenia MotionEvents są takie same, jak te generowane przez kółko przewijania myszy, z wyjątkiem źródła.

Tryb bezpośredniej manipulacji

Zwykle przesuwanie i obrót umożliwiają poruszanie się po interfejsie użytkownika, podczas gdy naciśnięcie środkowego przycisku powoduje wykonanie akcji, chociaż nie zawsze tak się dzieje. Na przykład, jeśli użytkownik chce dostosować głośność alarmu, może użyć kontrolera obrotowego, aby przejść do suwaka głośności, nacisnąć środkowy przycisk, obrócić kontroler, aby dostosować głośność alarmu, a następnie nacisnąć przycisk Wstecz, aby powrócić do nawigacji . Nazywa się to trybem bezpośredniej manipulacji (DM) . W tym trybie pokrętło służy do bezpośredniej interakcji z widokiem, a nie do nawigacji.

Zaimplementuj DM na jeden z dwóch sposobów. Jeśli potrzebujesz tylko obsłużyć obrót, a widok, którym chcesz manipulować, odpowiednio reaguje na AccessibilityEvent ACTION_SCROLL_FORWARD i ACTION_SCROLL_BACKWARD , użyj prostego mechanizmu. W przeciwnym razie skorzystaj z zaawansowanego mechanizmu.

Prosty mechanizm jest jedyną opcją w oknach systemowych; aplikacje mogą korzystać z obu mechanizmów.

Prosty mechanizm

( Android 11 QPR3, Android 11 Samochód, Android 12 )
Twoja aplikacja powinna wywołać DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) . RotaryService rozpoznaje, kiedy użytkownik znajduje się w trybie DM i przechodzi do trybu DM, gdy użytkownik naciśnie środkowy przycisk, gdy widok jest aktywny. W trybie DM obroty wykonują ACTION_SCROLL_FORWARD lub ACTION_SCROLL_BACKWARD i opuszczają tryb DM, gdy użytkownik naciśnie przycisk Wstecz. Prosty mechanizm przełącza wybrany stan widoku podczas wchodzenia i wychodzenia z trybu DM.

Aby zapewnić wizualną wskazówkę, że użytkownik znajduje się w trybie DM, po wybraniu zmień wygląd widoku. Na przykład zmień tło, gdy android:state_selected ma true .

Zaawansowany mechanizm

Aplikacja określa, kiedy RotaryService wchodzi i wychodzi z trybu DM. Aby zapewnić spójne doświadczenie użytkownika, naciśnięcie środkowego przycisku przy widoku DM powinno spowodować przejście do trybu DM, a przycisk Wstecz powinien zakończyć tryb DM. Jeśli środkowy przycisk i/lub szturchnięcie nie są używane, mogą to być alternatywne sposoby wyjścia z trybu DM. W przypadku aplikacji takich jak Mapy do przejścia do trybu DM można użyć przycisku reprezentującego DM.

Aby obsługiwać zaawansowany tryb DM, widok:

  1. ( Android 11 QPR3, Android 11 Car, Android 12 ) MUSI nasłuchiwać zdarzenia KEYCODE_DPAD_CENTER , aby wejść w tryb DM i nasłuchiwać zdarzenia KEYCODE_BACK , aby wyjść z trybu DM, wywołując w każdym przypadku DirectManipulationHelper.enableDirectManipulationMode() . Aby nasłuchiwać tych zdarzeń, wykonaj jedną z następujących czynności:
    • Zarejestruj OnKeyListener .
    • Lub,
    • Rozszerz widok, a następnie zastąp jego metodę dispatchKeyEvent() .
  2. POWINNO nasłuchiwać zdarzeń szturchnięć ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT lub KEYCODE_DPAD_RIGHT ), jeśli widok powinien obsługiwać szturchnięcia.
  3. POWINNO nasłuchiwać zdarzeń MotionEvent i uzyskać liczbę rotacji w AXIS_SCROLL , jeśli widok chce obsłużyć obrót. Można to zrobić na kilka sposobów:
    1. Zarejestruj OnGenericMotionListener .
    2. Rozszerz widok i zastąp jego metodę dispatchTouchEvent() .
  4. Aby uniknąć utknięcia w trybie DM, MUSISZ wyjść z trybu DM, gdy fragment lub działanie, do którego należy widok, nie jest interaktywne.
  5. POWINNO zapewniać wizualną wskazówkę wskazującą, że widok jest w trybie DM.

Poniżej znajduje się przykład widoku niestandardowego, który wykorzystuje tryb DM do przesuwania i powiększania mapy:

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

Więcej przykładów można znaleźć w projekcie RotaryPlayground .

Widok aktywności

Podczas korzystania z ActivityView:

  • ActivityView nie powinien umożliwiać fokusu.
  • ( Android 11 QPR3, Android 11 Car, przestarzałe w Androidzie 11 )
    Zawartość ActivityView MUSI zawierać FocusParkingView jako pierwszy widok, na którym można się skupić, a jego app:shouldRestoreFocus MUSI mieć wartość false .
  • Zawartość ActivityView nie powinna zawierać widoków android:focusByDefault .

Dla użytkownika Widoki Aktywności nie powinny mieć żadnego wpływu na nawigację, z wyjątkiem tego, że obszary fokusu nie mogą obejmować Widoków Aktywności. Innymi słowy, nie można mieć jednego obszaru skupienia zawierającego treść wewnątrz i na zewnątrz ActivityView . Jeśli nie dodasz żadnego FocusAreas do ActivityView , katalog główny hierarchii widoków w ActivityView będzie uważany za niejawny obszar fokusu.

Przyciski działające po przytrzymaniu

Większość przycisków powoduje pewne działanie po kliknięciu. Zamiast tego niektóre przyciski działają po ich przytrzymaniu. Na przykład przyciski szybkiego przewijania do przodu i przewijania do tyłu zazwyczaj działają, gdy są przytrzymywane. Aby takie przyciski obsługiwały obroty, nasłuchuj KeyEvents KEYCODE_DPAD_CENTER w następujący sposób:

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

W którym mRunnable podejmuje akcję (taką jak przewijanie) i planuje uruchomienie się z opóźnieniem.

Tryb dotykowy

Użytkownicy mogą używać kontrolera obrotowego do interakcji z radioodtwarzaczem w samochodzie na dwa sposoby: za pomocą kontrolera obrotowego lub dotykając ekranu. Podczas korzystania z pokrętła podświetlony jest jeden z widoków, na których można ustawić ostrość. Po dotknięciu ekranu nie pojawia się podświetlenie ostrości. Użytkownik może w dowolnym momencie przełączać się pomiędzy trybami wprowadzania danych:

  • Obróć → dotknij. Gdy użytkownik dotknie ekranu, podświetlenie ostrości znika.
  • Dotknij → obrotowy. Gdy użytkownik szturchnie, obróci lub naciśnie środkowy przycisk, pojawi się podświetlenie fokusu.

Przyciski Wstecz i Strona główna nie mają wpływu na tryb wprowadzania.

Rotary opiera się na istniejącej koncepcji trybu dotykowego Androida. Za pomocą View.isInTouchMode() można określić, jakiego trybu wprowadzania danych używa użytkownik. Do nasłuchiwania zmian możesz użyć OnTouchModeChangeListener . Chociaż można to wykorzystać do dostosowania interfejsu użytkownika do bieżącego trybu wprowadzania, należy unikać większych zmian, ponieważ mogą one wprowadzać w błąd.

Rozwiązywanie problemów

W aplikacjach przeznaczonych do obsługi dotyku często stosuje się zagnieżdżone widoki, na których można się skupiać. Na przykład wokół ImageButton może znajdować się FrameLayout , z których oba można ustawić. Nie szkodzi to dotykowi, ale może powodować pogorszenie komfortu korzystania z funkcji obrotowej, ponieważ użytkownik musi dwukrotnie obrócić kontroler, aby przejść do następnego widoku interaktywnego. Aby zapewnić wygodę użytkownika, Google zaleca ustawienie skupienia widoku zewnętrznego lub widoku wewnętrznego, ale nie obu.

Jeśli przycisk lub przełącznik straci ostrość po naciśnięciu za pomocą pokrętła, może mieć zastosowanie jeden z poniższych warunków:

  • Przycisk lub przełącznik jest blokowany (na krótko lub na czas nieokreślony) w wyniku naciśnięcia przycisku. W obu przypadkach można temu zaradzić na dwa sposoby:
    • Pozostaw stan android:enabled jako true i użyj stanu niestandardowego, aby wyszarzić przycisk lub przełączyć, jak opisano w Custom State .
    • Użyj kontenera, aby otoczyć przycisk lub przełącznik i ustawić kontener, na którym można się skupić zamiast przycisku lub przełącznika. (Odbiornik kliknięcia musi znajdować się w kontenerze.)
  • Przycisk lub przełącznik jest wymieniany. Na przykład akcja wykonywana po naciśnięciu przycisku lub przełączeniu przełącznika może spowodować odświeżenie dostępnych akcji, powodując zastąpienie istniejących przycisków nowymi. Można temu zaradzić na dwa sposoby:
    • Zamiast tworzyć nowy przycisk lub przełącznik, ustaw ikonę i/lub tekst istniejącego przycisku lub przełącznika.
    • Jak powyżej, dodaj możliwy do skupienia kontener wokół przycisku lub przełącznika.

Obrotowy plac zabaw

RotaryPlayground to aplikacja referencyjna dla urządzeń obrotowych. Skorzystaj z niego, aby dowiedzieć się, jak zintegrować funkcje obrotowe ze swoimi aplikacjami. RotaryPlayground jest zawarty w kompilacjach emulatorów i kompilacjach dla urządzeń z systemem operacyjnym Android Automotive OS (AAOS).

  • Repozytorium RotaryPlayground : packages/apps/Car/tests/RotaryPlayground/
  • Wersje: Android 11 QPR3, Android 11 Car i Android 12

Aplikacja RotaryPlayground wyświetla po lewej stronie następujące karty:

  • Karty. Przetestuj poruszanie się po obszarach fokusu, pomijanie elementów, na których nie można ustawić ostrości, oraz wprowadzanie tekstu.
  • Bezpośrednia manipulacja. Testuj widżety obsługujące prosty i zaawansowany tryb bezpośredniej manipulacji. Ta zakładka służy specjalnie do bezpośredniej manipulacji w oknie aplikacji.
  • Manipulacja interfejsem użytkownika systemu. Testuj widżety obsługujące bezpośrednią manipulację w oknach systemowych, w których obsługiwany jest tylko prosty tryb bezpośredniej manipulacji.
  • Siatka. Przetestuj nawigację obrotową według wzoru Z z przewijaniem.
  • Powiadomienie. Przetestuj włączanie i wyłączanie powiadomień heads-up.
  • Zwój. Przetestuj przewijanie mieszanki treści, na których można się skupić i których nie można skupić.
  • Widok sieciowy. Przetestuj nawigację po łączach w WebView .
  • Niestandardowy FocusArea . Przetestuj dostosowywanie FocusArea :
    • Owinąć.
    • android:focusedByDefault i app:defaultFocus
    • .
    • Wyraźne cele szturchania.
    • Poruszaj skrótami.
    • FocusArea bez widoków, na których można się skupić.
,

Poniższy materiał jest przeznaczony dla twórców aplikacji.

Aby Twoja aplikacja obsługiwała rotację, MUSISZ:

  1. Umieść FocusParkingView w odpowiednim układzie aktywności.
  2. Upewnij się, że widoki można (lub nie można) skupić.
  3. Użyj FocusArea s, aby objąć wszystkie widoki, na których można się skupić, z wyjątkiem FocusParkingView .

Każde z tych zadań opisano szczegółowo poniżej po skonfigurowaniu środowiska do tworzenia aplikacji obsługujących technologię rotacyjną.

Skonfiguruj kontroler obrotowy

Zanim zaczniesz tworzyć aplikacje obsługujące funkcję obrotową, potrzebujesz kontrolera obrotowego lub modułu zastępczego. Masz opcje opisane poniżej.

Emulator

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

Możesz także użyć aosp_car_x86_64-userdebug .

Aby uzyskać dostęp do emulowanego kontrolera obrotowego:

  1. Kliknij trzy kropki na dole paska narzędzi:

    Uzyskaj dostęp do emulowanego kontrolera obrotowego
    Rysunek 1. Dostęp do emulowanego kontrolera obrotowego
  2. Wybierz opcję Car obrotowy w rozszerzonym oknie sterowania:

    Wybierz opcję Samochód obrotowy
    Rysunek 2. Wybierz opcję Samochód obrotowy

Klawiatura USB

  • Podłącz klawiaturę USB do urządzenia z systemem Android Automotive OS (AAOS). W niektórych przypadkach uniemożliwia to wyświetlenie klawiatury ekranowej.
  • Użyj userdebug lub kompilacji eng .
  • Włącz filtrowanie kluczowych zdarzeń:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • W poniższej tabeli znajdziesz odpowiedni klucz dla każdej akcji:
    Klucz Akcja obrotowa
    Q Obróć przeciwnie do ruchu wskazówek zegara
    mi Obróć zgodnie z ruchem wskazówek zegara
    A Rusz w lewo
    D Przesuń w prawo
    W Podnieś się
    S Przesuń w dół
    F lub przecinek Środkowy przycisk
    R lub Esc Przycisk powrotu

Polecenia ADB

Za pomocą poleceń car_service można wstrzykiwać obrotowe zdarzenia wejściowe. Polecenia te można uruchamiać na urządzeniach z systemem Android Automotive OS (AAOS) lub na emulatorze.

polecenia car_service Wejście obrotowe
adb shell cmd car_service inject-rotary Obróć przeciwnie do ruchu wskazówek zegara
adb shell cmd car_service inject-rotary -c true Obróć zgodnie z ruchem wskazówek zegara
adb shell cmd car_service inject-rotary -dt 100 50 Obróć wielokrotnie w kierunku przeciwnym do ruchu wskazówek zegara (100 ms temu i 50 ms temu)
adb shell cmd car_service inject-key 282 Rusz w lewo
adb shell cmd car_service inject-key 283 Przesuń w prawo
adb shell cmd car_service inject-key 280 Podnieś się
adb shell cmd car_service inject-key 281 Przesuń w dół
adb shell cmd car_service inject-key 23 Kliknięcie środkowego przycisku
adb shell input keyevent inject-key 4 Kliknij przycisk Wstecz

Sterownik obrotowy OEM

Jest to najbardziej realistyczna opcja, gdy sprzęt kontrolera obrotowego jest uruchomiony. Jest to szczególnie przydatne do testowania szybkiego obrotu.

FocusParkingView

FocusParkingView to przezroczysty widok w bibliotece Car UI (car-ui-library) . RotaryService używa go do obsługi nawigacji za pomocą kontrolera obrotowego. FocusParkingView musi być pierwszym widokiem w układzie, na którym można ustawić fokus. Musi być umieszczony poza wszystkimi obszarami FocusArea . Każde okno musi mieć jeden FocusParkingView . Jeśli już korzystasz z układu podstawowego car-ui-library, który zawiera FocusParkingView , nie musisz dodawać kolejnego FocusParkingView . Poniżej pokazano przykład FocusParkingView w 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>

Oto powody, dla których potrzebujesz FocusParkingView :

  1. Android nie usuwa automatycznie fokusu, gdy fokus jest ustawiony w innym oknie. Jeśli spróbujesz usunąć fokus w poprzednim oknie, Android ponownie ustawi ostrość widoku w tym oknie, co spowoduje jednoczesne skupienie się na dwóch oknach. Dodanie FocusParkingView do każdego okna może rozwiązać ten problem. Ten widok jest przezroczysty, a jego domyślne podświetlenie fokusu jest wyłączone, dzięki czemu jest niewidoczne dla użytkownika, niezależnie od tego, czy jest on skupiony, czy nie. Może przejąć fokus, dzięki czemu RotaryService może ustawić na nim fokus w celu usunięcia podświetlenia fokusu.
  2. Jeśli w bieżącym oknie znajduje się tylko jeden FocusArea , obrócenie kontrolera w FocusArea powoduje, że RotaryService przenosi fokus z widoku po prawej stronie do widoku po lewej stronie (i odwrotnie). Dodanie tego widoku do każdego okna może rozwiązać problem. Gdy RotaryService ustali, że celem fokusu jest FocusParkingView , może określić, że wkrótce nastąpi zawijanie i w którym momencie unika zawijania, nie przesuwając fokusu.
  3. Gdy pokrętło uruchamia aplikację, system Android ustawia ostrość na pierwszym widoku, który można ustawić, czyli zawsze na widoku FocusParkingView . FocusParkingView określa optymalny widok, na którym należy się skupić, a następnie ustawia ostrość.

Możliwość skupienia widoków

RotaryService opiera się na istniejącej koncepcji skupienia widoku w środowisku Android, której początki sięgają czasów, gdy telefony miały fizyczne klawiatury i pady kierunkowe. Istniejący atrybut android:nextFocusForward został ponownie przystosowany do obsługi obrotowej (zobacz Dostosowywanie FocusArea ), ale android:nextFocusLeft , android:nextFocusRight , android:nextFocusUp i android:nextFocusDown nie są.

RotaryService skupia się wyłącznie na widokach, na których można się skupić. Niektóre widoki, takie jak Button s, zwykle można ustawić jako aktywne. Inne, takie jak TextView i ViewGroup , zwykle nie są. Klikalne widoki są automatycznie ustawiane jako aktywne, a widoki są automatycznie klikalne, gdy mają odbiornik kliknięcia. Jeśli ta automatyczna logika skutkuje żądaną możliwością skupienia, nie trzeba jawnie ustawiać możliwości skupiania widoku. Jeśli logika automatyczna nie zapewnia pożądanej możliwości skupienia, ustaw atrybut android:focusable na true lub false albo programowo ustaw możliwość skupienia widoku za pomocą View.setFocusable(boolean) . Aby RotaryService mogło się na tym skupić, widok MUSI spełniać następujące wymagania:

  • Możliwość ustawienia ostrości
  • Włączony
  • Widoczny
  • Mają niezerowe wartości szerokości i wysokości

Jeśli widok nie spełnia wszystkich tych wymagań, na przykład przycisk, na którym można ustawić fokus, ale jest wyłączony, użytkownik nie może użyć pokrętła, aby się na nim skupić. Jeśli chcesz skupić się na wyłączonych widokach, rozważ użycie stanu niestandardowego zamiast android:state_enabled aby kontrolować sposób wyświetlania widoku bez wskazywania, że ​​Android powinien uważać go za wyłączony. Twoja aplikacja może poinformować użytkownika, dlaczego po dotknięciu widok jest wyłączony. W następnej sekcji wyjaśniono, jak to zrobić.

Stan niestandardowy

Aby dodać stan niestandardowy:

  1. Aby dodać atrybut niestandardowy do widoku. Na przykład, aby dodać niestandardowy stan state_rotary_enabled do klasy widoku CustomView , użyj:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Aby śledzić ten stan, dodaj zmienną instancji do swojego widoku wraz z metodami dostępu:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. Aby odczytać wartość atrybutu podczas tworzenia widoku:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. W klasie widoku zastąp metodę onCreateDrawableState() , a następnie dodaj stan niestandardowy, jeśli to konieczne. Na przykład:
    @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. Spraw, aby moduł obsługi kliknięć w widoku działał inaczej w zależności od jego stanu. Na przykład moduł obsługi kliknięcia może nic nie robić lub może wyświetlić toast, gdy mRotaryEnabled ma false .
  6. Aby przycisk wyglądał na wyłączony, w tle widoku możesz rysować, użyj app:state_rotary_enabled zamiast android:state_enabled . Jeśli jeszcze go nie masz, musisz dodać:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Jeśli widok jest wyłączony w którymkolwiek układzie, zamień android:enabled="false" na app:state_rotary_enabled="false" , a następnie dodaj przestrzeń nazw app , jak powyżej.
  8. Jeśli Twój widok jest programowo wyłączony, zamień wywołania setEnabled() na wywołania setRotaryEnabled() .

Strefa zainteresowania

Użyj FocusAreas aby podzielić aktywne widoki na bloki, aby ułatwić nawigację i zachować spójność z innymi aplikacjami. Na przykład, jeśli aplikacja ma pasek narzędzi, powinien on znajdować się w oddzielnym obszarze FocusArea od reszty aplikacji. Paski zakładek i inne elementy nawigacyjne również powinny być oddzielone od reszty aplikacji. Duże listy powinny generalnie mieć swój własny FocusArea . Jeśli nie, użytkownicy muszą przeglądać całą listę, aby uzyskać dostęp do niektórych widoków.

FocusArea jest podklasą LinearLayout w bibliotece car-ui. Gdy ta funkcja jest włączona, FocusArea rysuje podświetlenie, gdy jeden z jego elementów podrzędnych jest skupiony. Aby dowiedzieć się więcej, zobacz Dostosowywanie podświetlenia fokusu .

Jeśli podczas tworzenia bloku nawigacyjnego w pliku układu zamierzasz używać LinearLayout jako kontenera dla tego bloku, zamiast tego użyj FocusArea . W przeciwnym razie zawiń blok w FocusArea .

NIE zagnieżdżaj FocusArea w innym FocusArea . Prowadzi to do niezdefiniowanego zachowania nawigacji. Upewnij się, że wszystkie widoki, na których można się skupić, są zagnieżdżone w FocusArea .

Przykład FocusArea w RotaryPlayground pokazano poniżej:

<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 działa w następujący sposób:

  1. Podczas obsługi akcji obracania i szturchania RotaryService szuka wystąpień FocusArea w hierarchii widoków.
  2. Po otrzymaniu zdarzenia rotacji RotaryService przenosi fokus do innego widoku, który może skupić się na tym samym FocusArea .
  3. Po otrzymaniu zdarzenia szturchnięcia RotaryService przenosi fokus do innego widoku, który może skupić się na innym (zazwyczaj sąsiadującym) FocusArea .

Jeśli w układzie nie uwzględnisz żadnych FocusAreas , widok główny będzie traktowany jako ukryty obszar fokusu. Użytkownik nie może naciskać, aby poruszać się po aplikacji. Zamiast tego będą się one obracać po wszystkich widokach, na których można się skupić, co może być odpowiednie w przypadku okien dialogowych.

Personalizacja obszaru FocusArea

Do dostosowania nawigacji obrotowej można użyć dwóch standardowych atrybutów widoku:

  • android:nextFocusForward umożliwia twórcom aplikacji określenie kolejności rotacji w obszarze fokusu. Jest to ten sam atrybut, który służy do kontrolowania kolejności tabulacji podczas nawigacji za pomocą klawiatury. NIE używaj tego atrybutu do tworzenia pętli. Zamiast tego użyj app:wrapAround (patrz poniżej), aby utworzyć pętlę.
  • android:focusedByDefault umożliwia twórcom aplikacji określenie domyślnego widoku fokusu w oknie. NIE używaj tego atrybutu i app:defaultFocus (patrz poniżej) w tym samym FocusArea .

FocusArea definiuje również pewne atrybuty umożliwiające dostosowanie nawigacji obrotowej. Za pomocą tych atrybutów nie można dostosowywać ukrytych obszarów fokusu.

  1. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    app:defaultFocus może służyć do określenia identyfikatora widoku potomnego, na którym można ustawić fokus, na którym należy się skupić, gdy użytkownik przejdzie do tego FocusArea .
  2. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    app:defaultFocusOverridesHistory można ustawić na true , aby widok określony powyżej był aktywny, nawet jeśli z historią wskazującą, że skupiono się na innym widoku w tym FocusArea .
  3. ( Android 12 )
    Użyj app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut i app:nudgeDownShortcut , aby określić identyfikator widoku potomnego, na którym można ustawić fokus, na którym powinien się skupiać, gdy użytkownik przesuwa w danym kierunku. Aby dowiedzieć się więcej, zapoznaj się z treścią skrótów przesuwania poniżej.

    ( Android 11 QPR3, Android 11 Car, przestarzałe w Androidzie 12 ) app:nudgeShortcut i app:nudgeShortcutDirection obsługiwały tylko jeden skrót szturchnięcia.

  4. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby umożliwić obrót w tym FocusArea , app:wrapAround można ustawić na true . Jest to najczęściej używane, gdy widoki są ułożone w okrąg lub owal.
  5. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby dostosować dopełnienie podświetlenia w tym FocusArea , użyj app:highlightPaddingStart , app:highlightPaddingEnd , app:highlightPaddingTop , app:highlightPaddingBottom , app:highlightPaddingHorizontal i app:highlightPaddingVertical .
  6. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby dostosować postrzegane granice tego FocusArea w celu znalezienia celu szturchnięcia, użyj app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , app:bottomBoundOffset , app:horizontalBoundOffset i app:verticalBoundOffset .
  7. ( Android 11 QPR3, Android 11 Samochód, Android 12 )
    Aby jawnie określić identyfikator sąsiadującego FocusArea (lub obszarów) w podanych kierunkach, użyj app:nudgeLeft , app:nudgeRight , app:nudgeUp i app:nudgeDown . Użyj tej opcji, jeśli domyślnie używane wyszukiwanie geometryczne nie pozwala znaleźć żądanego celu.

Podsuwanie zwykle powoduje nawigację pomiędzy obszarami ostrości. Jednak w przypadku skrótów przesuwania przesuwanie czasami najpierw powoduje nawigację w obrębie FocusArea , więc może być konieczne dwukrotne szturchnięcie, aby przejść do następnego FocusArea . Skróty przesuwania są przydatne, gdy FocusArea zawiera długą listę, po której następuje pływający przycisk akcji , jak w poniższym przykładzie:

Przesuń skrót
Rysunek 3. Przesuń skrót

Bez skrótu szturchnięcia użytkownik musiałby przeglądać całą listę, aby dotrzeć do FAB.

Dostosowywanie podświetlenia ostrości

Jak wspomniano powyżej, RotaryService opiera się na istniejącej koncepcji skupienia widoku w środowisku Android. Kiedy użytkownik obraca się i szturcha, RotaryService przesuwa fokus, skupiając jeden widok i tracąc ostrość na innym. W systemie Android, gdy widok jest skupiony, jeśli widok:

  • Określił własne wyróżnienie fokusu, Android rysuje wyróżnienie fokusu widoku.
  • Nie określa podświetlenia fokusu, a domyślne wyróżnienie fokusu nie jest wyłączone, system Android rysuje domyślne wyróżnienie fokusu dla widoku.

Aplikacje przeznaczone do obsługi dotyku zwykle nie określają odpowiednich podświetleń.

Domyślne podświetlenie fokusu jest zapewniane przez platformę Android i może zostać zastąpione przez producenta OEM. Twórcy aplikacji otrzymują go, gdy motyw, którego używają, pochodzi z Theme.DeviceDefault .

Aby zapewnić spójne wrażenia użytkownika, w miarę możliwości polegaj na domyślnym podświetleniu fokusu. Jeśli potrzebujesz podświetlenia o niestandardowym kształcie (na przykład okrągłego lub pigułkowego) lub jeśli używasz motywu niepochodzącego z Theme.DeviceDefault , użyj zasobów car-ui-library, aby określić własne wyróżnienie fokusu każdy widok.

Aby określić niestandardowe podświetlenie widoku, zmień tło lub element rysunkowy pierwszego planu widoku na rysowalny, który różni się, gdy widok jest skupiony. Zwykle zmieniasz tło. Poniższy rysunek, jeśli zostanie użyty jako tło dla widoku kwadratowego, tworzy okrągłe podświetlenie:

<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 ) Pogrubione odniesienia do zasobów w powyższym przykładzie identyfikują zasoby zdefiniowane przez bibliotekę car-ui. Producent OEM zastępuje je, aby zachować zgodność z określonym przez nich domyślnym podświetleniem fokusu. Dzięki temu kolor podświetlenia fokusu, szerokość obrysu itd. nie zmienią się, gdy użytkownik będzie przechodzić między widokiem z niestandardowym podświetleniem fokusu a widokiem z domyślnym podświetleniem fokusu. Ostatnim elementem jest zmarszczka służąca do dotyku. Domyślne wartości użyte dla zasobów pogrubionych wyglądają następująco:

Domyślne wartości pogrubionych zasobów
Rysunek 4. Domyślne wartości pogrubionych zasobów

Ponadto niestandardowe wyróżnienie fokusu jest wymagane, gdy przycisk ma jednolity kolor tła, aby zwrócić na niego uwagę użytkownika, jak w przykładzie poniżej. Może to sprawić, że podkreślenie ostrości będzie trudne do zauważenia. W tej sytuacji określ niestandardowe wyróżnienie fokusu za pomocą kolorów dodatkowych :

Jednolity kolor tła
  • ( Android 11 QPR3, Android 11 Samochód, 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

Na przykład:

Skoncentrowany, a nie przyciśniętySkoncentrowany, przyciśnięty
Skoncentrowany, a nie przyciśnięty Skoncentrowany, przyciśnięty

Przewijanie obrotowe

Jeśli Twoja aplikacja używa RecyclerView s, zamiast tego powinieneś używać CarUiRecyclerView s. Gwarantuje to, że Twój interfejs użytkownika będzie spójny z innymi, ponieważ dostosowanie OEM dotyczy wszystkich CarUiRecyclerView .

Jeśli na wszystkich elementach listy można ustawić fokus, nie musisz robić nic więcej. Nawigacja obrotowa przesuwa fokus pomiędzy elementami na liście, a lista przewija się, aby nowo wybrany element był widoczny.

( Android 11 QPR3, Android 11 Samochód, Android 12 )
Jeśli występuje mieszanka elementów, na których można ustawić fokus i na których nie można ustawić ostrości, lub jeśli na wszystkich elementach nie można ustawić ostrości, można włączyć przewijanie obrotowe, co pozwala użytkownikowi używać kontrolera obrotowego do stopniowego przewijania listy bez pomijania elementów, na których nie można ustawić ostrości. Aby włączyć przewijanie obrotowe, ustaw atrybut app:rotaryScrollEnabled na true .

( Android 11 QPR3, Android 11 Samochód, Android 12 )
Możesz włączyć przewijanie obrotowe w dowolnym przewijanym widoku, w tym av CarUiRecyclerView , za pomocą metody setRotaryScrollEnabled() w CarUiUtils . Jeśli to zrobisz, musisz:

  • Skoncentruj przewijalny widok, aby można było się na nim skupić, gdy żaden z jego możliwych do skupienia widoków potomnych nie jest widoczny,
  • Wyłącz domyślne wyróżnienie fokusu w przewijanym widoku, wywołując setDefaultFocusHighlightEnabled(false) , aby przewijany widok nie wydawał się skupiony,
  • Upewnij się, że przewijany widok jest skupiony przed jego potomkami, wywołując setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) .
  • Nasłuchuj zdarzeń MotionEvents za pomocą SOURCE_ROTARY_ENCODER i AXIS_VSCROLL lub AXIS_HSCROLL , aby wskazać odległość do przewinięcia i kierunek (przez znak).

Gdy przewijanie obrotowe jest włączone w CarUiRecyclerView , a użytkownik obraca się do obszaru, w którym nie ma widoków, na których można ustawić fokus, pasek przewijania zmienia się z szarego na niebieski, jakby wskazywał, że pasek przewijania jest aktywny. Jeśli chcesz, możesz zastosować podobny efekt.

Zdarzenia MotionEvents są takie same, jak te generowane przez kółko przewijania myszy, z wyjątkiem źródła.

Tryb bezpośredniej manipulacji

Zwykle przesuwanie i obrót umożliwiają poruszanie się po interfejsie użytkownika, podczas gdy naciśnięcie środkowego przycisku powoduje wykonanie akcji, chociaż nie zawsze tak się dzieje. Na przykład, jeśli użytkownik chce dostosować głośność alarmu, może użyć kontrolera obrotowego, aby przejść do suwaka głośności, nacisnąć środkowy przycisk, obrócić kontroler, aby dostosować głośność alarmu, a następnie nacisnąć przycisk Wstecz, aby powrócić do nawigacji . Nazywa się to trybem bezpośredniej manipulacji (DM) . W tym trybie pokrętło służy do bezpośredniej interakcji z widokiem, a nie do nawigacji.

Zaimplementuj DM na jeden z dwóch sposobów. Jeśli potrzebujesz tylko obsłużyć obrót, a widok, którym chcesz manipulować, odpowiednio reaguje na AccessibilityEvent ACTION_SCROLL_FORWARD i ACTION_SCROLL_BACKWARD , użyj prostego mechanizmu. W przeciwnym razie skorzystaj z zaawansowanego mechanizmu.

Prosty mechanizm jest jedyną opcją w oknach systemowych; aplikacje mogą korzystać z obu mechanizmów.

Prosty mechanizm

( Android 11 QPR3, Android 11 Samochód, Android 12 )
Twoja aplikacja powinna wywołać DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) . RotaryService rozpoznaje, kiedy użytkownik znajduje się w trybie DM i przechodzi do trybu DM, gdy użytkownik naciśnie środkowy przycisk, gdy widok jest aktywny. W trybie DM obroty wykonują ACTION_SCROLL_FORWARD lub ACTION_SCROLL_BACKWARD i opuszczają tryb DM, gdy użytkownik naciśnie przycisk Wstecz. Prosty mechanizm przełącza wybrany stan widoku podczas wchodzenia i wychodzenia z trybu DM.

Aby zapewnić wizualną wskazówkę, że użytkownik znajduje się w trybie DM, po wybraniu zmień wygląd widoku. Na przykład zmień tło, gdy android:state_selected ma true .

Zaawansowany mechanizm

Aplikacja określa, kiedy RotaryService wchodzi i wychodzi z trybu DM. Aby zapewnić spójne doświadczenie użytkownika, naciśnięcie środkowego przycisku przy widoku DM powinno spowodować przejście do trybu DM, a przycisk Wstecz powinien zakończyć tryb DM. Jeśli środkowy przycisk i/lub szturchnięcie nie są używane, mogą to być alternatywne sposoby wyjścia z trybu DM. W przypadku aplikacji takich jak Mapy do przejścia do trybu DM można użyć przycisku reprezentującego DM.

Aby obsługiwać zaawansowany tryb DM, widok:

  1. ( Android 11 QPR3, Android 11 Car, Android 12 ) MUSI nasłuchiwać zdarzenia KEYCODE_DPAD_CENTER , aby wejść w tryb DM i nasłuchiwać zdarzenia KEYCODE_BACK , aby wyjść z trybu DM, wywołując w każdym przypadku DirectManipulationHelper.enableDirectManipulationMode() . Aby nasłuchiwać tych zdarzeń, wykonaj jedną z następujących czynności:
    • Zarejestruj OnKeyListener .
    • Lub,
    • Rozszerz widok, a następnie zastąp jego metodę dispatchKeyEvent() .
  2. POWINNO nasłuchiwać zdarzeń szturchnięć ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT lub KEYCODE_DPAD_RIGHT ), jeśli widok powinien obsługiwać szturchnięcia.
  3. POWINNO nasłuchiwać zdarzeń MotionEvent i uzyskać liczbę rotacji w AXIS_SCROLL , jeśli widok chce obsłużyć obrót. Można to zrobić na kilka sposobów:
    1. Zarejestruj OnGenericMotionListener .
    2. Rozszerz widok i zastąp jego metodę dispatchTouchEvent() .
  4. Aby uniknąć utknięcia w trybie DM, MUSISZ wyjść z trybu DM, gdy fragment lub działanie, do którego należy widok, nie jest interaktywne.
  5. POWINNO zapewniać wizualną wskazówkę wskazującą, że widok jest w trybie DM.

Poniżej znajduje się przykład widoku niestandardowego, który wykorzystuje tryb DM do przesuwania i powiększania mapy:

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

Więcej przykładów można znaleźć w projekcie RotaryPlayground .

Widok aktywności

Podczas korzystania z ActivityView:

  • ActivityView nie powinien umożliwiać fokusu.
  • ( Android 11 QPR3, Android 11 Car, przestarzałe w Androidzie 11 )
    Zawartość ActivityView MUSI zawierać FocusParkingView jako pierwszy widok, na którym można się skupić, a jego app:shouldRestoreFocus MUSI mieć wartość false .
  • Zawartość ActivityView nie powinna zawierać widoków android:focusByDefault .

Dla użytkownika Widoki Aktywności nie powinny mieć żadnego wpływu na nawigację, z wyjątkiem tego, że obszary fokusu nie mogą obejmować Widoków Aktywności. Innymi słowy, nie można mieć jednego obszaru skupienia zawierającego treść wewnątrz i na zewnątrz ActivityView . Jeśli nie dodasz żadnego FocusAreas do ActivityView , katalog główny hierarchii widoków w ActivityView będzie uważany za niejawny obszar fokusu.

Przyciski działające po przytrzymaniu

Większość przycisków powoduje pewne działanie po kliknięciu. Zamiast tego niektóre przyciski działają po ich przytrzymaniu. Na przykład przyciski szybkiego przewijania do przodu i przewijania do tyłu zazwyczaj działają, gdy są przytrzymywane. Aby takie przyciski obsługiwały obroty, nasłuchuj KeyEvents KEYCODE_DPAD_CENTER w następujący sposób:

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

W którym mRunnable podejmuje akcję (taką jak przewijanie) i planuje uruchomienie się z opóźnieniem.

Tryb dotykowy

Użytkownicy mogą używać kontrolera obrotowego do interakcji z radioodtwarzaczem w samochodzie na dwa sposoby: za pomocą kontrolera obrotowego lub dotykając ekranu. Podczas korzystania z pokrętła podświetlony jest jeden z widoków, na których można ustawić ostrość. Po dotknięciu ekranu nie pojawia się podświetlenie ostrości. Użytkownik może w dowolnym momencie przełączać się pomiędzy trybami wprowadzania danych:

  • Obróć → dotknij. Gdy użytkownik dotknie ekranu, podświetlenie ostrości znika.
  • Dotknij → obrotowy. Gdy użytkownik szturchnie, obróci lub naciśnie środkowy przycisk, pojawi się podświetlenie fokusu.

Przyciski Wstecz i Strona główna nie mają wpływu na tryb wprowadzania.

Rotary opiera się na istniejącej koncepcji trybu dotykowego Androida. Za pomocą View.isInTouchMode() można określić, jakiego trybu wprowadzania danych używa użytkownik. Do nasłuchiwania zmian możesz użyć OnTouchModeChangeListener . Chociaż można to wykorzystać do dostosowania interfejsu użytkownika do bieżącego trybu wprowadzania, należy unikać większych zmian, ponieważ mogą one wprowadzać w błąd.

Rozwiązywanie problemów

W aplikacjach przeznaczonych do obsługi dotyku często stosuje się zagnieżdżone widoki, na których można się skupiać. Na przykład wokół ImageButton może znajdować się FrameLayout , z których oba można ustawić. Nie szkodzi to dotykowi, ale może powodować pogorszenie komfortu korzystania z funkcji obrotowej, ponieważ użytkownik musi dwukrotnie obrócić kontroler, aby przejść do następnego widoku interaktywnego. Aby zapewnić wygodę użytkownika, Google zaleca ustawienie skupienia widoku zewnętrznego lub widoku wewnętrznego, ale nie obu.

Jeśli przycisk lub przełącznik straci ostrość po naciśnięciu za pomocą pokrętła, może mieć zastosowanie jeden z poniższych warunków:

  • Przycisk lub przełącznik jest blokowany (na krótko lub na czas nieokreślony) w wyniku naciśnięcia przycisku. W obu przypadkach można temu zaradzić na dwa sposoby:
    • Pozostaw stan android:enabled jako true i użyj stanu niestandardowego, aby wyszarzić przycisk lub przełączyć, jak opisano w Custom State .
    • Użyj kontenera, aby otoczyć przycisk lub przełącznik i ustawić kontener, na którym można się skupić zamiast przycisku lub przełącznika. (Odbiornik kliknięcia musi znajdować się w kontenerze.)
  • Przycisk lub przełącznik jest wymieniany. Na przykład akcja wykonywana po naciśnięciu przycisku lub przełączeniu przełącznika może spowodować odświeżenie dostępnych akcji, powodując zastąpienie istniejących przycisków nowymi. Można temu zaradzić na dwa sposoby:
    • Zamiast tworzyć nowy przycisk lub przełącznik, ustaw ikonę i/lub tekst istniejącego przycisku lub przełącznika.
    • Jak powyżej, dodaj możliwy do skupienia kontener wokół przycisku lub przełącznika.

Obrotowy plac zabaw

RotaryPlayground to aplikacja referencyjna dla urządzeń obrotowych. Skorzystaj z niego, aby dowiedzieć się, jak zintegrować funkcje obrotowe ze swoimi aplikacjami. RotaryPlayground jest zawarty w kompilacjach emulatorów i kompilacjach dla urządzeń z systemem operacyjnym Android Automotive OS (AAOS).

  • Repozytorium RotaryPlayground : packages/apps/Car/tests/RotaryPlayground/
  • Wersje: Android 11 QPR3, Android 11 Car i Android 12

Aplikacja RotaryPlayground wyświetla po lewej stronie następujące karty:

  • Karty. Przetestuj poruszanie się po obszarach fokusu, pomijanie elementów, na których nie można ustawić ostrości, oraz wprowadzanie tekstu.
  • Bezpośrednia manipulacja. Testuj widżety obsługujące prosty i zaawansowany tryb bezpośredniej manipulacji. Ta zakładka służy specjalnie do bezpośredniej manipulacji w oknie aplikacji.
  • Manipulacja interfejsem użytkownika systemu. Testuj widżety obsługujące bezpośrednią manipulację w oknach systemowych, w których obsługiwany jest tylko prosty tryb bezpośredniej manipulacji.
  • Siatka. Przetestuj nawigację obrotową według wzoru Z z przewijaniem.
  • Powiadomienie. Przetestuj włączanie i wyłączanie powiadomień heads-up.
  • Zwój. Przetestuj przewijanie mieszanki treści, na których można się skupić i których nie można skupić.
  • Widok sieciowy. Przetestuj nawigację po łączach w WebView .
  • Niestandardowy FocusArea . Przetestuj dostosowywanie FocusArea :
    • Owinąć.
    • android:focusedByDefault i app:defaultFocus
    • .
    • Wyraźne cele szturchania.
    • Poruszaj skrótami.
    • FocusArea bez widoków, na których można się skupić.