- dispatchTouchEvent
- 代碼不長,重要的地方有兩個,分別是調用mOnTouchListener.onTouch(this, event)和onTouchEvent(event)
- 12行默認返回false
- 29到34行:如果mOnTouchListener不爲空,執行mOnTouchListener.onTouch(this, event)方法,這個方法就是public void setOnTouchListener(OnTouchListener l) { getListenerInfo().mOnTouchListener = l;},如果onTouch(this, event)方法返回true,result=true
- 36到38,如果上面onTouch方法返回true,不會執行下面的onTouchEvent(event)方法,接下來看onTouchEvent方法
- onTouchEvent
- 這個代碼稍微長一點,還是抓重點分析
- 代碼中兩次出現了下面的判斷
(((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
第一次:7到16行,狀態爲DISABLE即代碼中調用view.setEnable(true)時,返回上面的結果
第二次:23行到133行,即函數結束前的一個大括號,滿足上面的條件返回true這裏說明只要View滿足clickable、longClickable和contextClickable(api23開始支持)的任何一項,onTouchEvent返回true,返回true意味着可以消費onTouchEvent事件,使得事件不再繼續向上傳遞。
舉個例子:下面的代碼onTouchEvent事件的
case MotionEvent.ACTION_MOVE:不會執行,後面會說明原因。
public class MyView extends TextView {
protected static final String TAG = "MyButton";
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "MyView onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "MyView onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "MyView onTouchEvent ACTION_UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
} - 26到131行對event三種動作的解析,核心代碼出來了
- 先說ACTION_DOWN,Touch的第一個動作計劃司ACOIN_DOWN,80到105行.這裏面設計三個動作分別是onclick,longclick和CheckForTap
- 88行,判斷view是否在可以滑動的組件裏面,例如:scrollview和listview
public boolean isInScrollingContainer() {
ViewParent p = getParent();
while (p != null && p instanceof ViewGroup) {
if (((ViewGroup) p).shouldDelayChildPressedState()) {
return true;
}
p = p.getParent();
}
return false;
} - 94到99行,如果發生touch事件的view在可以滑動的組件裏面,執行postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());這個方法的意思是延遲
TAP_TIMEOUT(100毫秒)後,執行CheckForTap()方法,如下:
private final class CheckForTap implements Runnable {
public float x;
public float y;
@Override
public void run() {
mPrivateFlags &= ~PFLAG_PREPRESSED;
setPressed(true, x, y);
checkForLongClick(ViewConfiguration.getTapTimeout(), x, y);
}
}
三行代碼:- 設置狀態mPrivateFlags爲false,這個後面後用到
- 設置press狀態
- checkForLongClick,longClick事件,注意這裏的參數傳的是ViewConfiguration.getTapTimeout(), 即檢查滑動延遲的TAP_TIMEOUT(100毫秒) 這個函數下面詳細說明
- 繼續向下103行checkForLongClick(0, x, y)有調用了這個函數
- 很簡單就是調用mOnLongClickListener.onLongClick方法,注意下面的代碼做了對LONG_CLICKABLE的判斷,也即是說如果設置了view.setOnLongClickListener(false)則無法執行onLongClick事件
private void checkForLongClick(int delayOffset, float x, float y) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() - delayOffset);
}
} - onLongClick觸發的條件是執行完ACTION_DOWN動作後500毫秒,這裏的兩個動作都採用了postDelay的方式執行,這裏是爲了方便ACTION_UP或ACTION_MOVE動作發生時,可以直接將未執行的延遲任務remove掉
- 很簡單就是調用mOnLongClickListener.onLongClick方法,注意下面的代碼做了對LONG_CLICKABLE的判斷,也即是說如果設置了view.setOnLongClickListener(false)則無法執行onLongClick事件
- ACTION_DOWN動作說完了,總結一下就是:
- 如果執行ACTION_DOWN的view在可以滑動的組件內,猶豫不能確定是不是滑動作,給了一個100毫秒的延遲動作
- 延遲500毫秒執行longClick事件
- 88行,判斷view是否在可以滑動的組件裏面,例如:scrollview和listview
- 27到78行下面看最複雜的ACTION_UP事件
- 注意看33行的focusTaken判斷,再看50行,連起來就是如果view可以獲得焦點並且已經獲得了焦點則不能再執行click事件(例如edittext不能執行onClick)
- 51到53說明了即使是立即執行的onClick方法也推薦使用post,目的就是使view點擊後視圖狀態先發生變化,真是煞費苦心啊
- 45到47行,removeLongPressCallback();此時如果postDelay500ms的longClock事件沒有發生,將其remove掉
- 剩下的兩個時間ACTION_CANCEL和ACTION_MOVE兩個事件比較簡單,做了一些動作處理,設置Press狀態,移除longClick和TapCallBack動作
- 至此onTouchEvent解析完畢,有點羅嗦,總結一下
- 代碼中setEnable和setClickable的作用相信很多人都曾困擾過,setEnable爲false不能點擊,setLongClickable爲false不能長按,setClickable爲false對onClick事件沒有直接影響,就是名字唬人而已
- View的onClickable、onLongClickable,onContextClickable任一成立,該View會消費此onTouchEvent事件,這個非常重要
- longClick事件在TOUCH_DOWN中連續摁下500ms執行,onClick在TOUCH_UP中執行,兩者可以同時執行,longClick返回false時。處於焦點狀態的View不能執行onClick事件
- 未完待續
安卓事件分發框架
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.