View的滑動沖突深入理解

由于這部分的內(nèi)容涉及的底層知識較多,沒有讀過源碼小伙伴相對比較難以理解铐达。許多小伙伴遇到滑動沖突的時候只是去知其然而不是去知其所以然限次。其實大家沒必要害怕去接觸復雜的內(nèi)容于宙,其實他們也都是由多個細節(jié)方面的特點堆積形成的扛稽。就此部分的內(nèi)容談一談我自己的看法吁峻,期待各位的不吝賜教。

滑動沖突的產(chǎn)生

那么滑動沖突時如何產(chǎn)生的呢在张?界面中只要在內(nèi)外倆層同時可以滑動的時候就會產(chǎn)生滑動沖突用含,導致內(nèi)外倆層只有一層可以滑動。

場景一:外部和內(nèi)部倆層滑動方向不一致


內(nèi)外層的滑動效果可以通過倆種方式來實現(xiàn)帮匾,ViewPager + Fragment和ScrollView + Fragment啄骇。對于前者來說系統(tǒng)在內(nèi)部已經(jīng)解決了滑動沖突,而后者需要手動解決瘟斜。這就需要小伙伴們了解一些基礎(chǔ)的事件分發(fā)機制的知識缸夹。

場景二:外部和內(nèi)部倆層滑動方向一致


實現(xiàn)方式同場景一,不同的是因為內(nèi)外倆層的滑動方向一致螺句,也就是說當手指滑動的時候虽惭,系統(tǒng)無法確定用戶是想讓那一層滑動。進而會導致要么只有一層滑動蛇尚,要么內(nèi)外倆層都可以滑動但是比較卡頓趟妥。

場景三:主要是針對場景一和二的嵌套


就是針對場景一和場景二的嵌套。其實也沒有看起來這么復雜佣蓉,可以理解為幾個沖突的疊加。簡單來說就是將其拆分成多個場景二或者場景一來處理亲雪。

滑動沖突的解決思路

對于場景一來說勇凭,滑動類型(水平,垂直)可以根據(jù)滑動路徑與水平方向的夾角义辕,或者是水平方向和垂直方向的距離差(dy - dx)虾标,或者是水平和垂直方向上的速度差,然后根據(jù)滑動是水平滑動還是豎直滑動進一步取決于誰來攔截當前事件灌砖。對于場景二璧函,場景三來說比較特殊傀蚌,無法根據(jù)場景一的思路來解決。一般是可以在業(yè)務(wù)找到一個突破點進行相應(yīng)的處理蘸吓。這里將不再贅述善炫。


滑動沖突的解決方式

針對上述的場景一般有倆種方式去解決。分別是外部攔截內(nèi)部攔截库继。

外部攔截(父容器優(yōu)先)就是由父容器首先決定是否消耗事件箩艺,然后才會傳遞給子元素。相比內(nèi)部攔截更簡單宪萄,也符合View的事件分發(fā)機制艺谆,是解決滑動沖突的優(yōu)先選擇。通過重寫父容器的onInterceptTouchEvent方法實現(xiàn)拜英。針對不同的滑動沖突静汤,只需要修改父容器需要消耗此事件的條件即可,外部攔截的邏輯框架如下:

public boolean?onInterceptTouchEvent(MotionEvent event) {

boolean intercepted =?false;

int?x = ?(int)event.getX();

int?y = ?(int)event.getY();

switch(event.getAction()) {

case MotionEvent.ACTION_DOWN: {

intercepted =?false;

//如果為true的話子元素將接收不到任何事件

if(!scroller.isFinished()) {

scroller.abortAnimation();

intercepted =?true;

}

break;

}

caseMotionEvent.ACTION_MOVE: {

int?tempX = x-lastXIntercept;

int?tempY = y-lastYIntercept;

//是否是水平滑動

if(Math.abs(tempX) >Math.abs(tempY)) {

intercepted =?true;

}else{

intercepted =?false;

}

break;

}

case MotionEvent.ACTION_UP: {

intercepted =?false;//一般不去攔截UP事件

break;

}

default:

break;

}

lastX = x;

lastY = y;

lastXIntercept = x;

lastYIntercept = y;

returnintercepted;

}

內(nèi)部攔截(子元素優(yōu)先)就是父容器默認不去攔截任何事件居凶,所有的事件都傳遞給子元素虫给,如果子元素需要消耗該事件就直接消耗,否則會通過重寫子元素的dispatchTouchEvent方法將事件傳遞給父容器處理排监。針對不同的滑動策略只需修改對應(yīng)的條件即可狰右,內(nèi)部攔截的邏輯框架如下:

public?boolean?dispatchTouchEvent(MotionEvent event){

int x = (int) event.getX();

int?y = (int) event.getY();

switch(event.getAction()) {

case?MotionEvent.ACTION_DOWN: {

horizontalScrollView.requestDisallowInterceptTouchEvent(true);

break;

}

case?MotionEvent.ACTION_MOVE: {

int?tempX = x - lastX;

int?tempY = y - lastY;

if(Math.abs(tempX) > Math.abs(tempY)) {

horizontalScrollView.requestDisallowInterceptTouchEvent(false);

}

break;

}

case?MotionEvent.ACTION_UP: {

break;

}

default:

break;

}

lastX = x;

lastY = y;

return?super.dispatchTouchEvent(event);

}

“MotionEvent.ACTION_DOWN: {// 必須返回false,否則后續(xù)事件無法傳遞到子元素”------why?

這個問題涉及到事件分發(fā)機制的一些知識。我盡量通俗的去分析舆床,方便大家伙兒的理解棋蚌。你有沒有想過,當你單擊一個按鈕的時候系統(tǒng)是如何確定被單擊的組件的呢挨队?是這樣的谷暮,事件一旦被觸發(fā)的話最先傳遞給當前的Activity,由它的dispatchTouchEvent()來完成事件的分發(fā)盛垦。具體工作由它內(nèi)部的Window將事件傳遞給Decor View(頂級父容器)湿弦,再由頂級父容器傳遞給子元素來實現(xiàn)的。對于有使用過標簽優(yōu)化界面的同學一定聽說過最外層的那個神秘的FrameLayout腾夯。是的颊埃,它就是Decor View

對于單個View來說,由于它沒有子元素無法再向下傳遞蝶俱,所以只能自己決定是否消耗事件班利。如果設(shè)置了OnTouchListener()的話,onTouch()就會被調(diào)用否則onTouchEvent()被調(diào)用榨呆。如果同時提供的話onTouch()會將onTouchEvent()屏蔽掉罗标。如果該組件決定處理事件,則會終止Down事件的分發(fā),并將接下來的所有事件都交給該組件直接進行處理(當然前提是事件可以傳遞給它)闯割。在執(zhí)行UP事件的時候彻消,如果在onTouchEvent()中設(shè)置onClickListener的話onClick()也會被觸發(fā)。

對于ViewGroup來說宙拉,如果不攔截當前事件的話宾尚,就會由子組件繼續(xù)進行事件的分發(fā)。當所有組件都沒有消耗事件的時候也就是說所有的onTouchEvent()方法中都返回false鼓黔,然后就會觸發(fā)Activity的onTouchEvent()事件來消耗事件央勒。否則,之后全部的事件都由ViewGroup來處理澳化,不會傳遞給子元素崔步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缎谷,隨后出現(xiàn)的幾起案子井濒,更是在濱河造成了極大的恐慌,老刑警劉巖列林,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瑞你,死亡現(xiàn)場離奇詭異,居然都是意外死亡希痴,警方通過查閱死者的電腦和手機者甲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砌创,“玉大人虏缸,你說我怎么就攤上這事∧凼担” “怎么了刽辙?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長甲献。 經(jīng)常有香客問我宰缤,道長,這世上最難降的妖魔是什么晃洒? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任慨灭,我火速辦了婚禮,結(jié)果婚禮上球及,老公的妹妹穿的比我還像新娘缘挑。我一直安慰自己,他們只是感情好桶略,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般际歼。 火紅的嫁衣襯著肌膚如雪惶翻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天鹅心,我揣著相機與錄音吕粗,去河邊找鬼。 笑死旭愧,一個胖子當著我的面吹牛颅筋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播输枯,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼议泵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了桃熄?” 一聲冷哼從身側(cè)響起先口,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞳收,沒想到半個月后碉京,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡螟深,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年谐宙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片界弧。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凡蜻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夹纫,到底是詐尸還是另有隱情咽瓷,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布舰讹,位于F島的核電站茅姜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏月匣。R本人自食惡果不足惜钻洒,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锄开。 院中可真熱鬧素标,春花似錦、人聲如沸萍悴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至计维,卻和暖如春袜香,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鲫惶。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工蜈首, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欠母。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓欢策,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赏淌。 傳聞我的和親對象是個殘疾皇子踩寇,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容