Разработка приложений

Следующий материал предназначен для разработчиков приложений.

Чтобы ваше приложение поддерживало ротацию, вы ДОЛЖНЫ:

  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. Выбор поворотной машины.

USB-клавиатура

  • Подключите USB-клавиатуру к устройству под управлением Android Automotive OS (AAOS). В некоторых случаях экранная клавиатура не появляется.
  • Используйте сборку userdebug или eng .
  • Включите фильтрацию ключевых событий:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • В таблице ниже можно найти соответствующую клавишу для каждого действия:
    Ключ Вращательное действие
    вопрос Поворот против часовой стрелки
    Э Повернуть по часовой стрелке
    А Сдвинуть влево
    Д Подтолкнуть вправо
    Вт Подтолкнуть вверх
    С Сдвинуть вниз
    F или запятая Центральная кнопка
    R или Esc Кнопка «Назад»

Команды АБР

Вы можете использовать команды 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 мс назад и 50 мс назад)
adb shell cmd car_service inject-key 282 Сдвинуть влево
adb shell cmd car_service inject-key 283 Подтолкнуть вправо
adb shell cmd car_service inject-key 280 Подтолкнуть вверх
adb shell cmd car_service inject-key 281 Сдвинуть вниз
adb shell cmd car_service inject-key 23 Нажатие центральной кнопки
adb shell input keyevent inject-key 4 Нажмите кнопку «Назад»

OEM поворотный контроллер

Когда ваше оборудование поворотного контроллера запущено и работает, это наиболее реалистичный вариант. Это особенно полезно для тестирования быстрого вращения.

ФокусПарковкаПросмотреть

FocusParkingView — прозрачное представление в библиотеке автомобильного пользовательского интерфейса (car-ui-library) . RotaryService использует его для поддержки навигации с помощью поворотного контроллера. FocusParkingView должен быть первым фокусируемым видом в макете. Он должен быть размещен за пределами всех FocusArea . Каждое окно должно иметь один FocusParkingView . Если вы уже используете базовый макет car-ui-library, который содержит FocusParkingView , вам не нужно добавлять еще один FocusParkingView . Ниже показан пример FocusParkingView в RotaryPlayground .

<FrameLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
   <com.android.car.ui.FocusParkingView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
</FrameLayout>

Вот причины, по которым вам нужен FocusParkingView :

  1. Android не очищает фокус автоматически, если фокус установлен в другом окне. Если вы попытаетесь снять фокус в предыдущем окне, Android перефокусирует представление в этом окне, в результате чего фокус будет сосредоточен на двух окнах одновременно. Добавление FocusParkingView в каждое окно может решить эту проблему. Это представление является прозрачным, а его выделение фокуса по умолчанию отключено, поэтому оно невидимо для пользователя независимо от того, находится оно в фокусе или нет. Он может получить фокус, чтобы RotaryService мог зафиксировать на нем фокус и убрать выделение фокуса.
  2. Если в текущем окне имеется только одна FocusArea , вращение контроллера в FocusArea приводит к тому, что RotaryService перемещает фокус с представления справа на представление слева (и наоборот). Добавление этого представления в каждое окно может решить проблему. Когда RotaryService определяет, что целью фокуса является FocusParkingView , он может определить, что произойдет циклический переход, и в этот момент он избегает циклического обхода, не перемещая фокус.
  3. Когда поворотный регулятор запускает приложение, Android фокусирует первое фокусируемое представление, которым всегда является FocusParkingView . FocusParkingView определяет оптимальный вид для фокусировки, а затем применяет фокус.

Фокусируемые виды

RotaryService основывается на существующей концепции фокуса просмотра в платформе Android, восходящей к тем временам, когда в телефонах были физические клавиатуры и крестовины. Существующий атрибут android:nextFocusForward перепрофилирован для поворотного (см. настройку FocusArea ), но android:nextFocusLeft , android:nextFocusRight , android:nextFocusUp и android:nextFocusDown — нет.

RotaryService фокусируется только на видах, которые можно фокусировать. Некоторые представления, такие как Button , обычно являются фокусируемыми. Другие, такие как TextView и ViewGroup , обычно этого не делают. Кликабельные представления автоматически фокусируются, а представления автоматически становятся кликабельными, если у них есть прослушиватель кликов. Если эта автоматическая логика приводит к желаемой фокусируемости, вам не нужно явно задавать фокусируемость представления. Если автоматическая логика не приводит к желаемой возможности фокусировки, установите для атрибута android:focusable значение true или false или программно задайте возможность фокусировки представления с помощью View.setFocusable(boolean) . Чтобы RotaryService мог сосредоточиться на этом, представление ДОЛЖНО соответствовать следующим требованиям:

  • Фокусируемый
  • Включено
  • Видимый
  • Иметь ненулевые значения ширины и высоты.

Если вид не соответствует всем этим требованиям (например, у него есть фокусируемая, но отключенная кнопка), пользователь не сможет использовать поворотный регулятор, чтобы сфокусироваться на нем. Если вы хотите сосредоточиться на отключенных представлениях, рассмотрите возможность использования пользовательского состояния вместо android:state_enabled чтобы контролировать внешний вид представления без указания того, что Android должен считать его отключенным. Ваше приложение может сообщить пользователю, почему просмотр отключается при нажатии. В следующем разделе объясняется, как это сделать.

Пользовательское состояние

Чтобы добавить пользовательское состояние:

  1. Чтобы добавить пользовательский атрибут в ваше представление. Например, чтобы добавить пользовательское состояние state_rotary_enabled в класс представления CustomView , используйте:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Чтобы отслеживать это состояние, добавьте в представление переменную экземпляра вместе с методами доступа:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. Чтобы прочитать значение вашего атрибута при создании представления:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. В классе представления переопределите метод onCreateDrawableState() , а затем при необходимости добавьте пользовательское состояние. Например:
    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        if (mRotaryEnabled) extraSpace++;
        int[] drawableState = super.onCreateDrawableState(extraSpace);
        if (mRotaryEnabled) {
            mergeDrawableStates(drawableState, { R.attr.state_rotary_enabled });
        }
        return drawableState;
    }
    
  5. Заставьте обработчик кликов вашего представления работать по-разному в зависимости от его состояния. Например, обработчик кликов может ничего не делать или может выдать всплывающее уведомление, если mRotaryEnabled имеет значение false .
  6. Чтобы кнопка отображалась отключенной, в фоновом режиме вашего представления используйте app:state_rotary_enabled вместо android:state_enabled . Если у вас его еще нет, вам нужно добавить:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Если ваше представление отключено в каких-либо макетах, замените android:enabled="false" на app:state_rotary_enabled="false" и затем добавьте пространство имен app , как указано выше.
  8. Если ваше представление отключено программно, замените вызовы setEnabled() вызовами setRotaryEnabled() .

ФокусОбласть

Используйте FocusAreas , чтобы разделить фокусируемые представления на блоки, чтобы упростить навигацию и обеспечить единообразие с другими приложениями. Например, если в вашем приложении есть панель инструментов, она должна находиться в отдельной FocusArea от остальной части вашего приложения. Панели вкладок и другие элементы навигации также должны быть отделены от остальной части приложения. Большие списки обычно должны иметь собственную FocusArea . В противном случае пользователям придется просматривать весь список, чтобы получить доступ к некоторым представлениям.

FocusArea — это подкласс LinearLayout в библиотеке car-ui-library. Когда эта функция включена, FocusArea подсвечивается, когда один из ее потомков находится в фокусе. Дополнительные сведения см. в разделе Настройка выделения фокуса .

Если при создании блока навигации в файле макета вы собираетесь использовать LinearLayout в качестве контейнера для этого блока, используйте вместо него FocusArea . В противном случае оберните блок в FocusArea .

НЕ вставляйте FocusArea в другую FocusArea . Это приводит к неопределенному поведению навигации. Убедитесь, что все фокусируемые представления вложены в FocusArea .

Пример FocusArea в RotaryPlayground показан ниже:

<com.android.car.ui.FocusArea
       android:layout_margin="16dp"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">
       <EditText
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:singleLine="true">
       </EditText>
   </com.android.car.ui.FocusArea>

FocusArea работает следующим образом:

  1. При обработке действий поворота и перемещения RotaryService ищет экземпляры FocusArea в иерархии представлений.
  2. При получении события поворота RotaryService перемещает фокус на другой View, который может получить фокус в той же FocusArea .
  3. При получении события подталкивания RotaryService перемещает фокус на другое представление, которое может получить фокус в другой (обычно соседней) FocusArea .

Если вы не включаете в макет ни одну FocusAreas , корневое представление рассматривается как неявная область фокусировки. Пользователь не может подталкивать пользователя к навигации в приложении. Вместо этого они будут вращаться по всем фокусируемым видам, что может быть достаточно для диалогов.

Настройка FocusArea

Для настройки поворотной навигации можно использовать два стандартных атрибута просмотра:

  • android:nextFocusForward позволяет разработчикам приложений указывать порядок вращения в области фокусировки. Это тот же атрибут, который используется для управления порядком табуляции при навигации с помощью клавиатуры. НЕ используйте этот атрибут для создания цикла. Вместо этого используйте app:wrapAround (см. ниже), чтобы создать цикл.
  • android:focusedByDefault позволяет разработчикам приложений указывать вид фокуса по умолчанию в окне. НЕ используйте этот атрибут и app:defaultFocus (см. ниже) в одной и той же FocusArea .

FocusArea также определяет некоторые атрибуты для настройки поворотной навигации. Неявные области фокусировки нельзя настроить с помощью этих атрибутов.

  1. ( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
    app:defaultFocus можно использовать для указания идентификатора фокусируемого представления-потомка, на котором должен быть сфокусирован, когда пользователь подталкивает к этому FocusArea .
  2. ( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
    app:defaultFocusOverridesHistory можно установить значение true , чтобы указанное выше представление взяло фокус, даже если с историей указано, что другое представление в этой FocusArea было сфокусировано.
  3. ( Андроид 12 )
    Используйте app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut и app:nudgeDownShortcut чтобы указать идентификатор фокусируемого представления-потомка, на котором должно быть сфокусировано, когда пользователь подталкивает в заданном направлении. Чтобы узнать больше, ознакомьтесь с содержанием ярлыков подсказок ниже.

    ( Android 11 QPR3, Android 11 Car, прекращено в Android 12 ) app:nudgeShortcut и app:nudgeShortcutDirection поддерживают только один ярлык подталкивания.

  4. ( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
    Чтобы включить вращение в этой FocusArea , app:wrapAround можно установить значение true . Чаще всего это используется, когда виды расположены по кругу или овалу.
  5. ( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
    Чтобы настроить отступ выделения в этой FocusArea , используйте app:highlightPaddingStart , app:highlightPaddingEnd , app:highlightPaddingTop , app:highlightPaddingBottom , app:highlightPaddingHorizontal и app:highlightPaddingVertical .
  6. ( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
    Чтобы настроить воспринимаемые границы этой FocusArea и найти цель смещения, используйте app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , app:bottomBoundOffset , app:horizontalBoundOffset и app:verticalBoundOffset .
  7. ( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
    Чтобы явно указать идентификатор соседней FocusArea (или областей) в заданных направлениях, используйте app:nudgeLeft , app:nudgeRight , app:nudgeUp и app:nudgeDown . Используйте это, когда геометрический поиск, используемый по умолчанию, не находит желаемую цель.

Подталкивание обычно осуществляется между областями фокуса. Но при использовании ярлыков подталкивания иногда сначала происходит перемещение внутри FocusArea , поэтому пользователю может потребоваться дважды подтолкнуть, чтобы перейти к следующей FocusArea . Ярлыки перемещения полезны, когда FocusArea содержит длинный список, за которым следует плавающая кнопка действия , как в примере ниже:

Сдвинуть ярлык
Рисунок 3. Ярлык смещения

Без ярлыка подталкивания пользователю пришлось бы прокручивать весь список, чтобы добраться до FAB.

Настройка выделения фокуса

Как отмечалось выше, RotaryService основывается на существующей концепции фокуса представления в платформе Android. Когда пользователь поворачивает и подталкивает, RotaryService перемещает фокус, фокусируя одно представление и расфокусируя другое. В Android, когда представление сфокусировано, если представление:

  • Указав собственную подсветку фокуса, Android рисует подсветку фокуса представления.
  • Не указывает выделение фокуса, а выделение фокуса по умолчанию не отключено. Android рисует выделение фокуса по умолчанию для представления.

В приложениях, предназначенных для сенсорного управления, обычно не предусмотрены соответствующие выделения фокуса.

Подсветка фокуса по умолчанию предоставляется платформой Android и может быть изменена OEM-производителем. Разработчики приложений получают его, когда используемая ими тема является производной от Theme.DeviceDefault .

Для обеспечения единообразия взаимодействия с пользователем по возможности полагайтесь на выделение фокуса по умолчанию. Если вам нужна подсветка фокуса нестандартной формы (например, круглая или в форме таблетки) или если вы используете тему, не производную от Theme.DeviceDefault , используйте ресурсы car-ui-library, чтобы указать собственную подсветку фокуса для каждый просмотр.

Чтобы указать пользовательскую подсветку фокуса для представления, измените рисуемый фон или передний план представления на рисуемый объект, который отличается, когда представление сфокусировано. Обычно вы меняете фон. Следующий рисунок, если он используется в качестве фона для квадратного вида, создает круглую подсветку фокуса:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_focused="true" android:state_pressed="true">
      <shape android:shape="oval">
         <solid android:color="@color/car_ui_rotary_focus_pressed_fill_color"/>
         <stroke
            android:width="@dimen/car_ui_rotary_focus_pressed_stroke_width"
            android:color="@color/car_ui_rotary_focus_pressed_stroke_color"/>
      </shape>
   </item>
   <item android:state_focused="true">
      <shape android:shape="oval">
         <solid android:color="@color/car_ui_rotary_focus_fill_color"/>
         <stroke
            android:width="@dimen/car_ui_rotary_focus_stroke_width"
            android:color="@color/car_ui_rotary_focus_stroke_color"/>
      </shape>
   </item>
   <item>
      <ripple...>
         ...
      </ripple>
   </item>
</selector>

( Android 11 QPR3, Android 11 Car, Android 12 ). Ссылки на ресурсы, выделенные жирным шрифтом в приведенном выше примере, обозначают ресурсы, определенные в библиотеке car-ui-library. OEM-производитель переопределяет их, чтобы они соответствовали указанному ими выделению фокуса по умолчанию. Это гарантирует, что цвет выделения фокуса, ширина обводки и т. д. не изменятся, когда пользователь перемещается между представлением с настраиваемым выделением фокуса и представлением с выделением фокуса по умолчанию. Последний элемент — это пульсация, используемая для прикосновения. Значения по умолчанию, используемые для выделенных жирным шрифтом ресурсов, выглядят следующим образом:

Значения по умолчанию для ресурсов, выделенных жирным шрифтом
Рисунок 4. Значения по умолчанию для ресурсов, выделенных жирным шрифтом.

Кроме того, пользовательская подсветка фокуса вызывается, когда кнопке присваивается сплошной цвет фона, чтобы привлечь к ней внимание пользователя, как в примере ниже. Из-за этого область фокусировки может быть трудно различима. В этой ситуации укажите пользовательскую подсветку фокуса, используя дополнительные цвета:

Сплошной цвет фона
  • ( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • ( Андроид 12 )
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

Например:

Сосредоточенный, не нажатыйСосредоточенный, нажатый
Сосредоточенный, не нажатый Сосредоточенный, нажатый

Вращающаяся прокрутка

Если ваше приложение использует RecyclerView , вам СЛЕДУЕТ использовать вместо него CarUiRecyclerView . Это гарантирует, что ваш пользовательский интерфейс будет соответствовать другим, поскольку настройки OEM-производителя применяются ко всем CarUiRecyclerView .

Если все элементы вашего списка доступны для фокусировки, вам больше ничего делать не нужно. Вращающаяся навигация перемещает фокус по элементам в списке, а список прокручивается, чтобы сделать видимым новый элемент, находящийся в фокусе.

( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
Если имеется сочетание фокусируемых и нефокусируемых элементов или если все элементы нефокусируемые, вы можете включить поворотную прокрутку, которая позволяет пользователю использовать поворотный контроллер для постепенной прокрутки списка, не пропуская нефокусируемые элементы. Чтобы включить поворотную прокрутку, установите для атрибута app:rotaryScrollEnabled значение true .

( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
Вы можете включить поворотную прокрутку в любом представлении с возможностью прокрутки, включая av CarUiRecyclerView , с помощью метода setRotaryScrollEnabled() в CarUiUtils . Если вы это сделаете, вам необходимо:

  • Сделайте прокручиваемое представление фокусируемым, чтобы на нем можно было сфокусироваться, когда ни одно из его фокусируемых дочерних представлений не видно.
  • Отключите выделение фокуса по умолчанию в прокручиваемом представлении, вызвав setDefaultFocusHighlightEnabled(false) чтобы прокручиваемое представление не казалось сфокусированным,
  • Убедитесь, что прокручиваемое представление сфокусировано перед его потомками, вызвав setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) .
  • Прослушивайте MotionEvents с помощью SOURCE_ROTARY_ENCODER и AXIS_VSCROLL или AXIS_HSCROLL , чтобы указать расстояние для прокрутки и направление (через знак).

Когда в CarUiRecyclerView включена поворотная прокрутка и пользователь поворачивается к области, где нет доступных для фокусировки представлений, полоса прокрутки меняет цвет с серого на синий, как будто указывая, что полоса прокрутки находится в фокусе. Если хотите, вы можете реализовать аналогичный эффект.

MotionEvents аналогичны событиям, генерируемым колесом прокрутки мыши, за исключением источника.

Режим прямого манипулирования

Обычно подталкивание и вращение перемещаются по пользовательскому интерфейсу, а нажатие центральной кнопки выполняет действие, хотя это не всегда так. Например, если пользователь хочет отрегулировать громкость будильника, он может использовать поворотный контроллер для перехода к ползунку громкости, нажать центральную кнопку, повернуть контроллер, чтобы отрегулировать громкость будильника, а затем нажать кнопку «Назад», чтобы вернуться к навигации. . Это называется режимом прямого манипулирования (DM) . В этом режиме поворотный контроллер используется для непосредственного взаимодействия с видом, а не для навигации.

Реализуйте DM одним из двух способов. Если вам нужно только обрабатывать вращение, а представление, которым вы хотите манипулировать, реагирует на ACTION_SCROLL_FORWARD и ACTION_SCROLL_BACKWARD AccessibilityEvent соответствующим образом, используйте простой механизм. В противном случае используйте расширенный механизм.

Простой механизм — единственный вариант в системных окнах; приложения могут использовать любой механизм.

Простой механизм

( Android 11 QPR3, Android 11 для автомобилей, Android 12 )
Ваше приложение должно вызвать DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) . RotaryService распознает, когда пользователь находится в режиме DM, и переходит в режим DM, когда пользователь нажимает центральную кнопку, когда вид находится в фокусе. В режиме DM вращение выполняется ACTION_SCROLL_FORWARD или ACTION_SCROLL_BACKWARD и выходит из режима DM, когда пользователь нажимает кнопку «Назад». Простой механизм переключает выбранное состояние представления при входе и выходе из режима DM.

Чтобы обеспечить визуальную подсказку о том, что пользователь находится в режиме DM, при выборе измените вид вашего представления. Например, измените фон, если android:state_selected имеет true .

Расширенный механизм

Приложение определяет, когда RotaryService входит в режим DM и выходит из него. Для единообразного взаимодействия с пользователем нажатие центральной кнопки с фокусом на представление DM должно перейти в режим DM, а кнопка «Назад» должна выйти из режима DM. Если центральная кнопка и/или подталкивание не используются, они могут быть альтернативными способами выхода из режима DM. В таких приложениях, как Карты, для входа в режим DM можно использовать кнопку, обозначающую DM.

Для поддержки расширенного режима DM представление:

  1. ( Android 11 QPR3, Android 11 Car, Android 12 ) ДОЛЖНЫ прослушивать событие KEYCODE_DPAD_CENTER для входа в режим DM и прослушивать событие KEYCODE_BACK для выхода из режима DM, вызывая DirectManipulationHelper.enableDirectManipulationMode() в каждом случае. Чтобы прослушивать эти события, выполните одно из следующих действий:
    • Зарегистрируйте OnKeyListener .
    • или,
    • Расширьте представление, а затем переопределите его метод dispatchKeyEvent() .
  2. СЛЕДУЕТ прослушивать события подталкивания ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT или KEYCODE_DPAD_RIGHT ), если представление должно обрабатывать подталкивания.
  3. СЛЕДУЕТ прослушивать MotionEvent и получать счетчик вращения в AXIS_SCROLL если представление хочет обрабатывать вращение. Есть несколько способов сделать это:
    1. Зарегистрируйте OnGenericMotionListener .
    2. Расширьте представление и переопределите его метод dispatchTouchEvent() .
  4. Чтобы избежать зависания в режиме DM, ДОЛЖЕН выйти из режима DM, когда фрагмент или действие, к которому принадлежит представление, не является интерактивным.
  5. СЛЕДУЕТ предоставлять визуальную подсказку, указывающую, что представление находится в режиме DM.

Ниже приведен пример пользовательского представления, в котором для панорамирования и масштабирования карты используется режим DM:

/** Whether this view is in DM mode. */
private boolean mInDirectManipulationMode;

/** Initializes the view. Called by the constructors. */ private void init() { setOnKeyListener((view, keyCode, keyEvent) -> { boolean isActionUp = keyEvent.getAction() == KeyEvent.ACTION_UP; switch (keyCode) { // Always consume KEYCODE_DPAD_CENTER and KEYCODE_BACK events. case KeyEvent.KEYCODE_DPAD_CENTER: if (!mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = true; DirectManipulationHelper.enableDirectManipulationMode(this, true); setSelected(true); // visually indicate DM mode } return true; case KeyEvent.KEYCODE_BACK: if (mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); setSelected(false); } return true; // Consume controller nudge events only when in DM mode. // When in DM mode, nudges pan the map. case KeyEvent.KEYCODE_DPAD_UP: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, -10f); return true; case KeyEvent.KEYCODE_DPAD_DOWN: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, 10f); return true; case KeyEvent.KEYCODE_DPAD_LEFT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(-10f, 0f); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(10f, 0f); return true; // Don't consume other key events. default: return false; } });
// When in DM mode, rotation zooms the map. setOnGenericMotionListener(((view, motionEvent) -> { if (!mInDirectManipulationMode) return false; float scroll = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL); zoom(10 * scroll); return true; })); }
@Override public void onPause() { if (mInDirectManipulationMode) { // To ensure that the user doesn't get stuck in DM mode, disable DM mode // when the fragment is not interactive (e.g., a dialog shows up). mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); } super.onPause(); }

Дополнительные примеры можно найти в проекте RotaryPlayground .

Просмотр активности

При использовании ActivityView:

  • ActivityView не должен быть фокусируемым.
  • ( Android 11 QPR3, Android 11 Car, поддержка в Android 11 устарела )
    Содержимое ActivityView ДОЛЖНО содержать FocusParkingView в качестве первого фокусируемого представления, а его атрибут app:shouldRestoreFocus ДОЛЖЕН быть false .
  • Содержимое ActivityView не должно иметь представлений android:focusByDefault .

Для пользователя ActivityViews не должно влиять на навигацию, за исключением того, что области фокуса не могут охватывать ActivityViews. Другими словами, вы не можете иметь единую область фокуса, содержащую контент внутри и снаружи ActivityView . Если вы не добавляете FocusArea в свой 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 выполняет действие (например, перематывает назад) и планирует запуск после задержки.

Сенсорный режим

Пользователи могут использовать поворотный контроллер для взаимодействия с головным устройством в автомобиле двумя способами: либо с помощью поворотного контроллера, либо путем прикосновения к экрану. При использовании поворотного контроллера выделяется один из доступных для фокусировки видов. При касании экрана выделение фокуса не появляется. Пользователь может переключаться между этими режимами ввода в любое время:

  • Поворотный → сенсорный. Когда пользователь касается экрана, выделение фокуса исчезает.
  • Сенсорный → поворотный. Когда пользователь подталкивает, поворачивает или нажимает центральную кнопку, появляется выделение фокуса.

Кнопки «Назад» и «Домой» не влияют на режим ввода.

Роторное управление сочетает в себе существующую в Android концепцию сенсорного режима . Вы можете использовать View.isInTouchMode() чтобы определить, какой режим ввода использует пользователь. Вы можете использовать OnTouchModeChangeListener для прослушивания изменений. Хотя это можно использовать для настройки пользовательского интерфейса для текущего режима ввода, избегайте каких-либо серьезных изменений, поскольку они могут сбить с толку.

Поиск неисправностей

В приложениях, предназначенных для сенсорного управления, обычно имеются вложенные фокусируемые представления. Например, вокруг ImageButton может быть FrameLayout , оба из которых являются фокусируемыми. Это не вредит сенсорному управлению, но может привести к ухудшению пользовательского опыта при повороте, поскольку пользователю приходится дважды повернуть контроллер, чтобы перейти к следующему интерактивному представлению. Для удобства пользователя Google рекомендует сделать фокусируемым либо внешний вид, либо внутренний вид, но не оба одновременно.

Если кнопка или переключатель теряет фокус при нажатии на поворотный контроллер, может возникнуть одно из следующих условий:

  • Кнопка или переключатель отключаются (на короткое или неопределенное время) из-за нажатия кнопки. В любом случае есть два пути решения этой проблемы:
    • Оставьте состояние android:enabled как true и используйте пользовательское состояние, чтобы сделать кнопку или переключатель серым, как описано в разделе «Пользовательское состояние» .
    • Используйте контейнер, чтобы окружить кнопку или переключатель, и сделайте контейнер фокусируемым вместо кнопки или переключателя. (Прослушиватель кликов должен находиться в контейнере.)
  • Кнопка или переключатель заменяется. Например, действие, выполняемое при нажатии кнопки или переключении переключателя, может вызвать обновление доступных действий, что приведет к замене существующих кнопок новыми. Есть два способа решить эту проблему:
    • Вместо создания новой кнопки или переключателя установите значок и/или текст существующей кнопки или переключателя.
    • Как и выше, добавьте фокусируемый контейнер вокруг кнопки или переключателя.

РотариИгровая площадка

RotaryPlayground — это справочное приложение для роторных игр. Используйте его, чтобы узнать, как интегрировать поворотные функции в ваши приложения. RotaryPlayground включен в сборки эмулятора и сборки для устройств под управлением Android Automotive OS (AAOS).

  • Репозиторий RotaryPlayground : packages/apps/Car/tests/RotaryPlayground/
  • Версии: Android 11 QPR3, Android 11 для автомобилей и Android 12.

Приложение RotaryPlayground слева отображает следующие вкладки:

  • Карты. Протестируйте навигацию по областям фокусировки, пропуская нефокусируемые элементы и ввод текста.
  • Прямая манипуляция. Тестируйте виджеты, поддерживающие простой и расширенный режим прямого манипулирования. Эта вкладка предназначена специально для прямого управления в окне приложения.
  • Манипуляции с системным пользовательским интерфейсом. Тестируйте виджеты, поддерживающие прямое манипулирование в системных окнах, где поддерживается только простой режим прямого манипулирования.
  • Сетка. Проверьте поворотную навигацию по z-шаблону с прокруткой.
  • Уведомление. Протестируйте включение и выключение хедз-ап уведомлений.
  • Прокрутите. Проверьте прокрутку как фокусируемого, так и нефокусируемого контента.
  • ВебВью. Проверьте навигацию по ссылкам в WebView .
  • Пользовательская FocusArea . Тестовая настройка FocusArea :
    • Обертывание.
    • android:focusedByDefault и app:defaultFocus
    • .
    • Явные цели подталкивания.
    • Подталкивание ярлыков.
    • FocusArea без фокусируемых видов.