Обзор входных событий

Попробуйте способ создания
Jetpack Compose — рекомендуемый набор инструментов пользовательского интерфейса для Android. Узнайте, как использовать сенсорный ввод и ввод в Compose.

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

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

Хотя вы чаще будете использовать прослушиватели событий для прослушивания взаимодействия с пользователем, может наступить момент, когда вам захочется расширить класс View, чтобы создать собственный компонент. Возможно, вы захотите расширить класс Button , чтобы сделать что-то более необычное. В этом случае вы сможете определить поведение событий по умолчанию для вашего класса, используя обработчики событий класса.

Слушатели событий

Прослушиватель событий — это интерфейс класса View , который содержит один метод обратного вызова. Эти методы будут вызываться платформой Android, когда представление, в котором зарегистрирован прослушиватель, запускается при взаимодействии пользователя с элементом в пользовательском интерфейсе.

В интерфейсы прослушивателя событий включены следующие методы обратного вызова:

onClick()
Из View.OnClickListener . Это вызывается, когда пользователь либо касается объекта (в сенсорном режиме), либо фокусируется на элементе с помощью навигационных клавиш или трекбола и нажимает подходящую клавишу «Ввод» или нажимает трекбол.
onLongClick()
Из View.OnLongClickListener . Это вызывается, когда пользователь либо касается и удерживает элемент (в сенсорном режиме), либо фокусируется на элементе с помощью навигационных клавиш или трекбола, нажимает и удерживает подходящую клавишу «ввод», либо нажимает и удерживает трекбол ( за одну секунду).
onFocusChange()
Из View.OnFocusChangeListener . Это вызывается, когда пользователь переходит к элементу или от него с помощью клавиш навигации или трекбола.
onKey()
Из View.OnKeyListener . Это вызывается, когда пользователь фокусируется на элементе и нажимает или отпускает аппаратную клавишу на устройстве.
onTouch()
Из View.OnTouchListener . Это вызывается, когда пользователь выполняет действие, квалифицируемое как событие касания, включая нажатие, отпускание или любой жест движения на экране (в пределах границ элемента).
onCreateContextMenu()
Из View.OnCreateContextMenuListener . Это вызывается при создании контекстного меню (в результате длительного «длительного щелчка»). См. обсуждение контекстных меню в руководстве разработчика меню .

Эти методы являются единственными обитателями соответствующего интерфейса. Чтобы определить один из этих методов и обрабатывать ваши события, реализуйте вложенный интерфейс в своей деятельности или определите его как анонимный класс. Затем передайте экземпляр вашей реализации соответствующему методу View.set...Listener() . (Например, вызовите setOnClickListener() и передайте ему свою реализацию OnClickListener .)

В приведенном ниже примере показано, как зарегистрировать прослушиватель on-click для кнопки.

Котлин

protected void onCreate(savedValues: Bundle) {
    ...
    val button: Button = findViewById(R.id.corky)
    // Register the onClick listener with the implementation above
    button.setOnClickListener { view ->
        // do something when the button is clicked
    }
    ...
}

Ява

// Create an anonymous implementation of OnClickListener
private OnClickListener corkyListener = new OnClickListener() {
    public void onClick(View v) {
      // do something when the button is clicked
    }
};

protected void onCreate(Bundle savedValues) {
    ...
    // Capture our button from layout
    Button button = (Button)findViewById(R.id.corky);
    // Register the onClick listener with the implementation above
    button.setOnClickListener(corkyListener);
    ...
}

Вам также может быть удобнее реализовать OnClickListener как часть вашей деятельности. Это позволит избежать дополнительной нагрузки классов и выделения объектов. Например:

Котлин

class ExampleActivity : Activity(), OnClickListener {
  
    protected fun onCreate(savedValues: Bundle) {
        val button: Button = findViewById(R.id.corky)
        button.setOnClickListener(this)
    }

    // Implement the OnClickListener callback
    fun onClick(v: View) {
        // do something when the button is clicked
    }
}

Ява

public class ExampleActivity extends Activity implements OnClickListener {
    protected void onCreate(Bundle savedValues) {
        ...
        Button button = (Button)findViewById(R.id.corky);
        button.setOnClickListener(this);
    }

    // Implement the OnClickListener callback
    public void onClick(View v) {
      // do something when the button is clicked
    }
    ...
}

Обратите внимание, что обратный вызов onClick() в приведенном выше примере не имеет возвращаемого значения, но некоторые другие методы прослушивания событий должны возвращать логическое значение. Причина зависит от события. Для тех немногих, кто это делает, вот почему:

  • onLongClick() — возвращает логическое значение, указывающее, воспользовались ли вы событием и не следует ли его передавать дальше. То есть верните true, чтобы указать, что вы обработали событие и оно должно остановиться здесь; верните false, если вы не обработали его и/или событие должно продолжаться для любых других прослушивателей щелчка.
  • onKey() — возвращает логическое значение, указывающее, воспользовались ли вы событием и не следует ли его передавать дальше. То есть верните true, чтобы указать, что вы обработали событие и оно должно остановиться здесь; верните false, если вы не обработали его и/или событие должно продолжаться для любых других прослушивателей, включенных в ключ.
  • onTouch() — возвращает логическое значение, указывающее, обрабатывает ли ваш слушатель это событие. Важно то, что это событие может иметь несколько действий, следующих друг за другом. Таким образом, если вы возвращаете false при получении события действия вниз, вы указываете, что не использовали это событие, а также не заинтересованы в последующих действиях из этого события. Таким образом, вас не будут вызывать для каких-либо других действий в рамках события, таких как жест пальцем или возможное событие действия вверх.

Помните, что события аппаратных клавиш всегда доставляются в представление, которое в данный момент находится в фокусе. Они отправляются, начиная с вершины иерархии представлений, а затем вниз, пока не достигнут соответствующего места назначения. Если ваш View (или дочерний элемент вашего View) в настоящее время имеет фокус, вы можете увидеть перемещение события с помощью метода dispatchKeyEvent() . В качестве альтернативы захвату ключевых событий через ваше представление вы также можете получать все события внутри вашего действия с помощью onKeyDown() и onKeyUp() .

Кроме того, думая о вводе текста для вашего приложения, помните, что многие устройства имеют только программные методы ввода. Такие методы не обязательно должны быть основаны на ключах; некоторые могут использовать голосовой ввод, рукописный ввод и т. д. Даже если метод ввода представляет интерфейс, похожий на клавиатуру, он обычно не запускает семейство событий onKeyDown() . Никогда не следует создавать пользовательский интерфейс, требующий управления нажатием определенных клавиш, если только вы не хотите ограничить свое приложение устройствами с аппаратной клавиатурой. В частности, не полагайтесь на эти методы для проверки ввода, когда пользователь нажимает клавишу возврата; вместо этого используйте такие действия, как IME_ACTION_DONE чтобы сигнализировать методу ввода о том, как ваше приложение ожидает реакции, чтобы оно могло осмысленно изменить свой пользовательский интерфейс. Избегайте предположений о том, как должен работать программный метод ввода, и просто доверьтесь ему, чтобы он предоставил вашему приложению уже отформатированный текст.

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

Обработчики событий

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

  • onKeyDown(int, KeyEvent) — вызывается при возникновении нового ключевого события.
  • onKeyUp(int, KeyEvent) — вызывается, когда происходит событие нажатия клавиши.
  • onTrackballEvent(MotionEvent) — вызывается при возникновении события движения трекбола.
  • onTouchEvent(MotionEvent) — вызывается при возникновении события движения сенсорного экрана.
  • onFocusChanged(boolean, int, Rect) — вызывается, когда представление получает или теряет фокус.

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

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

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

Для сенсорного устройства, как только пользователь коснется экрана, устройство перейдет в сенсорный режим. С этого момента фокусироваться будут только те виды, для которых isFocusableInTouchMode() имеет значение true, например виджеты редактирования текста. Другие представления, которые можно осязать, например кнопки, не будут фокусироваться при касании; они просто запускают свои прослушиватели по щелчку при нажатии.

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

Состояние сенсорного режима сохраняется во всей системе (все окна и действия). Чтобы запросить текущее состояние, вы можете вызвать isInTouchMode() , чтобы узнать, находится ли устройство в данный момент в сенсорном режиме.

Обработка фокуса

Платформа будет обрабатывать рутинное перемещение фокуса в ответ на ввод пользователя. Сюда входит изменение фокуса при удалении или скрытии представлений или при появлении новых представлений. Представления указывают на свою готовность получить фокус с помощью метода isFocusable() . Чтобы изменить, может ли представление получать фокус, вызовите setFocusable() . В сенсорном режиме вы можете запросить, позволяет ли представление фокусироваться с помощью isFocusableInTouchMode() . Вы можете изменить это с помощью setFocusableInTouchMode() .

На устройствах под управлением Android 9 (уровень API 28) или выше действиям не назначается первоначальный фокус. Вместо этого вы должны явно запросить первоначальный фокус, если хотите.

Движение фокуса основано на алгоритме, который находит ближайшего соседа в заданном направлении. В редких случаях алгоритм по умолчанию может не соответствовать предполагаемому поведению разработчика. В таких ситуациях вы можете явно переопределить следующие атрибуты XML в файле макета: nextFocusDown , nextFocusLeft , nextFocusRight и nextFocusUp . Добавьте один из этих атрибутов в представление, из которого уходит фокус. Определите значение атрибута как идентификатор представления, которому следует уделить фокус. Например:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>

Обычно в такой вертикальной компоновке переход вверх от первой кнопки никуда не ведет, равно как и переход вниз от второй кнопки. Теперь, когда верхняя кнопка определила нижнюю как nextFocusUp (и наоборот), фокус навигации будет циклически перемещаться сверху вниз и снизу вверх.

Если вы хотите объявить представление как фокусируемое в вашем пользовательском интерфейсе (хотя традиционно это не так), добавьте XML-атрибут android:focusable к представлению в объявлении макета. Установите значение true . Вы также можете объявить вид доступным для фокусировки в режиме касания с помощью android:focusableInTouchMode .

Чтобы запросить фокус на конкретном представлении, вызовите requestFocus() .

Чтобы прослушивать события фокуса (получать уведомления, когда представление получает или теряет фокус), используйте onFocusChange() , как описано в разделе «Прослушиватели событий» .