《Android進階之光》學習筆記之View體系與自定義View。對於一個APP來說,與用戶交互、將內容展示給用戶,都是一個個View通過擴展實現的。View是Android所有控件的基類,我們平常所用的TextVew和ImageView都是繼承自View的。
而用的LinearLayout、RelativeLayout和FrameLayout佈局控件繼承自ViewGroup,ViewGroup是View的組合,包含很多View和ViewGroup,而它包含的ViewGroup又可以包含View和ViewGroup,依次類推,形成一個View樹。
而ViewGroup也是繼承自View。
public abstract class ViewGroup extends View implements ViewParent,ViewManager{ ... }
坐標系
Android系統中有兩種坐標系,分別為Android坐標系和View坐標系。
Android坐標系
在Android中將屏幕左上角的頂點作為Android坐標系的原點,這個原點向右是X軸正方向,向下是Y軸正方向。在觸控事件中,使用(getRawX(),getRawY())獲得的坐標也是Android坐標系的坐標。
View坐標系
View坐標系
(1)View獲取自身的寬和高
width=getRight()-getLeft(); height=getBottom()-getTop();
此外系統給我們提供瞭獲取View寬和高的方法,getHeight()用來獲取View自身的高度,getWidth()用來獲取View自身的寬度。
源碼如下:
public final int getHeight(){ return mBottom-mTop; } public final int getWidth(){ return mRight-mLeft; }
(2)View自身的坐標
通過如下方法可以獲得View到其父控件(ViewGroup)的距離;
getTop():獲取View自身頂邊到其父佈局頂邊的距離。
getLeft():獲取View自身左邊到其父佈局左邊的距離。
getRight():獲取View自身右邊到其父佈局左邊的距離。
getBottom():獲取View自身底邊到其父佈局頂邊的距離。
(3)MotionEvent提供的方法
假設View坐標系示意圖中的圓點是觸摸點,此外View和ViewGroup的點擊事件都由onTouchEvent(MotionEvent event)方法來處理。MotionEvent在用戶交互中作用重大,其內部提供瞭很多事件常量,比如ACTION_DOWN、ACTION_UP和ACTION_MOVE。此外,MotionEvent也提供瞭獲取焦點坐標的各種方法。
getX():獲取點擊事件距離控件左邊的距離,即視圖坐標。
getY():獲取點擊事件距離控件頂邊的距離,即視圖坐標。
getRawX():獲取點擊事件距離整個屏幕左邊的距離,即絕對坐標。
getRawY():獲取點擊事件距離整個屏幕頂邊的距離,即絕對坐標。
View的滑動
基本思想:當點擊事件傳到View時,系統記下觸摸點的坐標,手指移動時系統記下移動後觸摸的坐標並算出偏移量,並通過偏移量來修改View的坐標。
View的6種滑動方式:
layout()
offsetLeftAndRight()與offsetTopAndBottom()
layoutParams
動畫
scrollTo與scrollBy
Scroller
解析Scroller
Scroller的原理:Scroller並不直接實現View的滑動,它需要配合View的computeScroll()方法。在computeScroll()中不斷讓View進行重繪,每次重繪都會計算滑動持續的時間,根據這個持續時間就能算出這次View滑動的位置,我們根據每次滑動的位置調用scrollTo()方法進行滑動,這樣不斷地重復上述過程就形成瞭彈性滑動。
View的事件分發機制
Activity構成
一個Activity包含一個Window對象,這個對象是由PhoneWindow來實現的。PhoneWindow將DecorView作為整個應用窗口的根View,而這個DecorView又將屏幕劃分為兩個區域:一個是TitleView,另一個是ContentView,而我們平常做應用所寫的佈局正是展示在ContentView中。
源碼解析View的事件分發機制
當我們點擊屏幕時,就產生瞭點擊事件,這個事件被封裝成瞭一個類:MotionEvent。而當這個MotionEvent產生後,那麼系統就會將這個MotionEvent傳遞給View的層級,MotionEvent在View中的層級傳遞過程中就是點擊事件分發。點擊事件有三個重要方法,如下:
dispatchTouchEvent(MotionEvent ev)——用來進行事件分發。
onInterceptTouchEvent(MotionEvent ev)——用來進行事件的攔截,在dispatchTouchEvent()中調用,需要註意的是View沒有提供該方法。
OnTouchEvent(MotionEvent ev)——用來處理點擊事件,在dispatchTouchEvent()方法中進行調用。
View的事件分發機制
當點擊事件產生後,事件首先會傳遞給當前的Activity,這會調用Activity的dispatchTouchEvent()方法,具體的事件處理工作交給DecorView,之後再由DecorView將事件處理工作交給根ViewGroup。
View的工作流程
View的工作流程,指的就是measure、layout和draw。其中measure用來測量View的寬和高,layout用來確定View的位置,draw則用來繪制View。
一個Activity包含一個Window對象,這個對象是由PhoneWindow來實現的。PhoneWindow將DecorView作為整個應用窗口的根View,通過ViewRootImpl的setView方法將DecorView加載到Window中。此外,通過ViewRootImpl的PerformTraveals使得ViewTree開始View的工作流程,方法內部分別執行performMeasure、performLayout和performDraw,在其方法的內部又分別調用View的measure、layout和draw方法。
自定義View
自定義View分為自定義View、自定義ViewGroup和自定義組合控件三大類。其中自定義View又分為繼承系統控件(比如TextView)和繼承View兩種。自定義ViewGroup也分為繼承ViewGroup和繼承系統特定的ViewGroup(比如RelativeLayout)。而自定義組合控件就是多個控件組合起來成為一個新的控件,主要用於解決多次重復使用同一類型的佈局。比如頂部標題欄及彈出的固定樣式的Dialog,這些都是常用的,所以把它們所需要的控件組合起來重新定義成一個新的控件。