前言
工作這段時(shí)間以來(lái),陸陸續(xù)續(xù)看了一些類的源碼裆熙,但是都沒(méi)有詳細(xì)的記錄下來(lái)端礼,只是在筆記上記錄了一些功能性的代碼,導(dǎo)致一段時(shí)間之后入录,完完全全忘記了蛤奥。所以今天打算回顧梳理系統(tǒng)里的手勢(shì)判斷類 GestureDetector 。由于我們的項(xiàng)目是一個(gè)拍照類美化的 App纷跛,所以在之前的版本需求里喻括,產(chǎn)品提了一個(gè)在預(yù)覽頁(yè)里加上 A/B 對(duì)比圖的需求。這里的 A贫奠、B 指的是原圖和添加了濾鏡之后的圖片唬血,拍照進(jìn)入預(yù)覽頁(yè)顯示原圖A望蜡,但是長(zhǎng)按一小段時(shí)間之后顯示加了濾鏡的圖片B。當(dāng)時(shí)第一時(shí)間想到的就是使用 GestureDetector 拷恨,但是預(yù)覽頁(yè)的自定義 View 已經(jīng)有了自己的事件攔截處理的邏輯脖律,不想加上 GestureDetector 使代碼更為復(fù)雜,所以便萌生了參考 GestureDetector 的想法腕侄。
概述
當(dāng)我們的手指接觸到屏幕的時(shí)候小泉,會(huì)產(chǎn)生許多事件,例如 down,move,up,scroll,fling 等等冕杠,一般情況下微姊, 如果我們需要捕獲這些事件做一些簡(jiǎn)單的出來(lái),只需要重寫 View 的 onTouchEvent(event) 即可分预,但是如果是一些比較復(fù)雜的手勢(shì)兢交,比如單擊,雙擊笼痹,長(zhǎng)按配喳,左右滑動(dòng),快速滑動(dòng)凳干,就需要我們?nèi)プ鲆恍╊~外的判斷邏輯了晴裹,所以系統(tǒng)給我們提供了 GestureDetector ,以此簡(jiǎn)化我們的開(kāi)發(fā)工作救赐。接下來(lái)看一下系統(tǒng)提供的 GestureDetector .OnGestureListener 接口涧团,以下接口方法中,返回 True 代碼事件被消費(fèi)了净响。源碼分析基于android- 25!
-
onDown(MotionEvent e)
當(dāng)我們觸碰到屏幕 Down 事件發(fā)生時(shí)少欺,會(huì)立即回調(diào)此方法。
-
onShowPress(MotionEvent e)
用戶已經(jīng)觸發(fā)了 Down 事件馋贤,但是并沒(méi)有觸發(fā) Move 或者 Up 事件赞别。這方法通常是作為一種反饋,讓用戶知道他手指按下屏幕這一動(dòng)作已經(jīng)被識(shí)別了配乓。
-
onSingleTapUp(MotionEvent e)
當(dāng)一個(gè)以 Up 事件結(jié)束的單擊發(fā)生時(shí)仿滔,回調(diào)此方法。
-
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
當(dāng)一個(gè)滑動(dòng)事件發(fā)生時(shí)回調(diào)此方法犹芹,其中 e1 代表滑動(dòng)開(kāi)始的第一個(gè) Down 事件崎页,e2 代表當(dāng)前手指正在滑動(dòng)的 Move 事件,distanceX 代表在 X 軸上前一次滑動(dòng)和當(dāng)次滑動(dòng)的差值量腰埂,注意飒焦,distanceX 并不是 e1 和 e2 在 X 方向上的差值量。distanceY 同 distanceX 類似,只不過(guò)代表的是 Y 軸牺荠。
-
onLongPress(MotionEvent e)
長(zhǎng)按事件發(fā)生時(shí)回調(diào)此方法
-
onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
快速滑動(dòng)發(fā)生時(shí)回調(diào)此方法翁巍,其中 e1 代表觸發(fā)這個(gè)動(dòng)作的 Down 事件,e2 代表這個(gè)動(dòng)作結(jié)束手指抬起的 Up 事件休雌, velocityX 代表在 X 方向上的滑動(dòng)速度灶壶,velocityY 代表在 Y 方向上的滑動(dòng)速度,數(shù)值單位均為 pixels /s 杈曲,即每秒劃過(guò)多少個(gè)像素驰凛。
在 GestureDetector 類中,我們還發(fā)現(xiàn)一個(gè)用于確認(rèn)單擊以及雙擊的接口 GestureDetector .OnDoubleTapListener
-
onSingleTapConfirmed(MotionEvent e)
當(dāng)確認(rèn)一個(gè)單擊事件發(fā)生時(shí)回調(diào)此方法担扑,也就是說(shuō)恰响,手勢(shì)判斷器判斷到這是一次單擊事件,用戶不會(huì)再次點(diǎn)擊屏幕魁亦。
-
onDoubleTap(MotionEvent e)
當(dāng)雙擊事件發(fā)生時(shí)回調(diào)此方法
-
onDoubleTapEvent(MotionEvent e)
雙擊事件發(fā)生過(guò)程中都會(huì)回調(diào)此方法渔隶,包括 Down 羔挡、Move 洁奈、Up 事件。
源碼分析
1.構(gòu)造方法
public GestureDetector(OnGestureListener listener) {
this(null, listener, null);
}
public GestureDetector(Context context, OnGestureListener listener) {
this(context, listener, null);
}
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
if (handler != null) {
//初始化Handler绞灼,用于處理延時(shí)消息
mHandler = new GestureHandler(handler);
} else {
mHandler = new GestureHandler();
}
//設(shè)置回調(diào)監(jiān)聽(tīng)器
mListener = listener;
if (listener instanceof OnDoubleTapListener) {
setOnDoubleTapListener((OnDoubleTapListener) listener);
}
if (listener instanceof OnContextClickListener) {
setContextClickListener((OnContextClickListener) listener);
}
init(context);
}
private void init(Context context) {
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
mIsLongpressEnabled = true;
// Fallback to support pre-donuts releases
int touchSlop, doubleTapSlop, doubleTapTouchSlop;
if (context == null) {
//noinspection deprecation
touchSlop = ViewConfiguration.getTouchSlop();
doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
//noinspection deprecation
mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
} else {
final ViewConfiguration configuration = ViewConfiguration.get(context);
//滑動(dòng)的時(shí)候利术,滑動(dòng)數(shù)值大于這個(gè)值才認(rèn)為滑動(dòng)開(kāi)始
touchSlop = configuration.getScaledTouchSlop();
doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
//雙擊位置之間的最大距離,大于這個(gè)數(shù)值不認(rèn)為是雙擊
doubleTapSlop = configuration.getScaledDoubleTapSlop();
//手指拋的最小速度
mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
//手指拋的最大速度
mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
}
//計(jì)算平方低矮,后面用到
mTouchSlopSquare = touchSlop * touchSlop;
mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
}
從以上可以看出印叁,構(gòu)造 GestureDetector 的過(guò)程中,主要做的就是初始化一些變量军掂,并且獲取一些數(shù)值作為閾值轮蜕。此外,還需要注意到 GestureHandler 蝗锥,手勢(shì)的
private class GestureHandler extends Handler {
//...忽略構(gòu)造函數(shù)
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PRESS:
//回調(diào) onShowPress 方法
mListener.onShowPress(mCurrentDownEvent);
break;
case LONG_PRESS:
//處理長(zhǎng)按消息
dispatchLongPress();
break;
case TAP:
// If the user's finger is still down, do not count it as a tap
if (mDoubleTapListener != null) {
if (!mStillDown) {
//此時(shí)跃洛,手指已抬起,回調(diào)確認(rèn)單擊的方法
mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
} else {
//未抬起终议,在手指抬起的時(shí)候回調(diào)確認(rèn)單擊的方法
mDeferConfirmSingleTap = true;
}
}
break;
default:
throw new RuntimeException("Unknown message " + msg); //never
}
}
}
2. 處理手勢(shì)信息
接下來(lái)也就到了 GestureDetector 的核心部分汇竭,也就是如何處理手勢(shì)信息,并判斷是具體的哪種手勢(shì)
public boolean onTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
}
final int action = ev.getAction();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
//初始化速度追蹤器穴张,并計(jì)算速度
mVelocityTracker.addMovement(ev);
//下面需要得出 X 细燎、Y 方向上的中心焦點(diǎn),如果是多點(diǎn)觸碰皂甘,需要計(jì)算平均值得出中心焦點(diǎn)
// 這里判斷這個(gè) action 是不是有非主要的手指抬起來(lái)了玻驻,如果是的話,記錄它的index,下面計(jì)算的時(shí)候
//忽略這個(gè)觸點(diǎn)
final boolean pointerUp =
(action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
// Determine focal point
float sumX = 0, sumY = 0;
final int count = ev.getPointerCount();
for (int i = 0; i < count; i++) {
if (skipIndex == i) continue;
sumX += ev.getX(i);
sumY += ev.getY(i);
}
//pointerUp 為 true偿枕,需要排除掉
final int div = pointerUp ? count - 1 : count;
final float focusX = sumX / div;
final float focusY = sumY / div;
boolean handled = false;
//根據(jù)不同的 action,做不同的處理
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
//...
break;
case MotionEvent.ACTION_POINTER_UP:
//...
break;
case MotionEvent.ACTION_DOWN:
//...
break;
case MotionEvent.ACTION_MOVE:
//...
break;
case MotionEvent.ACTION_UP:
//...
break;
case MotionEvent.ACTION_CANCEL:
//...
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
}
return handled;
}
從以上可以看出璧瞬,onTouchEvent() 方法主要統(tǒng)一對(duì)不同的輸入事件作統(tǒng)一的處理佛析。
2.1 Down 事件處理
case MotionEvent.ACTION_DOWN:
if (mDoubleTapListener != null) {
//如果設(shè)置了雙擊監(jiān)聽(tīng)器,進(jìn)入此結(jié)構(gòu)體
boolean hadTapMessage = mHandler.hasMessages(TAP);
//第一次點(diǎn)擊的時(shí)候發(fā)送了TAG類型的消息彪蓬,取消掉寸莫,防止出錯(cuò)
if (hadTapMessage) mHandler.removeMessages(TAP);
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
// This is a second tap
//判斷到這是第二次點(diǎn)擊,設(shè)置變量值档冬,回調(diào)相關(guān)方法
mIsDoubleTapping = true;
// Give a callback with the first tap of the double-tap
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
// Give a callback with down event of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else {
// This is a first tap
// 這是一個(gè)第一次的單擊膘茎,先發(fā)送延時(shí)消息,如果在這段時(shí)間之內(nèi)沒(méi)有再次點(diǎn)擊酷誓,GestureHandler 里就會(huì)處理 這個(gè) TAG 類型的消息
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
//設(shè)置相關(guān)變量
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
mCurrentDownEvent = MotionEvent.obtain(ev);
mAlwaysInTapRegion = true;
mAlwaysInBiggerTapRegion = true;
mStillDown = true;
mInLongPress = false;
mDeferConfirmSingleTap = false;
if (mIsLongpressEnabled) {
//允許長(zhǎng)按披坏,發(fā)送延時(shí)的長(zhǎng)按消息,在 TAP_TIMEOUT + LONGPRESS_TIMEOUT 這段時(shí)間之內(nèi)盐数,
//如果沒(méi)有其他手勢(shì)操作(移動(dòng)手指棒拂、抬起手指),會(huì)認(rèn)為是長(zhǎng)按手勢(shì)玫氢,并且回調(diào)LongPress 方法帚屉。
mHandler.removeMessages(LONG_PRESS);
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
//一小段時(shí)間之后,回調(diào) onShowPress(MotionEvent e) 方法漾峡,反饋給用戶
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
handled |= mListener.onDown(ev);
break;
//判斷第二次點(diǎn)擊是否有效
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
MotionEvent secondDown) {
//第一次點(diǎn)擊后攻旦,超過(guò)了限定范圍,認(rèn)為無(wú)效
if (!mAlwaysInBiggerTapRegion) {
return false;
}
final long deltaTime = secondDown.getEventTime() - firstUp.getEventTime();
//第一次點(diǎn)擊的手指抬起和第二次點(diǎn)擊的手指落下生逸,中間時(shí)間間隔不符合要求牢屋,無(wú)效
if (deltaTime > DOUBLE_TAP_TIMEOUT || deltaTime < DOUBLE_TAP_MIN_TIME) {
return false;
}
int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
//范圍沒(méi)超過(guò)mDoubleTapSlopSquare ,認(rèn)為有效
return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
}
Down 事件總結(jié):
- 單擊判斷:如果接受到一次單擊事件槽袄,會(huì)先發(fā)送 TAG 類型的延遲消息烙无,在這段時(shí)間內(nèi),如果消息沒(méi)被取消遍尺,則會(huì)進(jìn)入 GestureHandler 的 TAG 確認(rèn)操作截酷。
- 雙擊判斷:已經(jīng)有了第一次的單擊事件,這時(shí)候需要判斷第二次點(diǎn)擊和第一次單擊的距離狮鸭、時(shí)間合搅,條件符合則認(rèn)為是雙擊,回調(diào)相關(guān)方法歧蕉。
- 長(zhǎng)按判斷:點(diǎn)擊的時(shí)候就發(fā)送延遲的長(zhǎng)按消息灾部,要是這段時(shí)間內(nèi)沒(méi)被取消,就回調(diào)長(zhǎng)按的方法惯退。
2.2 Move 事件處理
case MotionEvent.ACTION_MOVE:
//已經(jīng)觸發(fā)長(zhǎng)按操作赌髓,一系列事件可以認(rèn)為是長(zhǎng)按,接下來(lái)不做其他處理
if (mInLongPress || mInContextClick) {
break;
}
final float scrollX = mLastFocusX - focusX;
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
//上面說(shuō)過(guò),雙擊過(guò)程中 onDoubleTapEvent(ev) 方法會(huì)被回調(diào)锁蠕,這個(gè)過(guò)程還沒(méi)結(jié)束
//回調(diào)這個(gè)方法
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
//第一次 Down 事件里夷野,mAlwaysInTapRegion 為 true
final int deltaX = (int) (focusX - mDownFocusX);
final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
// 距離超過(guò)一個(gè)數(shù)值,可以認(rèn)為是滑動(dòng)荣倾,進(jìn)入onScroll 模式悯搔,回調(diào)相關(guān)函數(shù)
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
mAlwaysInTapRegion = false;
//移除之前發(fā)送的延遲消息
mHandler.removeMessages(TAP);
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
}
if (distance > mDoubleTapTouchSlopSquare) {
mAlwaysInBiggerTapRegion = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
//最后的條件判斷,繼續(xù) onScroll 回調(diào)
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
}
break;
Move 事件總結(jié):
- 如果已經(jīng)觸發(fā)了長(zhǎng)按舌仍,并且回調(diào)了長(zhǎng)按的相關(guān)函數(shù)妒貌,就會(huì)認(rèn)為接下來(lái)的 Move 事件都會(huì)直接返回。
- 如果是雙擊铸豁,因?yàn)殡p擊過(guò)程還未結(jié)束灌曙,所以 Move 事件會(huì)一直回調(diào) onDoubleTapEvent(ev) 方法。
- 如果滑動(dòng)的距離超過(guò)一定的數(shù)值平方节芥,會(huì)進(jìn)入 onScroll 模式在刺,接下來(lái)的滑動(dòng),只要超過(guò) 1px头镊,就繼續(xù)回調(diào) onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 方法蚣驼。
2.3 Up 事件處理
case MotionEvent.ACTION_UP:
mStillDown = false;
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap
//是雙擊的過(guò)程,回調(diào)onDoubleTapEvent(ev) 方法
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
//長(zhǎng)按的過(guò)程拧晕,移除可能存在的 TAP 消息
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
//如果點(diǎn)擊之后沒(méi)有移動(dòng)隙姿,或者移動(dòng)的距離沒(méi)有超過(guò)一定的數(shù)值,可以認(rèn)為只是單擊而已
handled = mListener.onSingleTapUp(ev);
if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
//GestureHandler 里 mDeferConfirmSingleTap 被設(shè)置為 true厂捞,這里在手指抬起時(shí)進(jìn)行回調(diào)
mDoubleTapListener.onSingleTapConfirmed(ev);
}
} else if (!mIgnoreNextUpEvent) {
//進(jìn)入這個(gè)結(jié)構(gòu)體的時(shí)候,說(shuō)明前面沒(méi)有觸發(fā)雙擊或者長(zhǎng)按操作队丝,并且滑動(dòng)的距離也超過(guò)了最小值靡馁,
//mAlwaysInTapRegion 被設(shè)置為 false
// A fling must travel the minimum tap distance
final VelocityTracker velocityTracker = mVelocityTracker;
final int pointerId = ev.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
final float velocityY = velocityTracker.getYVelocity(pointerId);
final float velocityX = velocityTracker.getXVelocity(pointerId);
if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
//速度超過(guò)了最小值,可以認(rèn)為是一個(gè)拋的動(dòng)作
handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
}
}
//回收机久,方便下面使用
if (mPreviousUpEvent != null) {
mPreviousUpEvent.recycle();
}
// Hold the event we obtained above - listeners may have changed the original.
mPreviousUpEvent = currentUpEvent;
if (mVelocityTracker != null) {
// This may have been cleared when we called out to the
// application above.
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mIsDoubleTapping = false;
mDeferConfirmSingleTap = false;
mIgnoreNextUpEvent = false;
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;
Up 事件總結(jié):
- 判斷是雙擊的過(guò)程臭墨,則繼續(xù)回調(diào) onDoubleTapEvent(ev) 方法,結(jié)束這個(gè)過(guò)程.
- 如果是長(zhǎng)按的膘盖,也結(jié)束這個(gè)過(guò)程胧弛。
- 說(shuō)明前面沒(méi)有觸發(fā)雙擊或者長(zhǎng)按操作,并且滑動(dòng)的距離也超過(guò)了最小值侠畔,最后手指抬起時(shí)结缚,速度超過(guò)最小值,可以認(rèn)為是拋的動(dòng)作软棺,回調(diào) onFling() 方法红竭。
- 如果點(diǎn)擊之后沒(méi)有移動(dòng),或者移動(dòng)的距離沒(méi)有超過(guò)一定的數(shù)值,認(rèn)為只是簡(jiǎn)單的點(diǎn)擊茵宪,結(jié)束這個(gè)過(guò)程最冰。之前 點(diǎn)擊的時(shí)候 發(fā)生了延遲的 TAG消息,但是需要手指抬起才能回調(diào) onSingleTapConfirmed() 方法稀火,如果處理延遲消息的時(shí)候暖哨,手指還未抬起,則先設(shè)置變量標(biāo)記(處理邏輯如下圖)凰狞,等手指抬起再回調(diào)鹿蜀。
case TAP:
// If the user's finger is still down, do not count it as a tap
if (mDoubleTapListener != null) {
if (!mStillDown) {
//此時(shí),手指已抬起服球,回調(diào)確認(rèn)單擊的方法
mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
} else {
//未抬起茴恰,在手指抬起的時(shí)候回調(diào)確認(rèn)單擊的方法
mDeferConfirmSingleTap = true;
}
}
2.4 多點(diǎn)觸控處理
case MotionEvent.ACTION_POINTER_DOWN:
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;
// Cancel long press and taps
//有其他手指落下,移除所有消息斩熊,重置標(biāo)記變量
cancelTaps();
break;
case MotionEvent.ACTION_POINTER_UP:
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;
// Check the dot product of current velocities.
// If the pointer that left was opposing another velocity vector, clear.
mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
final int upIndex = ev.getActionIndex();
final int id1 = ev.getPointerId(upIndex);
final float x1 = mVelocityTracker.getXVelocity(id1);
final float y1 = mVelocityTracker.getYVelocity(id1);
for (int i = 0; i < count; i++) {
if (i == upIndex) continue;
final int id2 = ev.getPointerId(i);
final float x = x1 * mVelocityTracker.getXVelocity(id2);
final float y = y1 * mVelocityTracker.getYVelocity(id2);
//如果剩下的手指速度方向是和抬起那根手指的速度相反方向的往枣,就說(shuō)明不是fling,清空速度監(jiān)聽(tīng)
final float dot = x + y;
if (dot < 0) {
mVelocityTracker.clear();
break;
}
}
break;
private void cancelTaps() {
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
mHandler.removeMessages(TAP);
mIsDoubleTapping = false;
mAlwaysInTapRegion = false;
mAlwaysInBiggerTapRegion = false;
mDeferConfirmSingleTap = false;
mInLongPress = false;
mInContextClick = false;
mIgnoreNextUpEvent = false;
}
在 onTouchEvent(MotionEvent ev) 方法的前面粉渠,我們對(duì)于多點(diǎn)觸控的處理都是取平均值的分冈,對(duì)多個(gè)手指的UP和DOWN事件處理,其實(shí)就是做一些取消操作而讓多點(diǎn)觸摸不影響單點(diǎn)觸摸的應(yīng)用霸株,例如在多個(gè)手指落下的時(shí)候取消點(diǎn)擊信息等雕沉。
后記
第一次分析源碼,如果有錯(cuò)誤或者需要補(bǔ)充的去件,歡迎留言討論坡椒。