### View
#### 1、講下View的繪制流程?
> * 參考回答:
> * View的工作流程主要是指measure、layout、draw這三大流程,即測量、布局和繪制,其中measure確定View的**測量寬/高**,layout確定View的**最終寬/高**和**四個頂點的位置**,而draw則將View**繪制到屏幕**上
> * View的繪制過程遵循如下幾步:
> * **繪制背景** background.draw(canvas)
> * **繪制自己**(onDraw)
> * **繪制 children**(dispatchDraw)
> * **繪制裝飾**(onDrawScollBars)
> 
>
> * 推薦文章:
> * [官方文檔](https://developer.android.com/reference/android/view/View)
> * [Android View的繪制流程](https://www.jianshu.com/p/5a71014e7b1b)
> * [Android應用層View繪制流程與源碼分析](https://blog.csdn.net/yanbober/article/details/46128379)
#### 2、MotionEvent是什么?包含幾種事件?什么條件下會產生?
> * 參考回答:
> * MotionEvent是手指接觸屏幕后所產生的一系列事件。典型的事件類型有如下:
> * **ACTION\_DOWN**:手指剛接觸屏幕
> * **ACTION\_MOVE**:手指在屏幕上移動
> * **ACTION\_UP**:手指從屏幕上松開的一瞬間
> * **ACTION\_CANCELL**:手指保持按下操作,并從當前控件轉移到外層控件時觸發
> * 正常情況下,一次手指觸摸屏幕的行為會觸發一系列點擊事件,考慮如下幾種情況:
> * 點擊屏幕后松開,事件序列:DOWN→UP
> * 點擊屏幕滑動一會再松開,事件序列為DOWN→MOVE→.....→MOVE→UP
#### 3、描述一下View事件傳遞分發機制?
> * 參考回答:
> * View事件分發本質就是對MotionEvent事件分發的過程。即當一個MotionEvent發生后,系統將這個點擊事件傳遞到一個具體的View上
> * 點擊事件的傳遞順序:**Activity(Window)→ViewGroup→ View**
> * 事件分發過程由三個方法共同完成:
> * **dispatchTouchEvent**:用來進行事件的分發。如果事件能夠傳遞給當前View,那么此方法一定會被調用,返回結果受當前View的onTouchEvent和下級View的dispatchTouchEvent方法的影響,表示是否消耗當前事件
> * **onInterceptTouchEvent**:在上述方法內部調用,對事件進行攔截。該方法只在ViewGroup中有,View(不包含 ViewGroup)是沒有的。一旦攔截,則執行ViewGroup的onTouchEvent,在ViewGroup中處理事件,而不接著分發給View。且只調用一次,返回結果表示是否攔截當前事件
> * **onTouchEvent**: 在dispatchTouchEvent方法中調用,用來處理點擊事件,返回結果表示是否消耗當前事件
#### 4、如何解決View的事件沖突 ? 舉個開發中遇到的例子 ?
> * 參考回答:
> * 常見開發中事件沖突的有ScrollView與RecyclerView的滑動沖突、RecyclerView內嵌同時滑動同一方向
> * 滑動沖突的處理規則:
> * 對于由于外部滑動和內部滑動方向不一致導致的滑動沖突,可以根據滑動的方向判斷誰來攔截事件。
> * 對于由于外部滑動方向和內部滑動方向一致導致的滑動沖突,可以根據業務需求,規定何時讓外部View攔截事件,何時由內部View攔截事件。
> * 對于上面兩種情況的嵌套,相對復雜,可同樣根據需求在業務上找到突破點。
> * 滑動沖突的實現方法:
> * **外部攔截法**:指點擊事件都先經過父容器的攔截處理,如果父容器需要此事件就攔截,否則就不攔截。具體方法:需要重寫父容器的onInterceptTouchEvent方法,在內部做出相應的攔截。
> * **內部攔截法**:指父容器不攔截任何事件,而將所有的事件都傳遞給子容器,如果子容器需要此事件就直接消耗,否則就交由父容器進行處理。具體方法:需要配合requestDisallowInterceptTouchEvent方法。
#### 5、scrollTo()和scollBy()的區別?
> * 參考回答:
> * scollBy內部調用了scrollTo,它是基于當前位置的相對滑動;而scrollTo是絕對滑動,因此如果使用相同輸入參數多次調用scrollTo方法,由于View的初始位置是不變的,所以只會出現一次View滾動的效果
> * 兩者都只能對View內容的滑動,而非使View本身滑動。可以使用Scroller有過度滑動的效果
> * 推薦文章:
> * [View 的滑動原理和實現方式](https://www.jianshu.com/p/a177869b0382)
#### 6、Scroller是怎么實現View的彈性滑動?
> * 參考回答:
> * 在MotionEvent.ACTION\_UP事件觸發時調用startScroll()方法,該方法并沒有進行實際的滑動操作,而是記錄滑動相關量(滑動距離、滑動時間)
> * 接著調用invalidate/postInvalidate()方法,請求View重繪,導致View.draw方法被執行
> * 當View重繪后會在draw方法中調用computeScroll方法,而computeScroll又會去向Scroller獲取當前的scrollX和scrollY;然后通過scrollTo方法實現滑動;接著又調用postInvalidate方法來進行第二次重繪,和之前流程一樣,如此反復導致View不斷進行小幅度的滑動,而多次的小幅度滑動就組成了彈性滑動,直到整個滑動過成結束
> 
>
#### 7、 invalidate()和postInvalidate()的區別
> * 參考回答:
> * invalidate()與postInvalidate()都用于刷新View,主要區別是invalidate()在主線程中調用,若在子線程中使用需要配合handler;而postInvalidate()可在子線程中直接調用。
#### 8、SurfaceView和View的區別?
> * 參考回答:
> * View需要在UI線程對畫面進行刷新,而SurfaceView可在子線程進行頁面的刷新
> * View適用于主動更新的情況,而SurfaceView適用于被動更新,如頻繁刷新,這是因為如果使用View頻繁刷新會阻塞主線程,導致界面卡頓
> * SurfaceView在底層已實現雙緩沖機制,而View沒有,因此SurfaceView更適用于需要頻繁刷新、刷新時數據處理量很大的頁面(如視頻播放界面)
#### 9、自定義View如何考慮機型適配 ?
> * 參考回答:
> * 合理使用warp\_content,match\_parent
> * 盡可能的是使用RelativeLayout
> * 針對不同的機型,使用不同的布局文件放在對應的目錄下,android會自動匹配。
> * 盡量使用點9圖片。
> * 使用與密度無關的像素單位dp,sp
> * 引入android的百分比布局。
> * 切圖的時候切大分辨率的圖,應用到布局當中。在小分辨率的手機上也會有很好的顯示效果。