在我前面的自定義裁剪窗口的代碼中,我把裁剪的view放在了大的scrollview里,這樣就出現了程序只能觸發scrollview,無法操作我的裁剪窗口。所以我加了那篇博客下面最后兩段代碼。其實我遇到這個問題的時候是在一個scrollview里添加了一個Edittext,我限制了Edittext的高度,所以edittext里面的內容過多時會自己產生滾動條。但我怎么也無法觸發edittext的滾動事件,后來查了資料就明白了。后來一直沒對它做記錄,現在對這個知識點做一個總結。
原文地址請保留http://www.cnblogs.com/rossoneri/p/3994662.html
老規矩,先推薦博客資料,畢竟從別人那學來的知識,好東西要分享:
[Android實例] [版主原創]ScrollView嵌套ScrollView
[Android實例] [版主原創]android之ScrollView里嵌套ListView
其實這倆是一個作者,好多人也轉載這個(吐槽下那些只轉載不留原文地址的,太惡心)。但講的有點羅嗦了。。其實下面有更清楚的:
Android ScrollView嵌套ScrollView滾動的問題解決辦法
這個是從老外那轉來的,老外網站我這打不開了。。反正講的很簡單清楚:告訴你不建議嵌套scrollview,但嵌套了也沒關系,再告訴你出現問題的原因,然后給你解決方案,就兩行代碼而已blabla。
最后就是理解原理的關鍵:解決問題代碼方法的說明
android 事件處理機制之requestDisallowInterceptTouchEvent
一萬個贊!
好了,如果上面的你看完了,也就基本弄明白了,到這里就可以結束了。
總結:
這種情況說簡單點就是上層的view的onTouchEvent和下層view的onTouchEvent重疊了,系統無法判斷你想activate哪個onTouchEvent,然后系統就很蛋疼,跑程序的時候上層動一下,下層動一下,結果哪一層都動不起來(還是有小幅度偏移的)。這就是view之間的事,不限於scrollview,listView,gridView,自定義view什么的,所以標題定的也有問題,說白了就是view的嵌套。但為了文章能被更多的搜索到,我就是不改標題(什么心態)。
“當有多個層級的View時,在父層級允許的情況下,這個action會一直向下傳遞直到遇到最深層的View。所以touch事件最先調用的是最底層View的onTouchEent”
注意,允許,怎么設置是否允許呢?另外運行程序的時候的確是明顯的,下層view先動,上層view后動,估計默認下父層級都是允許把消息傳到最下層的吧。
“如果View的onTouchEvent接收到某個touch action並作了相應處理,最后有兩種返回方式return true和return false;return true會告訴系統當前的View需要處理這次的touch事件,以后的系統發出的ACTION_MOVE,ACTION_UP還是需要繼續監聽並接收 的,而且這次的action已經被處理掉了,父層的View是不可能觸發onTouchEvent了”
了解一下onTouchEvent的返回值的用途
“所以每一個action最多只能有一個onTouchEvent接口返回true”
多層(>2)情況需要考慮一下
“如果return false,便會通知系統,當前View不關心這一次的touch事件,此時這個action會傳向父級,調用父級View的onTouchEvent。 但是這一次的touch事件之后發出的任何action,該View都不會再接受,onTouchEvent在這一次的touch事件中再也不會觸發,也就是說一旦View返回false,那么之后的ACTION_MOVE,ACTION_UP等ACTION就不會在傳入這個View,但是下一次 touch事件的action還是會傳進來的。”
這里不太明白。代碼里return是在最后一行,所以會先執行前面的ACTION_*吧,return true的話,aiction執行之后再返回true,上層不再響應event可以理解,但false的話,他應該都執行過了啊?再給上層執行?
看了下源碼,view的源碼里onTouchEvent有一個int型的flag,在action觸發前會做個判斷,但自己復寫方法的話。。。我怎么覺得好奇怪。。再想想。。這里面原理多呢。。
1 public boolean onTouchEvent(MotionEvent event) { 2 final int viewFlags = mViewFlags; 3 4 if ((viewFlags & ENABLED_MASK) == DISABLED) { 5 if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { 6 setPressed(false); 7 } 8 // A disabled view that is clickable still consumes the touch 9 // events, it just doesn't respond to them. 10 return (((viewFlags & CLICKABLE) == CLICKABLE || 11 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); 12 } 13 14 if (mTouchDelegate != null) { 15 if (mTouchDelegate.onTouchEvent(event)) { 16 return true; 17 } 18 } 19 20 if (((viewFlags & CLICKABLE) == CLICKABLE || 21 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { 22 switch (event.getAction()) { 23 case MotionEvent.ACTION_UP: 24 ... 25 break; 26 27 case MotionEvent.ACTION_DOWN: 28 ... 29 break; 30 31 case MotionEvent.ACTION_CANCEL: 32 ... 33 break; 34 35 case MotionEvent.ACTION_MOVE: 36 ... 37 break; 38 } 39 return true; 40 } 41 42 return false; 43 }
“在父層級允許的情況下。假設不改變父層級的dispatch方法,在系統調用底層onTouchEvent之前會先調用父View的onInterceptTouchEvent方法判斷,父層View是不是要截獲本次touch事件之后的action。”
看到這里,前面的疑惑有點頭緒,再看源碼
明天畫個流程圖出來
“如果onInterceptTouchEvent返回了true,那么本次touch事件之后的所有action都不會再向深層的View傳遞,統統都會 傳給負層View的onTouchEvent,就是說父層已經截獲了這次touch事件,之后的action也不必詢問 onInterceptTouchEvent,在這次的touch事件之后發出的action時onInterceptTouchEvent不會再次調 用,知道下一次touch事件的來臨。如果onInterceptTouchEvent返回false,那么本次action將發送給更深層的View, 並且之后的每一次action都會詢問父層的onInterceptTouchEvent需不需要截獲本次touch事件。只有ViewGroup才有 onInterceptTouchEvent方法,因為一個普通的View肯定是位於最深層的View,touch事件能夠傳到這里已經是最后一站了,肯 定會調用View的onTouchEvent。”
這段大概看看吧,前面基本了解了。
“對於底層的View來說,有一種方法可以阻止父層的View截獲touch事件,就是調用 getParent().requestDisallowInterceptTouchEvent(true);方法。一旦底層View收到touch的 action后調用這個方法那么父層View就不會再調用onInterceptTouchEvent了,也無法截獲以后的action。”
最后的關鍵方法,就用這個方法即可實現功能~(但最好要先搞懂原理)
最后貼個最早發現這個問題的代碼,scrollview里的edittext,touch子view就設置其屬性true,touch父view就幫子view設置屬性false即可~
1 mEssay.setOnTouchListener(new View.OnTouchListener() { 2 3 @Override 4 public boolean onTouch(View v, MotionEvent event) { 5 // TODO Auto-generated method stub 6 v.getParent().requestDisallowInterceptTouchEvent(true); 7 return false; 8 } 9 }); 10 11 mScrollView.setOnTouchListener(new View.OnTouchListener() { 12 13 @Override 14 public boolean onTouch(View v, MotionEvent event) { 15 // TODO Auto-generated method stub 16 mEssay.getParent().requestDisallowInterceptTouchEvent(false); 17 return false; 18 } 19 });