觸摸事件分發
用戶在使用 Andriod 系統的時候會不斷的和我們的 App 進行各種類型的交互(類似點擊、滑動等等),“事件”就是一個非常有效的用來收集用戶行為的方式。在前面章節有提到過:Android 系統采用一個先進先出(FIFO)隊列來維護一個事件 List。在每個事件出列的時候,Android 系統會根據一定的規則對這些事件做分發,我們可以通過接收這些事件來對用戶的操作進行相應的處理。
1. 事件處理相關概念
-
Event Listeners Registration:
事件監聽器注冊。在接收事件之前完成注冊,目的是告訴系統當前需要監聽某個事件,從而在事件觸發的時候系統會回調已注冊接口中的方法。 -
Event Listeners:
事件監聽器。顧名思義,當某個事件被用戶行為觸發的時候,系統會回調所有已注冊相應事件監聽器的回調方法,從而完成事件的分發。 -
Event Handlers:
事件處理。當事件發生時,系統會回調我們注冊過的接口,所以可以在回調方法中對事件進行處理
2. 觸摸事件類型
一次完整的觸摸事件是從手指觸摸屏幕一直到離開屏幕,這個過程可能非常短暫,但是對于 Android 系統而言發生了很多狀態的切換,常用的主要有以下幾種:
- ACTION_DOWN:
手指剛接觸到的狀態 - ACTION_POINTER_DOWN:
在第一個狀態之后其他的點發生了觸摸 - ACTION_MOVE:
手指觸摸滑動 - ACTION_POINTER_UP:
除了第一個觸摸點以外的觸摸點離開屏幕 - ACTION_UP:
第一個接觸的點離開屏幕 - ACTION_CANCEL:
滑動時移動到無效區域
3. 觸摸事件監聽方法
3.1 注冊觸摸監聽器
為了能夠順利接收到以上事件,并進行相應處理,我們需要在事件發生之前完成注冊,方法如下:
public boolean onTouchEvent(MotionEvent ev){
switch(ev.getAction()){
case MotionEvent.ACTION_DOWN:{
break;
}
case MotionEvent.ACTION_MOVE:{
break;
}
return true;
}
}
3.2 獲取觸摸坐標
在接收到各個狀態的事件之后,我們需要從中獲取當前的觸摸/滑動坐標,如下:
float x = ev.getX();
float y = ev.getY();
4. 觸摸事件示例
在實際開發中,大多數時候我們需要監聽的是DOWN
、MOVE
以及UP
三個事件,我們可以在DOWN
事件中獲取到觸摸的起點,然后在MOVE
過程中獲取并不斷追蹤用戶的滑動坐標,最后在UP
事件中獲取終點進而結束本次 Touch 事件。
4.1 布局文件
首先編寫布局文件,我們需要 4 個 TextView,分別用來顯示觸摸起點的 X 軸、Y 軸坐標,以及滑動時的 X 軸、Y 軸偏移量,最后創建一個 View 用作觸摸事件的接收源。內容非常簡單,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:transitionGroup="true"
tools:context=".MainActivity">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="Android 事件處理"
android:textSize="35dp" />
<TextView
android:id="@+id/down_x"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:layout_alignStart="@+id/title"
android:layout_marginTop="30dp"
android:hint="點擊的X軸坐標"
android:textColor="@color/colorPrimary" />
<TextView
android:id="@+id/down_y"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/down_x"
android:layout_alignStart="@+id/down_x"
android:layout_marginTop="10dp"
android:hint="點擊的Y軸坐標"
android:textColor="@color/colorPrimary" />
<TextView
android:id="@+id/move_x"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/down_y"
android:layout_alignStart="@+id/down_y"
android:layout_marginTop="60dp"
android:hint="移動位置的X軸坐標"
android:textColor="@color/colorPrimaryDark" />
<TextView
android:id="@+id/move_y"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/move_x"
android:layout_alignStart="@+id/move_x"
android:hint="移動位置的Y軸坐標"
android:textColor="@color/colorPrimaryDark" />
<TextView
android:id="@+id/touch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="點我開始滑動"
android:textColor="#ff5480ff"
android:textSize="35sp" />
</RelativeLayout>
4.2 觸摸事件的注冊、監聽以及處理
在 MainActivity 中我們對 id 為 touch 的 TextView 注冊觸摸監聽器,然后在DOWN
中獲取觸摸起點,并寫在對應的 TextView 中;隨后在MOVE
中實時獲取滑動偏移量,也在對應的 TextView 中進行實時更新,代碼如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
float xAxis = 0f;
float yAxis = 0f;
float downXAxis = 0f;
float downYAxis = 0f;
TextView downX, downY, moveX, moveY;
TextView touch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
downX = findViewById(R.id.down_x);
downY = findViewById(R.id.down_y);
moveX = findViewById(R.id.move_x);
moveY = findViewById(R.id.move_y);
touch = findViewById(R.id.touch);
// 1、注冊觸摸監聽器
touch.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int actionPeformed = event.getAction();
// 2、判斷當前觸摸狀態
switch (actionPeformed) {
case MotionEvent.ACTION_DOWN: {
// 3、在不同狀態中進行觸摸事件處理
downXAxis = event.getX();
downYAxis = event.getY();
downX.setText("按下的位置橫坐標:" + downXAxis);
downY.setText("按下的位置縱坐標:" + downYAxis);
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = event.getX();
final float y = event.getY();
final float dx = x - downXAxis;
final float dy = y - downYAxis;
xAxis += dx;
yAxis += dy;
moveX.setText("移動距離的橫坐標:" + xAxis);
moveY.setText("移動距離的縱坐標:" + yAxis);
break;
}
}
return true;
}
});
}
}
編譯運行,效果如下:
觸摸左下角的“點我開始滑動”,當前觸摸的坐標就會在 TextView 中展示了,然后滑動手指,隨著滑動的偏移量的變化,也會在 TextView 中進行同步更新。
5. 小結
本節講解了觸摸事件的分發處理方式,首先介紹了事件處理的幾個常用概念及一次觸摸事件中切換的幾種狀態,然后講述了觸摸事件處理的幾個重要方法,最后用一個完整例子演示了觸摸事件的監聽處理。這個是繼onClick()
事件后最常用的一個事件,也是絕大多數事件分發的基礎事件,因為各種交互事件都是從觸摸開始的,所以大家即使用的不多也一定要掌握使用方法及其中的基本原理。