編輯:關於Android編程
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent 在dispatchTouchEvent中會進行OnTouchListener的判斷,如果OnTouchListener不為null且返回true,則表示事件被消費,onTouchEvent不會被執行;否則執行onTouchEvent。
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { //該方法為空方法,直接忽略之 onUserInteraction(); } //把事件ev交給phoneWindow來處理 if (getWindow().superDispatchTouchEvent(ev)) { //表明整個事件到此結束,處理完畢 return true; } //說明該事件在View中沒有得到處理,由Activity自己處理 //至於怎麼處理博客後面後有說明 return onTouchEvent(ev); }
superDispatchTouchEvent的方法
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor.superDispatchTouchEvent傳給了DecorView
public boolean dispatchTouchEvent(event){
//如果當前View對此事件攔截成功
if(this.onInterceptTouchEvent(event)){
//由當前View對此事件進行處理
//true 表示處理了該事件,false表示沒有處理該事件
return onTouchEvent(event);
}else{//沒有攔截成功
//交給子類來分發攔截處理
return child.dispatchTouchEvent(event);
}
}
所以就有了這張圖片
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxjb2RlPjxjb2RlPlZpZXdHcm91cNPA1Layu7vhttTAub3Yo6zS8s6qy/u1xG9uSW50ZXJjZXB0VG91Y2hFdmVudChNb3Rpb25FdmVudCBldinKvNbVt7W72LXEysdmYWxzZaOh1eLR+URlY29yVmlld7bUtb3AtLXEysK8/k1vdGlvbkV2ZW50vs3Wu9PQt9a3orW919NWaWV3sqLTydfTVmlld7340NDAub3Yus20psDttMvKwrz+wcsuIFZpZXew/MCo1rG907zMs9DT2lZpZXe1xNfTwODS8s6qxuS4uMDgVmlld8O709BvbkludGVyY2VwdFRvdWNoRXZlbnS3vbeoo6zL+dLUw7u3qLbUysK8/r340NDAub3Yo6zI57n71eLW1lZpZXe78cihtb3By8rCvP6jrMTHw7S+zbvh1rTQ0G9uVG91Y2hFdmVudLe9t6ijqLWxyLvV4tKyysfT0Mz1vP61xKOs1eK49sewzOHM9bz+1Nq21M/Cw+ZvblRvdWNot723qNf308O1xMqxuvK74dPQy7XD96OpoaMgPC9jb2RlPjwvY29kZT4NCjxoNCBpZD0="view的dispatchtouchevent">View的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
if (!onFilterTouchEventForSecurity(event)) {
return false;
}
//從這裡看出,onTouch方法是優先於onTouchEvnent方法的
//如果onTouch方法返回了true的話,那麼onTouchEvent方法就沒法執行
//相應的onClick方法也不會去執行
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
//如果這裡返回true的話,那麼也表明此次事件被處理了
return true;
}
//回調onTouchEvent方法
return onTouchEvent(event);
}
setOnTouchListener
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
}
ViewGroup的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
return target.dispatchTouchEvent(ev);
}
onTouchEvent的源碼
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP://抬起事件才會執行onClick
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
..........
if (!mHasPerformedLongPress) {
.........
if (!focusTaken) {
.........
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//這也就說明了onTouchEvent優先於onClick執行,因為onClick方法就在onTouchEvent方法裡執行
//驗證了上面的結論
if (!post(mPerformClick)) {
performClick();
}
}
}
...........
}
break;
}
//此時返回了true,此View處理了該事件,事件不會再往下進行分發
return true;
}
return false;
}
onTouch優先於
onTouchEvent|
onTouchEvent優先於
onClick===>onClick在onTouchEvent裡面執行
返回true dispatchTouchEvent方法會結束執行 然後onClick就不會執行了
//因為該方法返回true,所以dispatchTouchEvent方法會結束執行
//進而導致Button的onClick方法也沒機會執行
btn.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
doClick();
}
});
onInterceptTouchEvent負責對touch事件進行攔截,對於嵌套的view最先執行的是事件攔截方法的是最外層的那個view的onInterceptTouchEvent方法,然後依次執行子視圖的onInterceptTouchEvent,然後在執行子視圖的子視圖的事件攔截方法(當然在這裡假設所有嵌套視圖的onInterceptTouchEvent都會得到執行,讓每個視圖的onInterceptTouchEvent返回false即可)。參照上圖,所以onInterceptTouchEvent執行順序就是A—>B—>C—>D.也就是由父視圖到子視圖傳遞。總之,事件攔截機制是由父視圖開始發起對事件的攔截(出事了老子先上,兒子稍後)。參照上圖當手指觸摸事件時,父視圖A首先發起對該起事件的攔截,如果A攔截失敗,就交給它的子視圖B進行攔截;如果B攔截失敗就交給B的子視圖C再進行攔截..直到某一子視圖對該次事件攔截成功。 某一視圖攔截事件成功與否的判斷標識是onInterceptTouchEvent方法的返回值,當返回true的時候說明攔截成功,返回false的時候說明當前視圖對事件攔截失敗。 下面說說攔截成功的情況,假設C視圖對當前touch事件攔截成功。攔截成功意味著此次事件不會再傳遞到D視圖了。所以此時的D視圖的onInterceptTouchEvent就得不到運行(事件沒法到達了,還攔截誰呢?)。事件攔截成功後,緊接著就會對事件進行處理,處理的方法教給onTouchEvent方法處理。此時C視圖攔截成功,那麼緊接著就會執行C視圖的onTouchEvent方法,這是不是就意味著當前touch事件是由C視圖的onTouchEvent方法來處理的呢?這要由C視圖的onTouchEvent方法的返回值來決定。當C視圖的onTouchEvent返回true的時候,當前事件就由C全權處理,處理的當然是事件的各種action,什麼MotionEvent.ACTION_MOVE,ACTION_UP都交給了C的onTouchEvent方法進行處理。所以此時就可以在C的onTouchEvent方法中進行switch(event.getAction)判斷執行相關邏輯了。如果返回的false,說明C視圖對此事件不做處理或者處理不了,怎麼辦呢?兒子不行老爸來,於是事件就交到了B視圖的onTouchEvent方法中。同樣B對此事件處理與否還是看B的onTouchEvent返回值,具體的解釋就跟C一樣了,不復多言。 在A B C D的onInterceptTouchEvent和onTouchEvent都返回false的情況下,方法執行的順序依次為A.onInterceptTouchEvent–>B.onInterceptTouchEvent–>C.onInterceptTouchEvent–>D.touchEvent(最深的子視圖沒重寫onInterceptTouchEvent)–>C.touchEvent–>B.touchEvent–>A.touchEvent.也就是說攔截事件是父視圖優先有子視圖進行攔截,處理事件是子視圖優先父視圖進行處理。
onInterceptTouchEvent負責對事件進行攔截,攔截成功後交給最先遇到onTouchEvent返回true的那個view進行處理。
Android中touch事件的傳遞,絕對是先傳遞到ViewGroup,再傳遞到View的
當你點擊了某個控件,
首先會去調用該控件
所在布局的dispatchTouchEvent方法,然後在布局的dispatchTouchEvent方法中
找到被點擊的相應控件,再去
調用該控件的dispatchTouchEvent方法。如果我們點擊了MyLayout中的按鈕,會先去調用MyLayout的dispatchTouchEvent方法,可是你會發現MyLayout中並沒有這個方法。那就再到它的父類LinearLayout中找一找,發現也沒有這個方法。那只好繼續再找LinearLayout的父類ViewGroup,你終於在ViewGroup中看到了這個方法,按鈕的dispatchTouchEvent方法就是在這裡調用的
在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進行攔截,onInterceptTouchEvent方法返回true代表不允許事件繼續向子View傳遞,返回false代表不對事件進行攔截,默認返回false。 子View中如果將傳遞的事件消費掉,ViewGroup中將無法接收到任何事件。
以下內容來自圖解 Android 事件分發機制
dispatchTouchEvent和onTouchEvent一旦return true,事件就停止傳遞了 dispatchTouchEvent 和 onTouchEvent return false的時候事件都回傳給父控件的onTouchEvent處理。
對於dispatchTouchEvent 返回 false 的含義應該是:事件停止往子View傳遞和分發同時開始往父控件回溯(父控件的onTouchEvent開始從下往上回傳直到某個onTouchEvent return true),事件分發機制就像遞歸,return false 的意義就是遞歸停止然後開始回溯。 對於onTouchEvent return false 就比較簡單了,它就是不消費事件,並讓事件繼續往父控件的方向從下往上流動。
dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
ViewGroup 和View的這些方法的默認實現就是會讓整個事件安裝U型完整走完,所以 return super.xxxxxx() 就會讓事件依照U型的方向的完整走完整個事件流動路徑),中間不做任何改動,不回溯、不終止,每個環節都走到。如果看到方法return super.xxxxx() 那麼事件的下一個流向就是走U型下一個目標
onInterceptTouchEvent 的作用
默認是不會去攔截的,因為子View也需要這個事件,所以onInterceptTouchEvent攔截器return super.onInterceptTouchEvent()和return false是一樣的,是不會攔截的,事件會繼續往子View的dispatchTouchEvent傳遞。
對於dispatchTouchEvent,onTouchEvent,return true是終結事件傳遞。return false 是回溯到父View的onTouchEvent方法。 ViewGroup 想把自己分發給自己的onTouchEvent,需要攔截器onInterceptTouchEvent方法return true 把事件攔截下來。 ViewGroup的攔截器onInterceptTouchEvent 默認是不攔截的,所以return super.onInterceptTouchEvent()=return false; View 沒有攔截器,為了讓View可以把事件分發給自己的onTouchEvent,View的dispatchTouchEvent默認實現(super)就是把事件分發給自己的onTouchEvent。
ViewGroup和View 的dispatchTouchEvent 是做事件分發,那麼這個事件可能分發出去的四個目標
注:------> 後面代表事件目標需要怎麼做。
1、 自己消費,終結傳遞。------->return true ;
2、 給自己的onTouchEvent處理-------> 調用super.dispatchTouchEvent()系統默認會去調用 onInterceptTouchEvent,在onInterceptTouchEvent return true就會去把事件分給自己的onTouchEvent處理。
3、 傳給子View------>調用super.dispatchTouchEvent()默認實現會去調用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就會把事件傳給子類。
4、 不傳給子View,事件終止往下傳遞,事件開始回溯,從父View的onTouchEvent開始事件從下到上回歸執行每個控件的onTouchEvent------->return false;
注: 由於View沒有子View所以不需要onInterceptTouchEvent 來控件是否把事件傳遞給子View還是攔截,所以View的事件分發調用super.dispatchTouchEvent()的時候默認把事件傳給自己的onTouchEvent處理(相當於攔截),對比ViewGroup的dispatchTouchEvent 事件分發,View的事件分發沒有上面提到的4個目標的第3點。
ViewGroup和View的onTouchEvent方法是做事件處理的,那麼這個事件只能有兩個處理方式:
1、自己消費掉,事件終結,不再傳給誰----->return true;
2、繼續從下往上傳,不消費事件,讓父View也能收到到這個事件----->return false;View的默認實現是不消費的。所以super==false。
ViewGroup的onInterceptTouchEvent方法對於事件有兩種情況:
1、攔截下來,給自己的onTouchEvent處理--->return true;
2、不攔截,把事件往下傳給子View---->return false,ViewGroup默認是不攔截的,所以super==false;
Android 自定義橫向滾動條。當你的橫向字段或者表格很多時候,顯示不下內容,可以使用很想滾動條進行滾動。豎向方面我添加了listview進行添加數據。兩者滾動互不干擾
前言:動態加載在應用開發中有著很重要的地位,當我們項目越來越大,我們可以通過插件化來減少應用的內存,然後動態加載那些插件。還有一個方面,如果我們的應用頻繁的更新,頻繁的發
1.普通側滑效果圖: 思路:通過自定義View繼承HorizontalScrollView,然後重寫onMeasure(),onLayout(),onTouch
首先,我們要知道什麼是數據持久化。數據持久化就是指那些內存中的瞬時數據保存到存儲設備中,保證即使手機在關機的情況下,這些數據不會丟失。保存在內存中的數據是處於瞬時狀態,保