Android手勢(shì)識(shí)別功能
現(xiàn)在智能手機(jī)基本都是觸摸操作,點(diǎn)擊按鈕是一種交互方式,同時(shí)手勢(shì)相關(guān)的操作,比如滑動(dòng)等等同樣是很重要的交互方式。這篇文章是對(duì)安卓手勢(shì)交互相關(guān)知識(shí)點(diǎn)的整理和總結(jié),主要來(lái)源基于官方文檔。
觸摸交互中的概念
常用事件
首先要了解一些常用的事件:
ACTION_DOWN:第一個(gè)手指按下
ACTION_UP:第一個(gè)手指抬起
ACTION_POINTER_DOWN:第二、三、四等等手指按下
ACTION_POINTER_UP: 第二、三、四等等手指抬起
ACTION_MOVE: 手指移動(dòng)
ACTION_OUTSIDE:手指移出了屏幕
ACTION_CANCEL:收到前驅(qū)事件比如ACTION_DOWN后,后續(xù)事件被父控件攔截的情況下產(chǎn)生
上面我們可以看到,除了第一個(gè)手指有唯一的action down和action up事件觸發(fā),后續(xù)其它手指的按下和移動(dòng),都觸發(fā)的是同一個(gè)事件。那么這個(gè)時(shí)候就可能涉及到對(duì)不同手指區(qū)分的邏輯處理。
MotionEvent
MotionEvent中用action code和坐標(biāo)值描述了觸摸運(yùn)動(dòng)的軌跡,action code值描述了運(yùn)動(dòng)狀態(tài)的改變,坐標(biāo)值描述了軌跡的位置和一起其它信息。
比如 ACTION_DOWN表明手指開(kāi)始觸碰到屏幕,X和Y的坐標(biāo)軸值表明了當(dāng)前的位置。
上面僅僅是基本的單指操作,但是現(xiàn)在很多設(shè)備都提供多指操作的功能。多個(gè)手指每個(gè)手指都被在第一次觸碰屏幕的時(shí)候分配一個(gè)pointer id,直到這個(gè)手指離開(kāi)相應(yīng)的pointer id才變無(wú)效。當(dāng)?shù)谝粋€(gè)手指按下時(shí),會(huì)觸發(fā)ACTION_DOWN,ACTION_MOVE一系列的事件,同時(shí)當(dāng)?shù)诙€(gè)手指按下的時(shí)候,又會(huì)觸發(fā) ACTION_POINTER_DOWN事件,此后兩個(gè)手指移動(dòng)的時(shí)候,只會(huì)觸發(fā)ACTION_MOVE事件。當(dāng)一個(gè)ACTION_MOVE觸發(fā)的時(shí),通過(guò)使用 getPointerId(第幾個(gè)手指) 方法去獲取pointer id明確是哪一個(gè)手指,然后使用使用findPointerIndex 方法去獲得pointer index,pointer index代表了這一個(gè)MotionEvent事件中哪一個(gè)是當(dāng)前pointer對(duì)應(yīng)的事件。
MotionEvent事件捆綁
結(jié)合上面的概念,再來(lái)說(shuō)一下MotionEvent的捆綁。為了處理效率,安卓中會(huì)把MOVE動(dòng)作中多個(gè)坐標(biāo)點(diǎn)捆綁在一個(gè)MotionEvent中,對(duì)于單個(gè)手指操作,getX返回的是最近一點(diǎn)的坐標(biāo),getHistoricalX 返回的是之前的坐標(biāo)??聪旅嬉欢未a:
void printSamples(MotionEvent ev) {
//獲取MotionEvent中捆綁的坐標(biāo)點(diǎn)
final int historySize = ev.getHistorySize();
//獲取手指數(shù)目
final int pointerCount = ev.getPointerCount();
for (int h = 0; h < historySize; h++) {
System.out.printf("At time %d:", ev.getHistoricalEventTime(h));
for (int p = 0; p < pointerCount; p++) {
System.out.printf(" pointer %d: (%f,%f)",
ev.getPointerId(p), ev.getHistoricalX(p, h), ev.getHistoricalY(p, h));
}
}
}
可以看到,一個(gè)MotionEvent中,可能包括多個(gè)手指的動(dòng)作信息,以及一些歷史信息。
事件分發(fā)機(jī)制
MotionEvent代表觸摸后響應(yīng)的事件,安卓中的視圖是按照視圖樹(shù)構(gòu)建而成的,點(diǎn)擊之后,會(huì)生成點(diǎn)擊事件MotionEvent并沿樹(shù)傳遞。
與事件分發(fā)有關(guān)的方法有:
public boolean dispatchTouchEvent(MotionEvent ev) 事件分發(fā)
public boolean onInterceptTouchEvent(MotionEvent ev) 事件攔截
public boolean onTouchEvent(MotionEvent ev) 事件響應(yīng)
在一個(gè)ViewGroup中通常會(huì)具有以上三個(gè)方法,可以進(jìn)行事件的分發(fā)、攔截和響應(yīng),而在一個(gè)View中因?yàn)闆](méi)有子View,所以只能進(jìn)行事件的處理,也就只有onTouchEvent方法。
dispatchTouchEvent
事件分發(fā)的過(guò)程中,會(huì)以深度遍歷的方式進(jìn)行分發(fā)。分以下情況:
返回true,則事件會(huì)分發(fā)給當(dāng)前View,由當(dāng)前View消費(fèi)。
返回false,將事件返回給父View進(jìn)行消費(fèi)
默認(rèn) super.dispatchTouchEvent(ev),會(huì)調(diào)用當(dāng)前View的 onInterceptTouchEvent 進(jìn)行攔截處理。
一般情況下,我們不會(huì)去重寫(xiě)view的分發(fā)過(guò)程,而是著重處理事件的攔截和響應(yīng)。
onInterceptTouchEvent
如果返回true,則攔截當(dāng)前事件,交由onTouchEvent處理
如果返回false,則不攔截當(dāng)前事件,交由子View的dispatchTouchEvent處理
如果調(diào)用默認(rèn) super.onInterceptTouchEvent,則攔截當(dāng)前事件。
onTouchEvent
如果返回false,表明當(dāng)前View無(wú)法處理,之間會(huì)返回上級(jí)有上級(jí)View的onTouchEvent處理,一直 向上傳遞直到事件被消費(fèi)。
如果返回true則會(huì)接收并消費(fèi)該事件
如果返回 super.onTouchEvent(ev) 默認(rèn)處理事件的邏輯和返回 false 時(shí)相同。
注意,對(duì)于View而非ViewGroup來(lái)說(shuō),只具有onTouchEvent方法。所以在一個(gè)View中,處理事件響應(yīng)的典型代碼如下:
public class MainActivity extends Activity {
...
// This example shows an Activity, but you would use the same approach if
// you were subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
int action = MotionEventCompat.getActionMasked(event);
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN");
return true;
case (MotionEvent.ACTION_MOVE) :
Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP");
return true;
case (MotionEvent.ACTION_CANCEL) :
Log.d(DEBUG_TAG,"Action was CANCEL");
return true;
case (MotionEvent.ACTION_OUTSIDE) :
Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
"of current screen element");
return true;
default :
//當(dāng)前View不處理事件,交由上層處理。
return super.onTouchEvent(event);
}
}
同樣,一個(gè)View處理觸摸事件,還可以設(shè)置監(jiān)聽(tīng)器onTouchListener,不過(guò)要注意的是onTouchListener的優(yōu)先級(jí)比onTouch要高,如果其中返回了true,那么將不會(huì)調(diào)用onTouch方法。
手勢(shì)探測(cè)
onTouch中我們可以通過(guò)MotionEvent獲取觸摸點(diǎn)的坐標(biāo)信息,但是關(guān)于某些手勢(shì)比如點(diǎn)擊、滑動(dòng)還需要進(jìn)行我們自己的邏輯處理。在這里Android本身提供了一些手勢(shì)判別的功能。這樣在onTouch方法中,我們只需要把MotionEvent傳遞給手勢(shì)監(jiān)聽(tīng)器處理即可,同時(shí)實(shí)現(xiàn)接口中相應(yīng)的回調(diào)方法:
private GestureDetectorCompat mDetector;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDetector = new GestureDetectorCompat(this,this);
mDetector.setOnDoubleTapListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event){
this.mDetector.onTouchEvent(event);
// Be sure to call the superclass implementation
return super.onTouchEvent(event);
}
如果不需要監(jiān)聽(tīng)那么多事件,那么可以寫(xiě)一個(gè)監(jiān)聽(tīng)類(lèi)繼承GestureDetector.SimpleOnGestureListener并實(shí)現(xiàn)其中的方法。
如果要監(jiān)聽(tīng)觸摸的速度,那么可以通過(guò)VelocityTracker來(lái)監(jiān)聽(tīng):
switch(action) {
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
else {
mVelocityTracker.clear();
}
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
Log.d("", "X velocity: " +
VelocityTrackerCompat.getXVelocity(mVelocityTracker,
pointerId));
Log.d("", "Y velocity: " +
VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.recycle();
break;
通過(guò)將MotionEvent加入VelocityTracker中,可以通過(guò)computeCurrentVelocity算出速度。
(未完待續(xù)。。。)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 札記:android手勢(shì)識(shí)別功能實(shí)現(xiàn)(利用MotionEvent)
- 深入理解Android手勢(shì)識(shí)別
- Android實(shí)現(xiàn)Gesture手勢(shì)識(shí)別用法分析
- Android View進(jìn)行手勢(shì)識(shí)別詳解
- Android基礎(chǔ)開(kāi)發(fā)之手勢(shì)識(shí)別
- Android應(yīng)用開(kāi)發(fā)中觸摸屏手勢(shì)識(shí)別的實(shí)現(xiàn)方法解析
- android開(kāi)發(fā)之為activity增加左右手勢(shì)識(shí)別示例
- android創(chuàng)建手勢(shì)識(shí)別示例代碼
- android使用gesturedetector手勢(shì)識(shí)別示例分享
- 理解Android的手勢(shì)識(shí)別提高APP的用戶(hù)體驗(yàn)
相關(guān)文章
Android小程序?qū)崿F(xiàn)音樂(lè)播放列表
這篇文章主要為大家詳細(xì)介紹了Android小程序?qū)崿F(xiàn)音樂(lè)播放列表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Android仿微信雷達(dá)掃描效果的實(shí)現(xiàn)方法
最近看了一個(gè)視頻講了一種微信雷達(dá)掃描的實(shí)現(xiàn)方案,借鑒了一下,自己也寫(xiě)一個(gè)玩玩,所以下面這篇文章主要給大家介紹了利用Android模仿微信雷達(dá)掃描效果的實(shí)現(xiàn)方法,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-06-06
Android實(shí)現(xiàn)抽獎(jiǎng)轉(zhuǎn)盤(pán)實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)抽獎(jiǎng)轉(zhuǎn)盤(pán)實(shí)例代碼,可以應(yīng)用于Android游戲開(kāi)發(fā)中的一個(gè)應(yīng)用,需要的朋友可以參考下2014-07-07
Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果(一)
這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)動(dòng)畫(huà)效果的第一篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
在Linux下通過(guò)命令行打包Android應(yīng)用的方法
這篇文章主要介紹了在Linux下通過(guò)命令行打包Android應(yīng)用的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Android模擬實(shí)現(xiàn)支付寶螞蟻森林效果
這篇文章主要為大家詳細(xì)介紹了如何利用Android模擬實(shí)現(xiàn)支付寶中螞蟻森林的動(dòng)畫(huà)效果,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-09-09
Android開(kāi)發(fā)-之環(huán)境的搭建(圖文詳解)
這篇文章主要介紹了Android開(kāi)發(fā)-之環(huán)境的搭建(圖文詳解),具有一定的參考價(jià)值,有興趣的可以了解一下。2016-11-11
解析Android中實(shí)現(xiàn)滑動(dòng)翻頁(yè)之ViewFlipper的使用詳解
有一些場(chǎng)景,我們需要向用戶(hù)展示一系列的頁(yè)面。比如我們正在開(kāi)發(fā)一個(gè)看漫畫(huà)的應(yīng)用,可能就需要向用戶(hù)展示一張一張的漫畫(huà)圖片,用戶(hù)使用手指滑動(dòng)屏幕,可以在前一幅漫畫(huà)和后一幅漫畫(huà)之間切換。這個(gè)時(shí)候ViewFlipper就是一個(gè)很好的選擇2013-05-05
Android實(shí)現(xiàn)基于滑動(dòng)的SQLite數(shù)據(jù)分頁(yè)加載技術(shù)(附demo源碼下載)
這篇文章主要介紹了Android實(shí)現(xiàn)基于滑動(dòng)的SQLite數(shù)據(jù)分頁(yè)加載技術(shù),涉及Android針對(duì)SQLite數(shù)據(jù)的讀取及查詢(xún)結(jié)果的分頁(yè)顯示功能相關(guān)實(shí)現(xiàn)技巧,末尾還附帶demo源碼供讀者下載參考,需要的朋友可以參考下2016-07-07

