View的事件體系 - Android開發藝術探索讀書筆記(第三章)

Android開發藝術探索讀書筆記(第三章)

嘗試使用Markdown語法編寫

View的滑動三種方法

  • 使用View自身提供的 scrollTo/scrollBy方法
  • 通過動畫給View平移效果
  • 通過改變View的LayoutParams使得View重新佈局

scrollTo/scrollBy方法

只能改變內容位置,而不能改變View的佈局位置 。

!注意

原始狀態mScrollYmScrollX 都是0,然後View左滑動(View的左邊緣在父控件的左邊緣的右邊),則mScrollX 爲正值,同理右滑動mScrollX 爲負值。View上滑動(View的上邊緣在父控件的上邊緣上邊)mScrollY 則爲正,同理,View下滑,mScrollY 爲負。

總結:左正右負、上正下負

使用動畫

使用傳統屬性動畫,Android3.0以上版本能夠真正將控件移動(而3.0以下版本,則只能將View的影像移動,需要通過0一些小手段:在新的位置預先設置一個相同的控件,然後當木目標控件動畫結束時候,將目標控件隱藏,同時把預設的控件顯示出來

改變佈局參數

例子:將如想把某個控件往右移動100px,則在修改該控件的marginLeft參數值即可,另一種方法則是,在該控件左邊放置一個空View,然後增加空View的寬度。

三個方法的對比?

  1. scrollTo/scrollBy實現 比較方便實現滑動效果不影響內部元素單擊事件。 只能滑動View的內容,而不能滑動View的本身。
  2. 動畫實現 3.0版本以上沒有明顯缺點,而3.0以下版本,則在View不需要交互的情況下使用比較合適。還有個優點就是能夠實現較爲複雜的動畫,其他方式很難或者不能做到。
  3. 改變佈局參數實現只是使用起來比較麻煩,適用需要交互的View。

彈性滑動

  • 使用Scroller,內部保存了幾個參數(滑動起點:startXstartY;滑動距離:dxdy;滑動時間:duartion),最後通過invalidate 方法導致View重繪,重繪後,在View的draw方法中會調用computeScroll,而computeScroll 會去Scroller獲取當前的scrollXscrollY,最後通過scrollTo 方法實現滑動,緊接着又調用postInvalidate方法進行第二次重繪,如此反覆。
    總結:Scroller本身並不能實現View的滑動,它配合了View的computeScroll方法,不斷讓View重繪,而每次重繪距離滑動起始時間有一段時間間隔,間隔裏,Scroller得出了View當前的滑動位置,再通過scrollTo方法進行小幅度滑動,反覆進行。

View的事件分發機制

點擊事件傳遞規則

**ViewGroup中的三個重要方法**
  • dispatchTouchEvent 進行事件分發
  • onInterceptTouchEvent 內部調用,判斷是否攔截某個事件(View中沒有這個方法)
  • onTouchEvent dispatchTouchEvent方法中的調用,處理點擊事件,返回結果是否消耗當前事件

    dispatchTouchEvent 邏輯概述:先調用onInterceptTouchEvent 方法判斷是否攔截當前事件,若攔截,則調用onTouchEvent 判斷是否消耗當前時間;若不攔截,則調用子控件的dispatchTouchEvent 方法


補充:如果View需要處理事件,又設置了onClickListener,那麼其中的onTouch 方法會被回調,返回false,則當前View的onTouchEvent 方法仍會被調用,返回true,那麼onTouchEvent方法則不會被調用。由此可見,onTouchListener 優先級比onTouchEvent,另外,如果onTouchListener 中設置有onClickListener ,則onClick 方法會被調用。

優先級: onTouchListener > onTouchEvent > onClickListener

**重要結論** 1. 一個事件序列只能被一個View攔截並且消耗,但可以通過特殊手段做到讓兩個View同時處理,在一個View中通過*onTouchEvent* 強行傳遞給其他View處理 2. 某個View攔截之後,它的*onInterceptTouchEvent* 就不會再被調用,所以一旦攔截,該事件序列都只能由攔截它的控件調用了 3. 某個View如果不消耗*ACTION_DOWN* 事件,則同一事件序列的其他事件都不會再交給它處理,將重新將事件交給它的父元素處理 4. 某個View如果不消耗除了*ACTION_DOWN* 之外的事件,那麼,該點擊事件將會無效,後續的事件還是會接收,並且父元素的*onTouch* 方法也不會被調用,最後,事件會傳遞給Activity處理 5. ViewGroup默認不會攔截任何事件 6. View沒有*onInterceptTouchEvent* 方法 7. View的*onTouchEvent* 默認會消耗事件(返回true),除非該View不可點擊(clickable與longClickable同時爲false) 8. View的enable屬性不影響*onTouchEvent* 默認返回值,兩者無關 9. *onClick* 被調用的前提是,View可點擊,並且收到down、up的事件 10. 事件傳遞過程是由外向內的,先傳給父元素、再由父元素分發給子View,通過*requestDisallowInterceptTouchEvent* 可以在子元素中敢於父元素的事件分發過程(*ACTION_DOWN除外*)

View的滑動衝突

只要界面中內外兩層同時可以滑動,則會產生滑動衝突

**三種滑動衝突場景**
  • 外部滑動方向與內部滑動方向不一致
  • 外部滑動方向與內部滑動方向一致
  • 以上兩種情況的嵌套

外內不一致的場景
例子:ViewPagerFragment 配合使用組成頁面滑動,Fragment 裏面有ListView ,然而這種情況是沒有滑動衝突的,因爲ViewPager 內部已經解決了滑動衝突。但如果是ScrollView 就要手動解決衝突了。

處理規則:判斷滑動是水平還是垂直的,滑動是水平時候,讓外部View攔截滑動事件,而滑動是垂直的時候則是讓內部View來攔截滑動事件。注意,斜向滑動時候,則根據水平滑動距離與垂直滑動距離來判斷,算距離大的

內外一致的場景
處理規則:該場景較爲特殊,一般需要從業務上找到突破點,明確什麼時候需要外部滑動,什麼時候需要內部滑動,制定相應的處理規則。

以上兩種情況的嵌套
處理規則:該場景更爲特殊,處理方式與內外一致的場景 相同,根據業務需求制定相應的處理規則。

解決方法

  • 外部攔截法

    點擊事件都經由父控件的攔截處理,若父控件需要則攔截,不需要則不攔截,比較符合點擊事件的分發機制,需要重寫父容器的onInterceptTouchEvent 方法,並在內部做相應的攔截。

  • 內部攔截法

    父控件不攔截任何事件,所有的事件都傳遞給子元素,若子元素需要則消耗掉該事件,否則交給父控件處理,該方法與Android事件分發機制不一致,需要配合requestDisallowInterceptTouchEvent 方法才能正常工作,需要重寫子元素的dispatchTouchEvent 方法

注意:爲什麼父容器不能攔截ACTION_DOWN 事件,因爲攔截之後,所有事件都無法傳遞給子控件了。

發佈了20 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章