您好,登錄后才能下訂單哦!
Android開發(fā)中實(shí)現(xiàn)手勢識別功能的方法?相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
觸摸交互中的概念
常用事件
首先要了解一些常用的事件:
ACTION_DOWN:第一個手指按下
ACTION_UP:第一個手指抬起
ACTION_POINTER_DOWN:第二、三、四等等手指按下
ACTION_POINTER_UP: 第二、三、四等等手指抬起
ACTION_MOVE: 手指移動
ACTION_OUTSIDE:手指移出了屏幕
ACTION_CANCEL:收到前驅(qū)事件比如ACTION_DOWN后,后續(xù)事件被父控件攔截的情況下產(chǎn)生
上面我們可以看到,除了第一個手指有唯一的action down和action up事件觸發(fā),后續(xù)其它手指的按下和移動,都觸發(fā)的是同一個事件。那么這個時候就可能涉及到對不同手指區(qū)分的邏輯處理。
MotionEvent
MotionEvent中用action code和坐標(biāo)值描述了觸摸運(yùn)動的軌跡,action code值描述了運(yùn)動狀態(tài)的改變,坐標(biāo)值描述了軌跡的位置和一起其它信息。
比如 ACTION_DOWN表明手指開始觸碰到屏幕,X和Y的坐標(biāo)軸值表明了當(dāng)前的位置。
上面僅僅是基本的單指操作,但是現(xiàn)在很多設(shè)備都提供多指操作的功能。多個手指每個手指都被在第一次觸碰屏幕的時候分配一個pointer id,直到這個手指離開相應(yīng)的pointer id才變無效。當(dāng)?shù)谝粋€手指按下時,會觸發(fā)ACTION_DOWN,ACTION_MOVE一系列的事件,同時當(dāng)?shù)诙€手指按下的時候,又會觸發(fā) ACTION_POINTER_DOWN事件,此后兩個手指移動的時候,只會觸發(fā)ACTION_MOVE事件。當(dāng)一個ACTION_MOVE觸發(fā)的時,通過使用 getPointerId(第幾個手指) 方法去獲取pointer id明確是哪一個手指,然后使用使用findPointerIndex 方法去獲得pointer index,pointer index代表了這一個MotionEvent事件中哪一個是當(dāng)前pointer對應(yīng)的事件。
MotionEvent事件捆綁
結(jié)合上面的概念,再來說一下MotionEvent的捆綁。為了處理效率,安卓中會把MOVE動作中多個坐標(biāo)點(diǎn)捆綁在一個MotionEvent中,對于單個手指操作,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)); } } }
可以看到,一個MotionEvent中,可能包括多個手指的動作信息,以及一些歷史信息。
事件分發(fā)機(jī)制
MotionEvent代表觸摸后響應(yīng)的事件,安卓中的視圖是按照視圖樹構(gòu)建而成的,點(diǎn)擊之后,會生成點(diǎn)擊事件MotionEvent并沿樹傳遞。
與事件分發(fā)有關(guān)的方法有:
public boolean dispatchTouchEvent(MotionEvent ev) 事件分發(fā)
public boolean onInterceptTouchEvent(MotionEvent ev) 事件攔截
public boolean onTouchEvent(MotionEvent ev) 事件響應(yīng)
在一個ViewGroup中通常會具有以上三個方法,可以進(jìn)行事件的分發(fā)、攔截和響應(yīng),而在一個View中因?yàn)闆]有子View,所以只能進(jìn)行事件的處理,也就只有onTouchEvent方法。
dispatchTouchEvent
事件分發(fā)的過程中,會以深度遍歷的方式進(jìn)行分發(fā)。分以下情況:
返回true,則事件會分發(fā)給當(dāng)前View,由當(dāng)前View消費(fèi)。
返回false,將事件返回給父View進(jìn)行消費(fèi)
默認(rèn) super.dispatchTouchEvent(ev),會調(diào)用當(dāng)前View的 onInterceptTouchEvent 進(jìn)行攔截處理。
一般情況下,我們不會去重寫view的分發(fā)過程,而是著重處理事件的攔截和響應(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無法處理,之間會返回上級有上級View的onTouchEvent處理,一直 向上傳遞直到事件被消費(fèi)。
如果返回true則會接收并消費(fèi)該事件
如果返回 super.onTouchEvent(ev) 默認(rèn)處理事件的邏輯和返回 false 時相同。
注意,對于View而非ViewGroup來說,只具有onTouchEvent方法。所以在一個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); } }
同樣,一個View處理觸摸事件,還可以設(shè)置監(jiān)聽器onTouchListener,不過要注意的是onTouchListener的優(yōu)先級比onTouch要高,如果其中返回了true,那么將不會調(diào)用onTouch方法。
手勢探測
onTouch中我們可以通過MotionEvent獲取觸摸點(diǎn)的坐標(biāo)信息,但是關(guān)于某些手勢比如點(diǎn)擊、滑動還需要進(jìn)行我們自己的邏輯處理。在這里Android本身提供了一些手勢判別的功能。這樣在onTouch方法中,我們只需要把MotionEvent傳遞給手勢監(jiān)聽器處理即可,同時實(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)聽那么多事件,那么可以寫一個監(jiān)聽類繼承GestureDetector.SimpleOnGestureListener并實(shí)現(xiàn)其中的方法。
如果要監(jiān)聽觸摸的速度,那么可以通過VelocityTracker來監(jiān)聽:
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;
通過將MotionEvent加入VelocityTracker中,可以通過computeCurrentVelocity算出速度。
看完上述內(nèi)容,你們掌握Android開發(fā)中實(shí)現(xiàn)手勢識別功能的方法的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。