08.Android之View事件問題

目錄介紹

  • 8.0.0.1 簡述Android的事件分發機制?dispatchTouchEvent方法的做用是什麼?說下View和ViewGroup分發事件?
  • 8.0.0.2 onInterceptTouchEvent方法做用是什麼?onTouchEvent的方法的做用是什麼?
  • 8.0.0.4 滑動衝突有哪些場景?滑動衝突處理原則是什麼?滑動衝突解決辦法有哪些?分別是如何解決的?
  • 8.0.0.5 onTouch()、onTouchEvent()和onClick()關係是怎樣的,哪個先執行?若是設置了onClickListener, 可是onClick()沒有調用,可能產生的緣由?
  • 8.0.0.6 View滑動有哪些方法?這些方法分別是如何實現滑動的?分別有什麼優缺點?
  • 8.0.0.7 事件的傳遞規則是什麼?View處理事件的優先級?點擊事件傳遞過程遵循以下順序?事件傳遞規則要點?
  • 8.0.0.8 Scroller的做用?Scroller的要點有哪些?Scroller的使用步驟?Scroller工做原理?
  • 8.0.0.9 Activity事件分發的過程?Window事件分發?DecorView的事件分發?根View的事件分發?
  • 8.0.1.0 GestureDetector是幹什麼用的?GestureDetector做用和注意點?有哪些經常使用的監聽方法?
  • 8.0.1.2 View的滑動方式?如何讓控件滾動到某一位置?scrollTo()和scrollBy()的區別?Scroller是什麼?
  • 8.0.1.4 談一談View的工做原理,執行流程,MeasureSpec是什麼?有什麼做用?
  • 8.0.1.6 SurfaceView和View的區別,說一下SurfaceView的工做原理,爲什麼不會致使頁面卡頓?

好消息

  • 博客筆記大彙總【15年10月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
  • 連接地址:https://github.com/yangchong211/YCBlogs
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!

8.0.0.1 簡述Android的事件分發機制?dispatchTouchEvent方法的做用是什麼?說下View和ViewGroup分發事件?

  • 簡述Android的事件分發機制?技術博客大總結
    • 事件分發順序:Activty->ViewGroup->View
    • 主要方法:dispatchTouchEvent-分發事件、onInterceptTouchEvent-當前View是否攔截該事件、onTouchEvent-處理事件
    • 1.父View調用dispatchTouchEvent開啓事件分發。
    • 2.父View調用onInterceptTouchEvent判斷是否攔截該事件,一旦攔截後該事件的後續事件(如DOWN以後的MOVE和UP)都直接攔截,不會再進行判斷。
    • 3.若是父View進行攔截,父View調用onTouchEvent進行處理。
    • 4.若是父View不進行攔截,會調用子View的dispatchTouchEvent進行事件的層層分發。
  • dispatchTouchEvent方法的做用是什麼?
    • 用於進行事件的分發
    • 只要事件傳給當前View,該方法必定會被調用
    • 返回結果受到當前View的onTouchEvent和下級View的dispatchTouchEvent影響
    • 表示是否消耗當前事件
  • ViewGroup事件分發僞代碼
    • image
  • View事件分發僞代碼
    • image
  • View和ViewGroup在dispatchTouchEvent上的區別
    • ViewGroup在dispatchTouchEvent()中會進行事件的分發。
    • View在dispatchTouchEvent()中會對該事件進行處理。技術博客大總結

8.0.0.2 onInterceptTouchEvent方法做用是什麼?onTouchEvent的方法的做用是什麼?

  • onInterceptTouchEvent方法做用是什麼?
    • 在dispatchTouchEvent的內部調用,用於判斷是否攔截某個事件
    • View和ViewGroup在onInterceptTouchEvent上的區別:
      • View沒有該方法,View會處理全部收到的事件,但不必定會消耗該事件。
      • onInterceptTouchEvent是ViewGroup中添加的方法,用於判斷是否攔截該事件。
  • onTouchEvent的方法的做用是什麼?技術博客大總結
    • 在dispatchTouchEvent的中調用,用於處理點擊事件
    • 返回結果表示是否消耗當前事件

8.0.0.4 滑動衝突有哪些場景?滑動衝突處理原則是什麼?滑動衝突解決辦法有哪些?分別是如何解決的?

  • 滑動衝突有哪些場景?
    • 內層和外層滑動方向不一致:一個垂直,一個水平。好比輪播圖ViewPager和ScrollView
    • 內存和外層滑動方向一致:均垂直or水平。好比scrollView和RecyclerView
    • 前二者層層嵌套。好比ScrollView和RecyclerView(recyclerView中又嵌套recyclerView)
  • 滑動衝突處理原則
    • 對於內外層滑動方向不一樣,只須要根據滑動方向來給相應控件攔截
    • 對於內外層滑動方向相同,須要根據業務來進行事件攔截,規定什麼時候讓外部View攔截事件什麼時候由內部View攔截事件。
    • 前二者嵌套的狀況,根據前兩種原則層層處理便可。
  • 滑動衝突解決辦法有哪些?技術博客大總結
    • 外部攔截法:指點擊事件都先通過父容器的攔截處理,若是父容器須要此事件就攔截,不然就不攔截。具體方法:須要重寫父容器的onInterceptTouchEvent方法,在內部作出相應的攔截。
    • 內部攔截法:指父容器不攔截任何事件,而將全部的事件都傳遞給子容器,若是子容器須要此事件就直接消耗,不然就交由父容器進行處理。具體方法:須要配合requestDisallowInterceptTouchEvent方法。
  • 外部攔截解決滑動衝突法
    • 外部攔截法要點
      • 父容器的onInterceptTouchEvent方法中處理
      • ACTION_DOWN不攔截,一旦攔截會致使後續事件都直接交給父容器處理。
      • ACTION_MOVE中根據狀況進行攔截,攔截:return true,不攔截:return false(外部攔截核心)
      • ACTION_UP不攔截,若是父控件攔截UP,會致使子元素接收不到UP進一步會讓onClick方法沒法觸發。此外UP攔截也沒什麼用。
    • onClick方法生效的兩個條件?
      • View能夠點擊
      • 接收到了DOWN和UP事件
    • 外部攔截,自定義ScrollView,這塊能夠看個人博客:

8.0.0.5 onTouch()、onTouchEvent()和onClick()關係是怎樣的,哪個先執行?若是設置了onClickListener, 可是onClick()沒有調用,可能產生的緣由?

  • onTouch()、onTouchEvent()和onClick()關係是怎樣的,哪個先執行?
    • onTouch->onTouchEvent->onClick
      • 當一個View須要處理事件時,若是它設置了OnTouchListener,那麼OnTouchListener的onTouch方法會被回調。
      • 這時事件如何處理還得看onTouch的返回值,若是返回false,則當前View的onTouchEvent方法會被調用;若是返回true,那麼onTouchEvent方法將不會被調用。因而可知,給View設置的onTouchListener,其優先級比onTouchEvent要高。
      • 若是當前方法中設置了onClickListener,那麼它的onClick方法會被調用。能夠看出,經常使用的OnClickListener,其優先級別最低。
  • 若是設置了onClickListener, 可是onClick()沒有調用,可能產生的緣由? 技術博客大總結
    • 父View攔截了事件,沒有傳遞到當前View
    • View的Enabled = false(setEnabled(false)): view處於不可用狀態,會直接返回。
    • View的Clickable = false(setClickable\setLongClickable(false)):view不能夠點擊,不會執行onClick
    • View設置了onTouchListener,且消耗了事件。會提早返回。
    • View設置了TouchDelegate,且消耗了事件。會提早返回。

8.0.0.6 View滑動有哪些方法?這些方法分別是如何實現滑動的?分別有什麼優缺點?

  • View滑動有哪些方法?
    • layout:對View進行從新佈局定位。在onTouchEvent()方法中得到控件滑動先後的偏移。而後經過layout方法從新設置。
    • offsetLeftAndRight和offsetTopAndBottom:系統提供上下/左右同時偏移的API。onTouchEvent()中調用
    • LayoutParams: 更改自身佈局參數
    • scrollTo/scrollBy: 本質是移動View的內容,須要經過父容器的該方法來滑動當前View
    • Scroller: 平滑滑動,經過重載computeScroll(),使用scrollTo/scrollBy完成滑動效果。
    • 屬性動畫: 動畫對View進行滑動技術博客大總結
    • ViewDragHelper: 谷歌提供的輔助類,用於完成各類拖拽效果。
  • Layout實現滑動
    • image
  • offsetLeftAndRight和offsetTopAndBottom實現滑動
    • image
  • LayoutParams實現滑動
    • 經過父控件設置View在父控件的位置,但須要指定父佈局的類型,很差
    • 用ViewGroup的MariginLayoutParams的方法去設置margin
      //方法一:經過佈局設置在父控件的位置。可是必需要有父控件, 並且要指定父佈局的類型,很差的方法。 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); layoutParams.leftMargin = getLeft() + offsetX; layoutParams.topMargin = getTop() + offsetY; setLayoutParams(layoutParams); /**=============================================== * 方法二:用ViewGroup的MarginLayoutParams的方法去設置marign * 優勢:相比於上面方法, 就不須要知道父佈局的類型。 * 缺點:滑動到右側控件會縮小 *===============================================*/ ViewGroup.MarginLayoutParams mlayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); mlayoutParams.leftMargin = getLeft() + offsetX; mlayoutParams.topMargin = getTop() + offsetY; setLayoutParams(mlayoutParams); 
  • scrollTo\scrollBy實現滑動
    • 都是View提供的方法。
    • scrollTo-直接到新的x,y座標處。
    • scrollBy-基於當前位置的相對滑動。
    • scrollBy-內部是調用scrollTo.
    • scrollTo\scrollBy, 效果是移動View的內容,所以須要在View的父控件中調用。
    • scrollTo/By內部的mScrollX和mScrollY的意義
      • mScrollX的值,至關於手機屏幕相對於View左邊緣向右移動的距離,手機屏幕向右移動時,mScrollX的值爲正;手機屏幕向左移動(等價於View向右移動),mScrollX的值爲負。
      • mScrollY和X的狀況類似,手機屏幕向下移動,mScrollY爲+正值;手機屏幕向上移動,mScrollY爲-負值。
      • mScrollX/Y是根據第一次滑動前的位置來得到的,例如:第一次向左滑動200(等於手機屏幕向右滑動200),mScrollX = 200;第二次向右滑動50, mScrollX = 200 + (-50)= 150,而不是(-50)。
  • 動畫實現滑動的方法
    • 能夠經過傳統動畫或者屬性動畫的方式實現
    • 傳統動畫須要經過設置fillAfter爲true來保留動畫後的狀態(可是沒法在動畫後的位置進行點擊操做,這方面仍是屬性動畫好)
    • 屬性動畫會保留動畫後的狀態,可以點擊。技術博客大總結
  • ViewDragHelper
    • 經過ViewDragHelper去自定義ViewGroup讓其子View具備滑動效果。不過用的不多

8.0.0.7 事件的傳遞規則是什麼?View處理事件的優先級?點擊事件傳遞過程遵循以下順序?事件傳遞規則要點?

  • 事件的傳遞規則是什麼?
    • 點擊事件產生後,會先傳遞給根ViewGroup,並調用dispatchTouchEvent
    • 以後會經過onInterceptTouchEvent判斷是否攔截該事件,若是true,則表示攔截並交給該ViewGroup的onTouchEvent方法進行處理
    • 若是不攔截,則當前事件會傳遞給子元素,調用子元素的dispatchTouchEvent,如此反覆直到事件被處理
  • View處理事件的優先級?
    • 在View須要處理事件時,會先調用OnTouchListener的onTouch方法,並判斷onTouch的返回值
    • 返回true,表示處理完成,不會調用onTouchEvent方法
    • 返回false,表示未完成,調用onTouchEvent方法進行處理
    • 可見,onTouchEvent的優先級沒有OnTouchListener高
    • onTouchEvent沒有消耗的話就會交給TouchDelegate的onTouchEvent去處理。
    • 若是最後事件都沒有消耗,會在onTouchEvent中執行performClick()方法,內部會執行OnClickListener的onClick方法,優先級最低,屬於事件傳遞尾端
  • 點擊事件傳遞過程遵循以下順序?技術博客大總結
    • Activity->Window->View->分發
    • 若是View的onTouchEvent返回false,則父容器的onTouchEvent會被調用,最終能夠傳遞到Activity的onTouchEvent
  • 事件傳遞規則要點?
    • View一旦攔截事件,則整個事件序列都由它處理(ACTION_DOWN\UP等),onInterceptTouchEvent不會再調用(由於默認都攔截了)
    • 可是一個事件序列也能夠經過特殊方法交給其餘View處理(onTouchEvent)
    • 若是View開始處理事件(已經攔截),若是不消耗ACTIO_DOWN事件(onTouchEvent返回false),則同一事件序列的剩餘內容都直接交給父onTouchEvent處理
    • View消耗了ACTION_DOWN,但不處理其餘的事件,整個事件序列會消失(父onTouchEvent)不會調用。這些消失的點擊事件最終會傳給Activity處理。
    • ViewGroup默認不攔截任何事件(onInterceptTouchEvent默認返回false)
    • View沒有onInterceptTouchEvent方法,一旦有事件傳遞給View,onTouchEvent就會被調用
    • View的onTouchEvent默認都會消耗事件return true, 除非該View不可點擊(clickable和longClickable同時爲false)
    • View的enable屬性不影響onTouchEvent的默認返回值。即便是disable狀態。
    • onClick的發生前提是當前View可點擊,而且收到了down和up事件
    • 事件傳遞過程是由父到子,層層分發,能夠經過requestDisallowInterceptTouchEvent讓子元素干預父元素的事件分發(ACTION_DOWN除外)

8.0.0.8 Scroller的做用?Scroller的要點有哪些?Scroller的使用步驟?Scroller工做原理?

  • Scroller的做用?
    • 用於封裝滑動
    • 提供了基於時間的滑動偏移值,可是實際滑動須要咱們去負責。
  • Scroller的要點有哪些?
    • 調用startScroll方法時,Scroller只是單純的保存參數
    • 以後的invalidate方法致使的View重繪
    • View重繪以後draw方法會調用本身實現的computeScroll(),才真正實現了滑動
  • Scroller的使用步驟?
    • image
  • Scroller工做原理?技術博客大總結
    • Scroller自己不能實現View的滑動,須要配合View的computeScroll方法實現彈性滑動
    • 不斷讓View重繪,每一次重繪距離滑動的開始時間有一個時間間隔,經過該時間能夠獲得View當前的滑動距離
    • View的每次重繪都會致使View的小幅滑動,屢次小幅滑動就組成了彈性滑動

8.0.0.9 Activity事件分發的過程?Window事件分發?DecorView的事件分發?根View的事件分發?

  • Activity事件分發的過程?
    • 事件分發過程:Activity->Window->Decor View(當前界面的底層容器,setContentView的View的父容器)->ViewGroup->View
    • Activity的dispatchTouchEvent,會交給Window處理(getWindow().superDispatchTouchEvent()),
    • 返回true:事件所有結束
    • 返回false:全部View都沒有處理(onTouchEvent返回false),則調用Activity的onTouchEvent
  • Window事件分發?
    • Window和superDispatchTouchEvent分別是抽象類和抽象方法
    • Window的實現類是PhoneWindow
    • PhoneWindow的superDispatchTouchEvent()直接調用mDecor.superDispatchTouchEvent(),也就是直接傳給了DecorView
  • DecorView的事件分發?
    • DecorView繼承自FrameLayout
    • DecorView的superDispatchTouchEvent()會調用super.dispatchTouchEvent()——也就是ViewGroup的dispatchTouchEvent方法,以後就會層層分發下去。
  • 根View的事件分發?技術博客大總結
    • 頂層View調用dispatchTouchEvent
    • 調用onInterceptTouchEvent方法
    • 返回true,事件由當前View處理。若是有onTouchiListener,會執行onTouch,而且屏蔽掉onTouchEvent。沒有則執行onTouchEvent。若是設置了onClickListener,會在onTouchEvent後執行onClickListener
    • 返回false,不攔截,交給子View重複如上步驟。

8.0.1.0 GestureDetector是幹什麼用的?GestureDetector做用和注意點?有哪些經常使用的監聽方法?

  • GestureDetector做用和注意點?
    • 探測手勢和事件,須要經過提供的MotionEvent
    • 該類僅能用於touch觸摸提供的MotionEvent,不能用於traceball events(追蹤球事件)
    • 能夠在自定義View中重寫onTouchEvent()方法並在裏面用GestureDetector接管。
    • 能夠在View的setOnTouchListener的onTouch中將點擊事件交給GestureDetector接管。
  • 有哪些經常使用的監聽方法?
    • OnGestureListener
    • OnDoubleTapListener
    • OnContextClickListener
    • SimpleOnGestureListener
  • OnGestureListener
    • OnGestureListener做用技術博客大總結
      • 用於在手勢產生時,去通知監聽者。
      • 該監聽器會監聽全部的手勢,若是隻須要監聽一部分可使用SimpleOnGestureListener
    • OnGestureListener能監聽哪些手勢
      • 按下操做。
      • 按下以後,Move和Up以前。用於提供視覺反饋告訴用戶已經捕獲了他們的行爲。
      • 擡起操做。
      • 滑動操做(由Down MotionEvent e1觸發,當前是Move MotionEvent e2)
      • 長按操做。
      • 猛扔操做。
      • 全部有返回值的回調方法,return true-消耗該事件;return false-不消耗該事件
  • OnDoubleTapListener
    • OnDoubleTapListener做用
      • 監聽「雙擊操做」
      • 監聽「確認的單擊操做」—該單擊操做以後的操做沒法構成一次雙擊。
    • OnDoubleTapListener能監聽哪些手勢?
      • 單擊操做。
      • 雙擊操做.
      • 雙擊操做之間發生了down、move或者up事件。

8.0.1.2 View的滑動方式?如何讓控件滾動到某一位置?scrollTo()和scrollBy()的區別?Scroller是什麼?

  • View的滑動方式?
    • 三種方式:
      • a. 經過View自己提供的scrollTo/scrollBy方法
        • 移動的是View的內容,View自己不移動
      • b. 經過動畫給View施加平移效果實現滑動
        • 經過補間動畫移動的View的影像,View自己位置不發生改變。經過屬性動畫移動view的影像,view自己位置會發生改變。
      • c. 經過改變View的LayoutParams使View從新佈局實現滑動
        • 改變佈局參數,代碼以下:
        MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams(); params.width += 10; params.height += 10; mButton.setLayoutParams(params); 
    • 三種方法的使用對比
      • scrollTo/scrollBy:操做簡單,適合對View內容的滑動;
      • 動畫:操做簡單,主要適合於沒有交互的View和實現複雜的動畫效果;
      • 改變佈局參數:操做稍微複雜,適用於有交互的View。
  • scrollTo()和scrollBy()技術博客大總結
    • scrollBy內部調用了scrollTo,它是基於當前位置的相對滑動;而scrollTo是絕對滑動,所以若是利用相同輸入參數屢次調用scrollTo()方法,因爲View初始位置是不變只會出現一次View滾動的效果而不是屢次。
    • 引伸:二者都只能對view內容進行滑動,而不能使view自己滑動,且非平滑,可以使用Scroller有過渡滑動的效果。
  • Scroller實現滑動的具體過程:
    • 在MotionEvent.ACTION_UP事件觸發時調用startScroll()方法,該方法並無進行實際的滑動操做,而是記錄滑動相關量
    • 立刻調用invalidate/postInvalidate()方法,請求View重繪,致使View.draw方法被執行
    • 緊接着會調用View.computeScroll()方法,此方法是空實現,須要本身處理邏輯。具體邏輯是:先判斷computeScrollOffset(),若爲true(表示滾動未結束),則執行scrollTo()方法,它會再次調用postInvalidate(),如此反覆執行,直到返回值爲false。
    • image

8.0.1.4 談一談View的工做原理,執行流程,MeasureSpec是什麼?有什麼做用?

  • View工做流程
    • View工做流程簡單來講就是,先measure測量,用於肯定View的測量寬高,再 layout佈局,用於肯定View的最終寬高和四個頂點的位置,最後 draw繪製,用於將View 繪製到屏幕上。
    • image
    • ViewRoot對應於ViewRootImpl類,它是鏈接WindowManager和DecorView的紐帶。
    • View的繪製流程是從ViewRoot和performTraversals開始。
    • performTraversals()依次調用performMeasure()、performLayout()和performDraw()三個方法,分別完成頂級 View的繪製。
    • 其中,performMeasure()會調用measure(),measure()中又調用onMeasure(),實現對其全部子元素的measure過程,這樣就完成了一次measure過程;接着子元素會重複父容器的measure過程,如此反覆至完成整個View樹的遍歷。layout和draw同理。
  • MeasureSpec做用技術博客大總結
    • 經過寬測量值widthMeasureSpec和高測量值heightMeasureSpec決定View的大小
      • MeasureSpec組成:一個32位int值,高2位表明SpecMode(測量模式),低30位表明SpecSize
      • 直接繼承View的自定義View須要重寫onMeasure()並設置wrap_content時的自身大小,不然效果至關於macth_parent
    • SpecMode有三類:
      • UNSPECIFIED 表示父容器不對View有任何限制,通常用於系統內部,表示一種測量狀態;
      • EXACTLY 父容器已經檢測出view所需的精確大小,這時候view的最終大小SpecSize所指定的值,至關於match_parent或指定具體數值。
      • AT_MOST 父容器指定一個可用大小即SpecSize,view的大小不能大於這個值,具體多大要看view的具體實現,至關於wrap_content。

8.0.1.6 SurfaceView和View的區別,說一下SurfaceView的工做原理,爲什麼不會致使頁面卡頓?

  • SurfaceView是從View基類中派生出來的顯示類,他和View的區別有:
    • View須要在UI線程對畫面進行刷新,而SurfaceView可在子線程進行頁面的刷新
    • View適用於主動更新的狀況,而SurfaceView適用於被動更新,如頻繁刷新,這是由於若是使用View頻繁刷新會阻塞主線程,致使界面卡頓技術博客大總結
    • SurfaceView在底層已實現雙緩衝機制,而View沒有,所以SurfaceView更適用於須要頻繁刷新、刷新時數據處理量很大的頁面

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索