public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();//觸摸點(diǎn)x坐標(biāo)
final float y = event.getY();//觸摸點(diǎn)y坐標(biāo)
final int viewFlags = mViewFlags;//view的標(biāo)志,表示view的各種狀態(tài)
final int action = event.getAction();//事件的類型,如down,move,up
/*
* 此段if作用:當(dāng)view為disabled時(shí),根據(jù)按下狀態(tài),點(diǎn)擊狀態(tài)等標(biāo)記決定是否消耗事件。
*/
//view的標(biāo)志經(jīng)過二進(jìn)制位與計(jì)算,結(jié)果若為Disabled(view被禁用了,無效了);源碼顯示常量DISABLED(禁用) == ENABLED_MASK(激活_屏蔽)==0x00000020。假如viewFlags也等于此常量,if(true)
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {//如果手指離開屏幕(action_up),并且標(biāo)識(shí)為按下((mPrivateFlags & PFLAG_PRESSED)結(jié)果不為0)
setPressed(false);//設(shè)置按下的狀態(tài)為未按下(view禁用)
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
//這兩行注釋是連在一起的。譯文:一個(gè)無效視圖被點(diǎn)擊仍然會(huì)消耗觸摸事件,只是不會(huì)響應(yīng)它們。
return (((viewFlags & CLICKABLE) == CLICKABLE //是否為點(diǎn)擊
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) // 或長按點(diǎn)擊
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);//或上下文點(diǎn)擊(筆觸點(diǎn)擊,鼠標(biāo)右鍵等)
}
/*
* 此段if作用:如果存在觸摸委托對(duì)象,用它來輔助管理事件。
*/
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {//將事件交付委托
return true; //如果代理返回true執(zhí)行到此,消耗掉事件
}
}
/*
* 此段if作用:當(dāng)view不是禁用狀態(tài),并且事件也沒有交付TouchDelegate進(jìn)行處理,于是自己處理。
*/
// 此處和上方view禁用時(shí)return塊代碼一致,只是view禁用時(shí),無需處理
if (((viewFlags & CLICKABLE) == CLICKABLE || //點(diǎn)擊
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || //長按點(diǎn)擊
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { // 上下文點(diǎn)擊。
switch (action) {//事件的動(dòng)作類型
case MotionEvent.ACTION_UP: // 離開
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;// 位與計(jì)算。預(yù)警是否按下,否false,是true
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { //按下或者預(yù)備按下,都會(huì)繼續(xù)
// take focus if we don't have it already and we should in
// touch mode. 譯文:如果沒有焦點(diǎn),我應(yīng)該在觸摸模式設(shè)置它
boolean focusTaken = false;//取得焦點(diǎn)的bool標(biāo)識(shí)
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { // 如果view可獲取焦點(diǎn),并且可以在觸摸模式下獲取焦點(diǎn),并且當(dāng)前沒有焦點(diǎn)。
focusTaken = requestFocus();//為view請(qǐng)求焦點(diǎn),返回并設(shè)置是否取得焦點(diǎn)的bool標(biāo)識(shí)
}
if (prepressed) { //view預(yù)備按下。
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it. 譯文:設(shè)置此視圖的按下狀態(tài),并為動(dòng)畫提示提供觸摸坐標(biāo)。
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {//如果長按點(diǎn)擊已被執(zhí)行,并且下一個(gè)事件不被忽略
// This is a tap, so remove the longpress check. 譯文:這是一個(gè)閥門,所以移除長按檢測(cè)
removeLongPressCallback();//移除長按檢測(cè)定時(shí)器
// Only perform take click actions if we were in the pressed state. 譯文:如果我們?cè)诎聪聽顟B(tài)下,只執(zhí)行點(diǎn)擊動(dòng)作。
if (!focusTaken) {//如果獲取焦點(diǎn)不成功
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start. 譯文:使用一個(gè)Runnable然后發(fā)送它,倒不如直接調(diào)用performClick()。這可以在單擊操作啟動(dòng)之前更新視圖的其他視覺狀態(tài)。
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {//如果發(fā)送mPerformClick到消息隊(duì)列中,如果不成功
performClick();//直接調(diào)用PerformClick()方法,執(zhí)行click
}
}
}
if (mUnsetPressedState == null) {//如果未設(shè)置點(diǎn)擊狀態(tài)的Runnable為空
mUnsetPressedState = new UnsetPressedState();//new 一個(gè)。用來取消pressed的
}
if (prepressed) {//如果預(yù)備點(diǎn)擊
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());//發(fā)送runable,子view點(diǎn)擊狀態(tài)耗時(shí)
} else if (!post(mUnsetPressedState)) {//如果非預(yù)備點(diǎn)擊,且發(fā)送mUnsetPressedState到消息隊(duì)列不成功
// If the post failed, unpress right now
mUnsetPressedState.run();//runable 跑起來
}
removeTapCallback();//移除閥門
}
mIgnoreNextUpEvent = false;//忽略下一個(gè)事件:false
break;
case MotionEvent.ACTION_DOWN: //動(dòng)作:點(diǎn)下
mHasPerformedLongPress = false;//未執(zhí)行長按,才剛剛down,肯定未發(fā)生長按,所以初始使其值為false
if (performButtonActionOnTouchDown(event)) {//如果在觸摸事件中執(zhí)行按鈕動(dòng)作,break跳出。按鈕搶占焦點(diǎn)?使view點(diǎn)擊失效?
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container. 譯文:走上層次結(jié)構(gòu),以確定我們是否在滾動(dòng)容器。
boolean isInScrollingContainer = isInScrollingContainer();//是否在滾動(dòng)容器
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll. 譯文:對(duì)于滾動(dòng)容器內(nèi)的視圖,延遲短時(shí)間的按下,以防滾動(dòng)。
if (isInScrollingContainer) {//如果在滾動(dòng)
mPrivateFlags |= PFLAG_PREPRESSED;//mPrivateFlags = mPrivateFlags | PFLAG_PREPRESSED;位或
if (mPendingCheckForTap == null) {//如果沒有待辦檢測(cè)閥門,也是一個(gè)runable
mPendingCheckForTap = new CheckForTap();//new 一個(gè)
}
mPendingCheckForTap.x = event.getX();//點(diǎn)下的x坐標(biāo)
mPendingCheckForTap.y = event.getY();//點(diǎn)下的y坐標(biāo)
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());//發(fā)送延遲時(shí)間,以檢測(cè)是長按還是普通點(diǎn)擊。
} else {//不在滾動(dòng)
// Not inside a scrolling container, so show the feedback right away. 譯文:不在滾動(dòng)的容器中,李即顯示反饋
setPressed(true, x, y);//設(shè)置點(diǎn)擊
checkForLongClick(0, x, y);//檢測(cè)長按點(diǎn)擊。此時(shí)不滾動(dòng),延遲為0
}
break;
case MotionEvent.ACTION_CANCEL: //動(dòng)作:取消。所以將所有點(diǎn)擊動(dòng)作還原、清零
setPressed(false);// 設(shè)為為點(diǎn)擊
removeTapCallback();//移除閥門
removeLongPressCallback();//移除長按閥門
mInContextButtonPress = false;//上下文按鈕點(diǎn)擊false
mHasPerformedLongPress = false;//長按點(diǎn)擊未執(zhí)行
mIgnoreNextUpEvent = false;//不再忽略下一個(gè)事件
break;
case MotionEvent.ACTION_MOVE: //動(dòng)作:滑動(dòng)(不放開,持續(xù))
drawableHotspotChanged(x, y);//將坐標(biāo)點(diǎn)傳下去。也許view本身或其子view熱點(diǎn)改變,需要傳播給Drawable,以作出相關(guān)改變
// Be lenient about moving outside of buttons. 譯文:對(duì)移動(dòng)從而超出范圍的按鈕寬讓
if (!pointInView(x, y, mTouchSlop)) {//如果坐標(biāo)不在view上
// Outside button
removeTapCallback();//移除閥門
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {//不在view上,卻還是按下(?)
// Remove any future long press/tap checks. 譯文:移除任何未來的長按/閥門檢查
removeLongPressCallback();//移除長按
setPressed(false);//未點(diǎn)擊
}
}
break;
}
return true;//只要發(fā)生了點(diǎn)擊,長按點(diǎn)擊、,上下文點(diǎn)擊,就消耗掉事件
}
return false;//默認(rèn)不消耗事件
}
讀完,有一種吐血三千丈,腦袋十個(gè)大的感覺。
另查到資料說:
| 正常點(diǎn)擊 | |
|---|---|
| 0~500毫秒 | Press |
| 500+毫秒 | LongPress |
| 容器滾動(dòng)時(shí) | |
|---|---|
| 0~100毫秒 | Prepress(預(yù)按下) |
| 100~500毫秒 | Press(按下) |
| 500+毫秒 | LongPress() |