На устройствах Android есть несколько способов перехвата событий, связанных с взаимодействием пользователя с приложением. При рассмотрении событий в пользовательском интерфейсе подход заключается в том, чтобы фиксировать события из конкретного объекта View, с которым взаимодействует пользователь. Для этого используется класс View.
В различных классах View, которые вы будете использовать для создания макета, вы можете заметить несколько общедоступных методов обратного вызова, которые выглядят полезными для событий пользовательского интерфейса. Эти методы вызываются фреймворком Android, когда с объектом выполняется соответствующее действие. Например, когда пользователь нажимает на объект View (например, кнопку), для этого объекта вызывается метод 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. Вызывается при создании контекстного меню (в результате долгого нажатия). Подробнее о контекстных меню рассказывается в руководстве для разработчиков.
Эти методы являются единственными обитателями соответствующего интерфейса. Чтобы определить один из этих методов и обрабатывать события, реализуйте вложенный интерфейс в Activity или определите его как анонимный класс.
Затем передайте экземпляр реализации в соответствующий метод View.set...Listener(). Например, вызовите и передайте ему вашу реализацию setOnClickListener()OnClickListener.
В примере ниже показано, как зарегистрировать прослушиватель кликов для кнопки.
Kotlin
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 } ... }
Java
// 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 как часть Activity. Это позволит избежать дополнительной загрузки классов и распределения объектов. Пример:
Kotlin
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 } }
Java
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() в примере выше не имеет возвращаемого значения, но некоторые другие методы прослушивателя событий должны возвращать логическое значение. Причина зависит от события. Вот почему это происходит:
– возвращает логическое значение, указывающее, было ли событие принято и не должно ли оно передаваться дальше. Если вы обработали событие и оно должно остановиться, возвращается значение true. Если событие не обработано и/или должно быть передано другим прослушивателям кликов, возвращается значение false.onLongClick()– возвращает логическое значение, указывающее, было ли событие принято и не должно ли оно передаваться дальше. То есть возвращайте true, чтобы указать, что вы обработали событие и оно должно остановиться здесь, и false, если вы не обработали его и/или событие должно продолжаться для любых других прослушивателей on-key.onKey()– возвращает логическое значение, указывающее, принимает ли прослушиватель это событие. Важно, что за этим событием может следовать несколько действий. Поэтому, если вы возвращаете false при получении события нажатия, вы указываете, что не приняли событие и не заинтересованы в последующих действиях из этого события. Таким образом, вы не будете вызываться для каких-либо других действий в рамках события, таких как жест пальцем или возможное событие действия вверх.onTouch()
Помните, что события аппаратных клавиш всегда доставляются в представление, находящееся в фокусе. Они отправляются, начиная с вершины иерархии View, а затем вниз, пока не достигнут соответствующего пункта назначения. Если фокус находится на вашем представлении (или его дочернем элементе), вы можете увидеть, как событие проходит через метод . В качестве альтернативы вы можете получать все события в Activity с помощью dispatchKeyEvent() и onKeyDown().onKeyUp()
Также при разработке приложения учитывайте, что у многих устройств есть только программные способы ввода. Такие методы не обязательно должны быть основаны на клавишах; некоторые могут использовать голосовой ввод, рукописный ввод и так далее. Даже если способ ввода представляет собой интерфейс, похожий на клавиатуру, он, как правило, не запускает семейство событий . Никогда не создавайте интерфейс, требующий нажатия определенных клавиш, если только вы не хотите ограничить свое приложение устройствами с аппаратной клавиатурой. В частности, не полагайтесь на эти методы для проверки ввода, когда пользователь нажимает клавишу Return. Вместо этого используйте такие действия, как onKeyDown()IME_ACTION_DONE, чтобы сообщить методу ввода, как должно реагировать ваше приложение, чтобы он мог соответствующим образом изменить свой пользовательский интерфейс. Не делайте предположений о том, как должен работать метод ввода программного обеспечения, и просто доверьте ему предоставление уже отформатированного текста в ваше приложение.
Примечание. Android сначала вызывает обработчики событий, а затем соответствующие обработчики по умолчанию из определения класса. Если прослушиватель событий возвращает значение true, это останавливает распространение события на другие прослушиватели событий, а также блокирует обратный вызов обработчика событий по умолчанию в представлении. Поэтому возвращайте значение true, только если вы уверены, что хотите завершить событие.
Обработчики событий
Если вы создаете собственный компонент на основе View, то сможете определить несколько методов обратного вызова, используемых в качестве обработчиков событий по умолчанию. В документе Custom View Components (Специальные компоненты View) вы найдете информацию о некоторых распространенных обратных вызовах, используемых для обработки событий, в том числе:
– вызывается, когда происходит новое ключевое событие.onKeyDown(int, KeyEvent)– вызывается, когда происходит событие отпускания клавиши.onKeyUp(int, KeyEvent)– вызывается при возникновении события движения трекбола.onTrackballEvent(MotionEvent)– вызывается, когда происходит событие движения по сенсорному экрану.onTouchEvent(MotionEvent)– вызывается, когда представление получает или теряет фокус.onFocusChanged(boolean, int, Rect)
Есть и другие методы, которые не входят в класс View, но могут напрямую влиять на обработку событий. Поэтому при управлении более сложными событиями внутри макета рассмотрите следующие методы:
– позволяетActivity.dispatchTouchEvent(MotionEvent)Activityперехватывать все события касания до того, как они будут отправлены в окно.– позволяетViewGroup.onInterceptTouchEvent(MotionEvent)ViewGroupотслеживать события, когда они отправляются дочерним представлениям.– вызовите этот метод для родительского объекта View, чтобы указать, что он не должен перехватывать события касания с помощьюViewParent.requestDisallowInterceptTouchEvent(boolean).onInterceptTouchEvent(MotionEvent)
Сенсорный режим
Когда пользователь перемещается по пользовательскому интерфейсу с помощью клавиш со стрелками или трекбола, необходимо выделять элементы, с которыми можно взаимодействовать (например, кнопки), чтобы пользователь видел, куда будет направлен ввод. Однако если устройство поддерживает сенсорный ввод и пользователь начинает взаимодействовать с интерфейсом, касаясь его, то выделять элементы или фокусироваться на определенном представлении больше не нужно. Поэтому существует режим взаимодействия, называемый "сенсорным режимом".
Если устройство поддерживает сенсорный ввод, то при касании экрана оно переходит в сенсорный режим. С этого момента фокус можно будет устанавливать только на представления, для которых isFocusableInTouchMode() имеет значение true, например на виджеты редактирования текста.
Другие интерактивные представления, например кнопки, не будут получать фокус при нажатии, а просто будут запускать прослушиватели кликов.
Каждый раз, когда пользователь нажимает клавишу направления или прокручивает трекбол, устройство выходит из сенсорного режима и находит представление, на которое нужно перевести фокус. Теперь пользователь может возобновить взаимодействие с пользовательским интерфейсом, не касаясь экрана.
Режим сенсорного экрана поддерживается во всей системе (во всех окнах и действиях).
Чтобы узнать текущее состояние, вызовите метод isInTouchMode().
Управление фокусом
Фреймворк будет обрабатывать стандартные перемещения фокуса в ответ на действия пользователя.
Это включает изменение фокуса при удалении или скрытии представлений, а также при появлении новых представлений. Представления указывают на готовность принять фокус с помощью метода . Чтобы изменить возможность фокусировки на представлении, вызовите isFocusable(). В режиме сенсорного управления вы можете запросить, разрешает ли View фокусировку с помощью setFocusable().
Вы можете изменить это в isFocusableInTouchMode().
setFocusableInTouchMode()
На устройствах с Android 9 (уровень API 28) или более поздней версии начальный фокус не назначается. Вместо этого вы должны явно запросить начальный фокус, если это необходимо.
Перемещение фокуса основано на алгоритме, который находит ближайший элемент в заданном направлении. В редких случаях алгоритм по умолчанию может не соответствовать поведению, которое ожидает разработчик. В таких случаях вы можете явно переопределить значения с помощью следующих XML-атрибутов в файле макета: nextFocusDown, nextFocusLeft, nextFocusRight и nextFocusUp. Добавьте один из этих атрибутов в представление, из которого уходит фокус. В качестве значения атрибута укажите идентификатор объекта View, на который должен быть переведен фокус. Пример:
<LinearLayout android:orientation="vertical" ... > <Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" ... /> <Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" ... /> </LinearLayout>
Обычно в таком вертикальном макете переход вверх от первой кнопки или вниз от второй ни к чему не приводит. Теперь, когда верхняя кнопка определила нижнюю как nextFocusUp (и наоборот), фокус навигации будет циклически перемещаться сверху вниз и снизу вверх.
Если вы хотите сделать View фокусируемым (по умолчанию это не так), добавьте в объявление макета XML-атрибут android:focusable.
Установите значение true. Вы также можете объявить View как фокусируемый в режиме касания с помощью android:focusableInTouchMode.
Чтобы запросить фокус для определенного представления, вызовите метод .requestFocus()
Чтобы получать уведомления о том, что представление получает или теряет фокус, используйте , как описано в разделе Прослушиватели событий.onFocusChange()