觸摸事件分發(fā)
用戶在使用 Andriod 系統(tǒng)的時(shí)候會(huì)不斷的和我們的 App 進(jìn)行各種類型的交互(類似點(diǎn)擊、滑動(dòng)等等),“事件”就是一個(gè)非常有效的用來(lái)收集用戶行為的方式。在前面章節(jié)有提到過(guò):Android 系統(tǒng)采用一個(gè)先進(jìn)先出(FIFO)隊(duì)列來(lái)維護(hù)一個(gè)事件 List。在每個(gè)事件出列的時(shí)候,Android 系統(tǒng)會(huì)根據(jù)一定的規(guī)則對(duì)這些事件做分發(fā),我們可以通過(guò)接收這些事件來(lái)對(duì)用戶的操作進(jìn)行相應(yīng)的處理。
1. 事件處理相關(guān)概念
-
Event Listeners Registration:
事件監(jiān)聽器注冊(cè)。在接收事件之前完成注冊(cè),目的是告訴系統(tǒng)當(dāng)前需要監(jiān)聽某個(gè)事件,從而在事件觸發(fā)的時(shí)候系統(tǒng)會(huì)回調(diào)已注冊(cè)接口中的方法。 -
Event Listeners:
事件監(jiān)聽器。顧名思義,當(dāng)某個(gè)事件被用戶行為觸發(fā)的時(shí)候,系統(tǒng)會(huì)回調(diào)所有已注冊(cè)相應(yīng)事件監(jiān)聽器的回調(diào)方法,從而完成事件的分發(fā)。 -
Event Handlers:
事件處理。當(dāng)事件發(fā)生時(shí),系統(tǒng)會(huì)回調(diào)我們注冊(cè)過(guò)的接口,所以可以在回調(diào)方法中對(duì)事件進(jìn)行處理
2. 觸摸事件類型
一次完整的觸摸事件是從手指觸摸屏幕一直到離開屏幕,這個(gè)過(guò)程可能非常短暫,但是對(duì)于 Android 系統(tǒng)而言發(fā)生了很多狀態(tài)的切換,常用的主要有以下幾種:
- ACTION_DOWN:
手指剛接觸到的狀態(tài) - ACTION_POINTER_DOWN:
在第一個(gè)狀態(tài)之后其他的點(diǎn)發(fā)生了觸摸 - ACTION_MOVE:
手指觸摸滑動(dòng) - ACTION_POINTER_UP:
除了第一個(gè)觸摸點(diǎn)以外的觸摸點(diǎn)離開屏幕 - ACTION_UP:
第一個(gè)接觸的點(diǎn)離開屏幕 - ACTION_CANCEL:
滑動(dòng)時(shí)移動(dòng)到無(wú)效區(qū)域
3. 觸摸事件監(jiān)聽方法
3.1 注冊(cè)觸摸監(jiān)聽器
為了能夠順利接收到以上事件,并進(jìn)行相應(yīng)處理,我們需要在事件發(fā)生之前完成注冊(cè),方法如下:
public boolean onTouchEvent(MotionEvent ev){
switch(ev.getAction()){
case MotionEvent.ACTION_DOWN:{
break;
}
case MotionEvent.ACTION_MOVE:{
break;
}
return true;
}
}
3.2 獲取觸摸坐標(biāo)
在接收到各個(gè)狀態(tài)的事件之后,我們需要從中獲取當(dāng)前的觸摸/滑動(dòng)坐標(biāo),如下:
float x = ev.getX();
float y = ev.getY();
4. 觸摸事件示例
在實(shí)際開發(fā)中,大多數(shù)時(shí)候我們需要監(jiān)聽的是DOWN
、MOVE
以及UP
三個(gè)事件,我們可以在DOWN
事件中獲取到觸摸的起點(diǎn),然后在MOVE
過(guò)程中獲取并不斷追蹤用戶的滑動(dòng)坐標(biāo),最后在UP
事件中獲取終點(diǎn)進(jìn)而結(jié)束本次 Touch 事件。
4.1 布局文件
首先編寫布局文件,我們需要 4 個(gè) TextView,分別用來(lái)顯示觸摸起點(diǎn)的 X 軸、Y 軸坐標(biāo),以及滑動(dòng)時(shí)的 X 軸、Y 軸偏移量,最后創(chuàng)建一個(gè) View 用作觸摸事件的接收源。內(nèi)容非常簡(jiǎn)單,代碼如下:
<?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="點(diǎn)擊的X軸坐標(biāo)"
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="點(diǎn)擊的Y軸坐標(biāo)"
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="移動(dòng)位置的X軸坐標(biāo)"
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="移動(dòng)位置的Y軸坐標(biāo)"
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="點(diǎn)我開始滑動(dòng)"
android:textColor="#ff5480ff"
android:textSize="35sp" />
</RelativeLayout>
4.2 觸摸事件的注冊(cè)、監(jiān)聽以及處理
在 MainActivity 中我們對(duì) id 為 touch 的 TextView 注冊(cè)觸摸監(jiān)聽器,然后在DOWN
中獲取觸摸起點(diǎn),并寫在對(duì)應(yīng)的 TextView 中;隨后在MOVE
中實(shí)時(shí)獲取滑動(dòng)偏移量,也在對(duì)應(yīng)的 TextView 中進(jìn)行實(shí)時(shí)更新,代碼如下:
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、注冊(cè)觸摸監(jiān)聽器
touch.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int actionPeformed = event.getAction();
// 2、判斷當(dāng)前觸摸狀態(tài)
switch (actionPeformed) {
case MotionEvent.ACTION_DOWN: {
// 3、在不同狀態(tài)中進(jìn)行觸摸事件處理
downXAxis = event.getX();
downYAxis = event.getY();
downX.setText("按下的位置橫坐標(biāo):" + downXAxis);
downY.setText("按下的位置縱坐標(biāo):" + 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("移動(dòng)距離的橫坐標(biāo):" + xAxis);
moveY.setText("移動(dòng)距離的縱坐標(biāo):" + yAxis);
break;
}
}
return true;
}
});
}
}
編譯運(yùn)行,效果如下:
觸摸左下角的“點(diǎn)我開始滑動(dòng)”,當(dāng)前觸摸的坐標(biāo)就會(huì)在 TextView 中展示了,然后滑動(dòng)手指,隨著滑動(dòng)的偏移量的變化,也會(huì)在 TextView 中進(jìn)行同步更新。
5. 小結(jié)
本節(jié)講解了觸摸事件的分發(fā)處理方式,首先介紹了事件處理的幾個(gè)常用概念及一次觸摸事件中切換的幾種狀態(tài),然后講述了觸摸事件處理的幾個(gè)重要方法,最后用一個(gè)完整例子演示了觸摸事件的監(jiān)聽處理。這個(gè)是繼onClick()
事件后最常用的一個(gè)事件,也是絕大多數(shù)事件分發(fā)的基礎(chǔ)事件,因?yàn)楦鞣N交互事件都是從觸摸開始的,所以大家即使用的不多也一定要掌握使用方法及其中的基本原理。