目錄介紹
- 3.0.0.1 View的繪制需要經過哪些過程份招?有哪些常用回調方法未荒?View的繪制流程的詳細流程是怎樣的妈倔?
- 3.0.0.2 View繪制流程冠骄,當一個TextView的實例調用setText()方法后執(zhí)行了什么伪煤?請說一下原理……
- 3.0.0.3 requestLayout()、invalidate()與postInvalidate()有什么區(qū)別凛辣?
- 3.0.0.4 DecorView的作用是什么抱既?DecorView中如何獲取ContentView以及Activity所設置的View?ViewRootIml如何和DecorView建立聯(lián)系?
- 3.0.0.5 getWidth()方法和getMeasureWidth()區(qū)別呢?為什么有時候用getWidth()或者getMeasureWidth()得到0扁誓?
- 3.0.0.6 平時寫的自定義控件有哪些防泵?如何優(yōu)化自定義view?View的繪制流程說一下蝗敢?自定義View的注意點捷泞?
- 3.0.0.7 View的wrap_content和match_parent效果一致的原因分析?getDefaultSize方法的處理邏輯寿谴?
- 3.0.0.8 ViewGroup(抽象類)的measure流程锁右?getChildMeasureSpec獲取子元素MeasureSpec的要點?
- 3.0.0.9 View的layout過程讶泰?View的layout()源碼分析咏瑟?LinearLayout的onLayout方法?View的測量寬高和最終寬高有什么區(qū)別峻厚?
- 3.0.1.0 draw的過程步驟是什么响蕴?View特殊方法setWillNotDraw是干什么用的?
- 3.0.1.1 View中x,y,translationX,translationY分別是什么惠桃?View平移時是否改變了left浦夷、top等原始參數?
- 3.0.1.2 MeasureSpec是什么辜王?MeasureSpec的組成劈狐?測量模式SpecMode的類型和具體含義?MeasureSpec和LayoutParams的對應關系呐馆?
- 3.0.1.3 如何獲取View的測量寬/高肥缔?如何在Activity啟動時獲得View的寬/高?Activity中獲得View寬高的4種辦法汹来?
- 3.0.1.4 Activity啟動到最終加載ViewRoot(執(zhí)行三大流程)的流程是什么续膳?
- 3.0.1.5 自定義View性能優(yōu)化有哪些?針對異常銷毀,自定義View如何優(yōu)化收班?如何避免創(chuàng)建大量對象坟岔?
好消息
- 博客筆記大匯總【15年10月到至今】,包括Java基礎及深入知識點摔桦,Android技術博客社付,Python學習筆記等等,還包括平時開發(fā)中遇到的bug匯總,當然也在工作之余收集了大量的面試題鸥咖,長期更新維護并且修正燕鸽,持續(xù)完善……開源的文件是markdown格式的!同時也開源了生活博客啼辣,從12年起啊研,積累共計500篇[近100萬字],將會陸續(xù)發(fā)表到網上熙兔,轉載請注明出處悲伶,謝謝艾恼!
- 鏈接地址:https://github.com/yangchong211/YCBlogs
- 如果覺得好住涉,可以star一下,謝謝钠绍!當然也歡迎提出建議舆声,萬事起于忽微,量變引起質變柳爽!所有的筆記將會更新到GitHub上媳握,同時保持更新,歡迎同行提出或者push不同的看法或者筆記磷脯!
3.0.0.1 View的繪制需要經過哪些過程蛾找?有哪些常用回調方法?View的繪制流程的詳細流程是怎樣的赵誓?
- View的繪制需要經過哪些過程打毛?
- measure:測量View的寬和高
- View的measure方法是final類型方法——表明該方法無法被重載
- View的measure方法會調用onMeasure方法,onMeasure會調用setMeasuredDimension方法設置View寬/高的測量值
- layout:確定View在父控件中的放置位置
- draw:負責將View繪制在屏幕上俩功。技術博客大總結
- measure:測量View的寬和高
- 有哪些常用回調方法幻枉?
- 構造方法
- onAttachToWindow:在包含View的Activity啟動時調用
- onDetachFromWindow:在包含View的Activity退出或者View被remove時回調
- onVisibilityChanged:當View的可見狀態(tài)發(fā)生改變時調用
- 比較重要的概念
- ViewRoot:連接WindowManager(外界訪問Window的入口)和DecorView(頂級View)的紐帶,View的三大流程均是通過ViewRoot來完成的诡蜓。
- DecorView:頂級View
- DecorView是頂級View熬甫,本質就是一個FrameLayout
- 包含了兩個部分,標題欄和內容欄
- 內容欄id是content蔓罚,也就是activity中setContentView所設置的部分椿肩,最終將布局添加到id為content的FrameLayout中
- View的繪制流程的詳細流程是怎樣的?技術博客大總結
- View的繪制流程是從ViewRoot的PerformTraversals方法開始的豺谈。大概的流程如下所示
- performTraversals會依次調用performMeasure, performLayout, performDraw三個方法郑象,這三個方法分別完成頂層View的measure,layout,draw方法,onMeasure又會調用所有子元素的measure過程核无,直到完成整個View樹的遍歷扣唱。同理,performLayout, performDraw的傳遞流程與performMeasure相似。唯一不同在于噪沙,performDraw的傳遞過程在draw方法中通過dispatchDraw實現(xiàn)炼彪,但沒有本質區(qū)別。
- Measure過程后可以調用getMeasureWidth和getMeasureHeight方法獲取View測量后的寬高正歼,與getWidth和getHeight的區(qū)別是:getMeasuredHeight()返回的是原始測量高度辐马,與屏幕無關,getHeight()返回的是在屏幕上顯示的高度局义。實際上在當屏幕可以包裹內容的時候喜爷,他們的值是相等的,只有當view超出屏幕后萄唇,才能看出他們的區(qū)別檩帐。當超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外沒有顯示的高度另萤。
- Layout過程確定View四個頂點的位置和實際的寬高湃密。
- Draw過程確定View的顯示,只有draw方法完成后View的內容才會出現(xiàn)在屏幕上四敞。
- View的繪制流程是從ViewRoot的PerformTraversals方法開始的豺谈。大概的流程如下所示
3.0.0.2 View繪制流程泛源,當一個TextView的實例調用setText()方法后執(zhí)行了什么?請說一下原理……
- View的繪制流程主要分為三步:
- onMeasure:測量視圖的大小忿危,從頂層父View到子View遞歸調用measure()方法达箍,measure()調用onMeasure()方法,onMeasure()方法完成繪制工作铺厨。
- onLayout:確定視圖的位置缎玫,從頂層父View到子View遞歸調用layout()方法,父View將上一步measure()方法得到的子View的布局大小和布局參數努释,將子View放在合適的位置上碘梢。
- onDraw:繪制最終的視圖,首先ViewRoot創(chuàng)建一個Canvas對象伐蒂,然后調用onDraw()方法進行繪制煞躬。onDraw()方法的繪制流程為
- ① 繪制視圖背景。
- ② 繪制畫布的圖層逸邦。 技術博客大總結
- ③ 繪制View內容恩沛。
- ④ 繪制子視圖,如果有的話缕减。
- ⑤ 還原圖層雷客。
- ⑥ 繪制滾動條。
3.0.0.3 requestLayout()桥狡、invalidate()與postInvalidate()有什么區(qū)別搅裙?requestLayout()何時不會觸發(fā)onDraw()皱卓?
- invalidate() postInvalidate()
- invalidate()該方法遞歸調用父View的invalidateChildInParent()方法,直到調用ViewRootImpl的invalidateChildInParent()方法部逮,最終觸發(fā)ViewRootImpl的performTraversals()方法娜汁,此時mLayoutRequestede為false,不會觸發(fā)onMesaure()與onLayout()方法兄朋,有可能會觸發(fā)onDraw()方法掐禁。
- 共同點:都是調用onDraw()方法,然后去達到重繪view的目的
- 區(qū)別:invalidate()用于主線程颅和,postInvalidate()用于子線程
- requestLayout()
- 該方法會遞歸調用父窗口的requestLayout()方法傅事,直到觸發(fā)ViewRootImpl的performTraversals()方法,此時mLayoutRequestede為true峡扩,會觸發(fā)onMesaure()與onLayout()方法蹭越,不一定會觸發(fā)onDraw()方法。
- 當view確定自身已經不再適合現(xiàn)有的區(qū)域時有额,該view本身調用這個方法要求parent view(父類的視圖)重新調用他的onMeasure般又、onLayout來重新設置自己位置彼绷。特別是當view的layoutparameter發(fā)生改變巍佑,并且它的值還沒能應用到view上時,這時候適合調用這個方法requestLayout()寄悯。requestLayout調用onMeasure和onLayout萤衰,不一定調用onDraw技術博客大總結
- 如何選擇
- 一般說來需要重新布局就調用requestLayout()方法,需要重新繪制就調用invalidate()方法猜旬。
- requestLayout()何時不會觸發(fā)onDraw()脆栋?
- 如果沒有改變控件的left\right\top\bottom就不會觸發(fā)onDraw()
- invalidate()在什么情況下不會觸發(fā)onDraw?
- 在ViewGroup中洒擦,invalidate默認不重新繪制子view椿争。
- 如何讓ViewGroup在invalidate時會觸發(fā)onDraw?技術博客大總結
- 本質需要將ViewGroup的dirtyOpaque設置為false
- 1.在構造函數中調用setWillNotDraw(false);
- 2.給ViewGroup設置背景熟嫩。調用setBackground秦踪。
- 本質需要將ViewGroup的dirtyOpaque設置為false
3.0.0.4 DecorView的作用是什么?DecorView中如何獲取ContentView以及Activity所設置的View?ViewRootIml如何和DecorView建立聯(lián)系?
- DecorView的作用是什么掸茅?
- DecorView是頂級View椅邓,本質就是一個FrameLayout
- 包含了兩個部分,標題欄和內容欄昧狮,內容欄id是content景馁,也就是activity中setContentView所設置的部分,最終將布局添加到id為content的FrameLayout中技術博客大總結
- DecorView中如何獲取ContentView以及Activity所設置的View?
- 獲取content:ViewGroup content = findViewById(R.android.id.content)
- 獲取設置的View:content.getChidlAt(0)
- ViewRootIml如何和DecorView建立聯(lián)系?技術博客大總結
- Activity對象在ActivityThread中創(chuàng)建完畢后逗鸣,會將DecorView添加到Window中
- 同時會創(chuàng)建ViewRootImpl合住,調用ViewRoot的setView方法將ViewRootImpl和DevorView建立關聯(lián)
root = new ViewRootImpl(view.getContext(), display); root.setView(view, wparams, panelParentView);
- ViewRoot為什么要和DecorView建立關聯(lián)
- DecorView等View的三大流程需要通過ViewRoot完成
3.0.0.5 getWidth()方法和getMeasureWidth()區(qū)別呢?為什么有時候用getWidth()或者getMeasureWidth()得到0?
- getWidth()方法和getMeasureWidth()區(qū)別呢
- getMeasureWidth()
- getMeasureWidth()方法在measure()過程結束后就可以獲取到了阱表,另外,getMeasureWidth()方法中的值是通過setMeasuredDimension()方法來進行設置的
- 這里mMeasuredWidth & MEASURED_SIZE_MASK表示的是測量階段結束之后爱致,view真實的值。
public final int getMeasuredWidth() { return mMeasuredWidth & MEASURED_SIZE_MASK; }
- getWidth()
- getWidth()方法要在layout()過程結束后才能獲取到,getWidth()方法中的值則是通過視圖右邊的坐標減去左邊的坐標計算出來的纫普。
- mRight和mLeft是什么值昨稼,是在什么時候被設置的。具體看layout()過程中源碼
@ViewDebug.ExportedProperty(category = "layout") public final int getWidth() { return mRight - mLeft; }
- getMeasureWidth()
- 為什么有時候用getWidth()或者getMeasureWidth()得到0
- 問題描述:使用getMeasuredWidth()和getMeasuredHeight()方法,無論是在onCreate()、onStart()剩岳、onResume()中調用晓铆,都無法得到控件的長度、和寬度链蕊。如下圖,測量的結果為0。
- 解釋:技術博客大總結
- 因為View的Measure過程和Activity的生命周期方法不是同步執(zhí)行的宴卖,所以無法保證Activity執(zhí)行了onCreate()、onStart()、onResume()時某個View已經測量完畢了,如果View還沒有測量完畢,那么獲得寬/高就是0蘑斧。
- 后來覺得這種解釋有點牽強,比如
- 解決控件測量寬高問題
- 如下所示
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); test(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); getWidth(4); } private void test(){ getWidth(1); marqueeView.measure(0, 0); getWidth(2); marqueeView.post(new Runnable() { @Override public void run() { getWidth(3); } }); } private void getWidth(int a){ int width2 = marqueeView.getWidth(); int measuredWidth2 = marqueeView.getMeasuredWidth(); Log.e(a+"MainActivity-----",width2+"-----"+measuredWidth2); } //11-28 17:03:17.559 15990-15990/com.yc.cn.ycbanner E/1MainActivity-----: 0-----0 //11-28 17:03:17.567 15990-15990/com.yc.cn.ycbanner E/2MainActivity-----: 0-----760 //11-28 17:03:17.684 15990-15990/com.yc.cn.ycbanner E/3MainActivity-----: 960-----960 //11-28 17:03:17.685 15990-15990/com.yc.cn.ycbanner E/4MainActivity-----: 960-----960
- 什么時候測量寬高不等于實際寬高扩劝?
- MeasuredWidth/height!=getWidth/Height()的場景:更改View的布局參數并進行重新布局后聂示,就會導致測量寬高!=實際寬高
3.0.0.6 平時寫的自定義控件有哪些秀鞭?如何優(yōu)化自定義view?View的繪制流程說一下?自定義View的注意點搀矫?
- 平時寫的自定義控件有哪些敏弃?
- 1組合控件麦到。這種自定義控件不需要我們自己繪制,而是使用原生控件組合成的新控件吸祟。如標題欄借杰,recyclerView封裝控件疮装。
- 2繼承原有的控件。這種自定義控件在原生控件提供的方法外,可以自己添加一些方法雷酪。比如圖片縮放控件墩弯,進度條控件。
- 3完全自定義控件:這個View上所展現(xiàn)的內容全部都是我們自己繪制出來的。比如百分比進度條控件
- 如何優(yōu)化自定義 view
- 為了加速你的view,對于頻繁調用的方法,需要盡量減少不必要的代碼。先從onDraw開始,需要特別注意不應該在這里做內存分配的事情迁沫,因為它會導致GC缅糟,從而導致卡頓二鳄。在初始化或者動畫間隙期間做分配內存的動作髓窜。不要在動畫正在執(zhí)行的時候做內存分配的事情祈餐。
- 你還需要盡可能的減少onDraw被調用的次數蜒谤,大多數時候導致onDraw都是因為調用了invalidate().因此請盡量減少調用invaildate()的次數。如果可能的話,盡量調用含有4個參數的invalidate()方法而不是沒有參數的invalidate()瑰剃。沒有參數的invalidate會強制重繪整個view歇竟。技術博客大總結
- 另外一個非常耗時的操作是請求layout。任何時候執(zhí)行requestLayout(),會使得Android UI系統(tǒng)去遍歷整個View的層級來計算出每一個view的大小号胚。如果找到有沖突的值弃秆,它會需要重新計算好幾次瘩欺。另外需要盡量保持View的層級是扁平化的鹰椒,這樣對提高效率很有幫助劫瞳。
- 如果你有一個復雜的UI,你應該考慮寫一個自定義的ViewGroup來執(zhí)行他的layout操作砚婆。與內置的view不同,自定義的view可以使得程序僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小装盯。這個PieChart 例子展示了如何繼承ViewGroup作為自定義view的一部分坷虑。PieChart 有子views,但是它從來不測量它們埂奈。而是根據他自身的layout法則迄损,直接設置它們的大小。
- View的繪制流程說一下账磺?
- View的繪制流程:OnMeasure()——>OnLayout()——>OnDraw()
- 第一步:OnMeasure():測量視圖大小芹敌。從頂層父View到子View遞歸調用measure方法,measure方法又回調OnMeasure垮抗。
- 第二步:OnLayout():確定View位置氏捞,進行頁面布局。從頂層父View向子View的遞歸調用view.layout方法的過程冒版,即父View根據上一步measure子View所得到的布局大小和布局參數液茎,將子View放在合適的位置上。
- 第三步:OnDraw():繪制視圖辞嗡。ViewRoot創(chuàng)建一個Canvas對象捆等,然后調用OnDraw()。六個步驟:①续室、繪制視圖的背景栋烤;②、保存畫布的圖層(Layer)挺狰;③明郭、繪制View的內容;④她渴、繪制View子視圖达址,如果沒有就不用;⑤趁耗、還原圖層(Layer)沉唠;⑥、繪制滾動條苛败。技術博客大總結
- 自定義View的注意點满葛?
- View需要支持wrap_content、padding
- ViewGroup需要支持子View的margin和自身的padding
- 盡量不要在View中使用Handler罢屈,View已經有post系列方法
- View如果有線程或者動畫嘀韧,需要及時停止(onDetachedFromWindow會在View被remove時調用)——避免內存泄露
- View如果有滑動嵌套情形,需要處理好滑動沖突
3.0.0.7 View的wrap_content和match_parent效果一致的原因分析缠捌?getDefaultSize方法的處理邏輯锄贷?
- View的wrap_content和match_parent效果一致的原因分析译蒂?
- 根據View的onMeasure方法中的getDefaultSize方法,我們可以發(fā)現(xiàn)在兩種模式下谊却,View的測量值等于該View的測量規(guī)格MeasureSpec中的尺寸柔昼。
- View的MeasureSpec本質是由自身的LayoutParams和父容器的MeasureSpec決定的。
- 當View為wrap_content時炎辨,該View的模式為AT_MOST捕透,且尺寸specSize為父容器的剩余空間大小。
- 當View為match_parent時碴萧,該View的模式跟隨父容器的模式(AT_MOST/EXACTLY), 且尺寸specSize為父容器的剩余空間大小乙嘀。
- 因此getDefaultSize中無論View是哪種模式,最終測量寬/高均等于尺寸specSize破喻,因此兩種屬性效果是完全一樣的(View的大小充滿了父容器的剩余空間)技術博客大總結
- 除非給定View固定的寬/高虎谢,View的specSize才會等于該固定值。
- getDefaultSize方法的處理邏輯低缩?
- getDefaultSize: 根據建議獲取的最小寬高和測量規(guī)格嘉冒,決定實際的測量寬高
- UNSPECIFIED模式:測量寬高 = 建議的最小寬高
- EXACTLY / AT_MOST模式:測量寬高 = specSize技術博客大總結
- View的getDefaultSize源碼要點(決定了View寬高的測量值)
- UNSPECIFIED模式時,寬/高為第一個參數也就是getSuggestedMinimumWidth()獲取的建議最小值
- AT_MOST(wrap_content)和EXACTLY(match_parent/具體值dp等)這兩個模式下咆繁,View寬高的測量值為當前View的MeasureSpec(測量規(guī)格)中指定的尺寸specsize
- getDefaultSize: 根據建議獲取的最小寬高和測量規(guī)格嘉冒,決定實際的測量寬高
3.0.0.8 ViewGroup(抽象類)的measure流程讳推?getChildMeasureSpec獲取子元素MeasureSpec的要點?
- ViewGroup(抽象類)的measure流程玩般?
- ViewGroup沒有onMeasure方法银觅,只定義了measureChildren方法(onMeasure根據不同布局難以統(tǒng)一)
- measureChildren中遍歷所有子元素并調用measureChild方法
- measureChild方法中會獲取子View的MeasureSpec(getChildMeasureSpec),然后調用子元素View的measure方法進行測量
- getChildMeasureSpec獲取子元素MeasureSpec的要點坏为?
- 子View的MeasureSpec是根據自身的LayoutParams和父容器SpecMode生成
- 當子View的布局參數為wrap_content究驴,且父容器模式為AT_MOST時,效果與子元素布局為match_parent是一樣的匀伏。因此當子View的布局參數為wrap_content時洒忧,需要給指定默認的寬/高
- LinearLayout的onMeasure()分析
- ViewGroup因為布局的不同,無法統(tǒng)一onMeasure方法够颠,具體內容根據布局的不同而不同熙侍,這里直接以LinearLayout進行分析
- onMeasure會根據orientation選擇measureVertical或者measureHorizontal進行測量
- measureVertical本質是遍歷子元素,并執(zhí)行子元素的measure方法履磨,并獲得子元素的總高度以及子元素在豎直方向上的margin等蛉抓。技術博客大總結
- 最終LinearLayout會測量自己的大小,在orientation的方向上剃诅,如果布局是match_parent或者具體數值巷送,測量過程與View一致(高度為specSize);如果布局是wrap_content矛辕,高度是所有子元素高度總和笑跛,且不會超過父容器的剩余空間付魔,最終高度需要考慮在豎直方向上的padding
3.0.0.9 View的layout過程?View的layout()源碼分析堡牡?LinearLayout的onLayout方法抒抬?View的測量寬高和最終寬高有什么區(qū)別?
- View的layout過程晤柄?
- 使用layout方法確定View本身的位置
- layout中調用onLayout方法確定所有子View的位置
- View的layout()源碼分析?
- 調用setFrame()設置View四個定點位置(即初始化mLeft,mRight,mTop,mBottom的值)
- 之后調用onLayout確定子View位置妖胀,該方法類似于onMeasure芥颈,View和ViewGroup中均沒有實現(xiàn),具體實現(xiàn)與具體布局有關赚抡。
- LinearLayout的onLayout方法爬坑?
- 根據orientation選擇調用layoutVertical或者layoutHorizontal
- layoutVertical中會遍歷所有子元素并調用setChildFrame(里面直接調用子元素的layout方法)
- 層層傳遞下去完成了整個View樹的layout過程
- setChildFrame中的寬/高實際就是子元素的測量寬/高(getMeasure…后直接傳入)
- View的測量寬高和最終寬高有什么區(qū)別?技術博客大總結
- 等價于getMeasuredWidth和getWidth有什么區(qū)別
- getWidth = mRight - mLeft涂臣,結合源碼測量值和最終值是完全相等的盾计。
- 區(qū)別在于:測量寬高形成于measure過程,最終寬高形成于layout過程(賦值時機不同)
- 也有可能導致兩者不一致:強行重寫View的layout方法赁遗,在傳參方面改變最終寬/高(雖然這樣毫無實際意義)
- 某些情況下署辉,View需要多次measure才能確定自己的測量寬高,在前幾次測量中等到的值可能有最終寬高不一致岩四。但是最終結果上哭尝,測量寬高=最終寬高
3.0.1.0 draw的過程步驟是什么?View特殊方法setWillNotDraw是干什么用的剖煌?
- draw的過程步驟是什么材鹦?
- 繪制背景(drawBackground(canvas))
- 繪制自己(onDraw)
- 繪制children(dispatchDraw)-遍歷調用所有子View的draw方法
- 繪制裝飾(如onDrawScollBars)
- View特殊方法setWillNotDraw是干什么用的?
- 若一個View不繪制任何內容耕姊,需要將該標志置為true桶唐,系統(tǒng)會進行相應優(yōu)化
- 默認View不開啟該標志位技術博客大總結
- 默認ViewGroup開啟該標志位
- 如果我們自定義控件繼承自ViewGroup并且本身不進行繪制時,就可以開啟該標志位
- 當該ViewGroup明確通過onDraw繪制內容時茉兰,就需要顯式關閉WILL_NOT_DRAW標志位尤泽。
3.0.1.1 View中x,y,translationX,translationY分別是什么?View平移時是否改變了left邦邦、top等原始參數安吁?
- View中x,y,translationX,translationY分別是什么?
- x,y是View當前左上角的坐標
- translationX,translationY是在滑動/動畫后燃辖,View當前位置和View最原始位置的距離鬼店。
- 因此得出等式:x(View左上角當前位置) = left(View左上角初始位置) + translationX(View左上角偏移的距離)
- View平移時是否改變了left、top等原始參數黔龟?技術博客大總結
- View平移時top妇智、left等參數不變滥玷,改變的是x,y,tranlsationX和tranlsationY
3.0.1.2 MeasureSpec是什么?MeasureSpec的組成巍棱?測量模式SpecMode的類型和具體含義惑畴?MeasureSpec和LayoutParams的對應關系?
- MeasureSpec是什么航徙?
- MeasureSpec是一種“測量規(guī)則”或者“測量說明書”如贷,決定了View的測量過程
- View的MeasureSpec會根據自身的LayoutParamse和父容器的MeasureSpec生成。
- 最終根據View的MeasureSpec測量出View的寬/高(測量時數據并非最終寬高)
- MeasureSpec的組成到踏?
- MeasureSpec代表一個32位int值杠袱,高2位是SpecMode,低30位是SpecSize
- SpecMode是指測量模式
- SpecSize是指在某種測量模式下的大小
- 類MesaureSpec提供了用于SpecMode和SpecSize打包和解包的方法
- 測量模式SpecMode的類型和具體含義窝稿?技術博客大總結
- UNSPECIFIED:父容器不對View有任何限制楣富,一般用于系統(tǒng)內部
- EXACTLY:精準模式,View的最終大小就是SpecSize指定的值(對應于LayoutParams的match_parent和具體的數值)
- AT_MOST:最大值模式伴榔,大小不能大于父容器指定的值SpecSize(對應于wrap_content)
- MeasureSpec和LayoutParams的對應關系纹蝴?
- View的MeasureSpec是需要通過自身的LayoutParams和父容器的MeasureSpec一起才能決定
- DecorView(頂級View)是例外,其本身MeasureSpec由窗口尺寸和自身LayoutParams共同決定
- MeasureSpec一旦確定踪少,onMeasure中就可以確定View的測量寬/高
3.0.1.3 如何獲取View的測量寬/高塘安?如何在Activity啟動時獲得View的寬/高?Activity中獲得View寬高的4種辦法秉馏?
- 如何獲取View的測量寬/高耙旦?
- 在measure完成后,可以通過getMeasuredWidth/Height()方法萝究,就能獲得View的測量寬高
- 在一定極端情況下免都,系統(tǒng)需要多次measure,因此得到的值可能不準確帆竹,最好的辦法是在onLayout方法中獲得測量寬/高或者最終寬/高
- 如何在Activity啟動時獲得View的寬/高绕娘?
- Activity的生命周期與View的measure不是同步運行,因此在onCreate/onStart/onResume均無法正確得到
- 若在View沒有測量好時栽连,去獲得寬高险领,會導致最終結果為0
- 有四種辦法去正確獲得寬高
- Activity中獲得View寬高的4種辦法?技術博客大總結
- onWindowFocusChanged
- View已經初始化完畢秒紧,可以獲得寬高绢陌;Activity得到焦點和失去焦點均會調用一次(頻繁onResume和onPause會導致頻繁調用)
- view.post(runnable)
- 通過post將一個runnable投遞到消息隊列尾部;等到Looper調用次runnable時熔恢,View已經完成初始化
- ViewTreeObserver
- 使用ViewTreeObserver的接口脐湾,可以在View樹狀態(tài)改變或者View樹內部View的可見性改變時,onGlobalLayout會被回調叙淌;能正確獲取View寬/高
- view.measure
- onWindowFocusChanged
3.0.1.4 Activity啟動到最終加載ViewRoot(執(zhí)行三大流程)的流程是什么秤掌?
- Activity啟動到最終加載ViewRoot(執(zhí)行三大流程)的流程是什么愁铺?
- Activity調用startActivity方法,最終會調用ActivityThread的handleLaunchActivity方法
- handleLaunchActivity會調用performLauchActivity方法(會調用Activity的onCreate闻鉴,并完成DecorView的創(chuàng)建)和handleResumeActivity方法
- handleResumeActivity方法會做四件事:performResumeActivity(調用activity的onResume方法)茵乱、getDecorView(獲取DecorView)、getWindowManager(獲取WindowManager)孟岛、WindowManager.addView(decor, 1)
- WindowManager.addView(decor, 1)本質是調用WindowManagerGlobal的addView方法瓶竭。其中主要做兩件事:1、創(chuàng)建ViewRootImpl實例 2蚀苛、root.setView(decor, ….)將DecorView作為參數添加到ViewRoot中在验,這樣就將DecorView加載到了Window中
- ViewRootImpl還有一個方法performTraveals方法,用于讓ViewTree開始View的工作流程:其中會調用performMeasure/Layout/Draw()三個方法,分別對應于View的三大流程堵未。
3.0.1.5 自定義View性能優(yōu)化有哪些?針對異常銷毀,自定義View如何優(yōu)化盏触?如何避免創(chuàng)建大量對象渗蟹?
- 自定義View性能優(yōu)化有哪些?
- 避免過度繪制
- 盡量減少或簡化計算
- 避免創(chuàng)建大量對象造成頻繁GC
- 禁止或避免I/O操作
- onDraw中避免冗余代碼、避免創(chuàng)建對象
- 復合View赞辩,要減少布局層級雌芽。
- 狀態(tài)和恢復和保存
- 開啟硬件加速
- 合理使用invalidate的參數版本。
- 減少冗余代碼:不要使用Handler辨嗽,因為已經有post系列方法.
- 使用的線程和動畫世落,要在onDetachedFromWindow中進行清理工作。
- 要妥善處理滑動沖突糟需。
- 避免過度繪制
- 像素點能畫一次就不要多次繪制屉佳,以及繪制看不到的背景
- 開發(fā)者選項里內的工具,只對xml布局有效果,看不到自定義View的過度繪制,仍然需要注意洲押。
- 盡量減少或簡化計算
- 不要做無用計算武花。盡可能的復用計算結果。技術博客大總結
- 沒有數據,或者數據較少的時候應如何處理,沒有事件需要響應的時候如何處理杈帐。
- 應該避免在for或while循環(huán)中做計算体箕。比如:去計算屏幕寬度等信息。
- 避免創(chuàng)建大量對象造成頻繁GC
- 應該避免在for或while循環(huán)中new對象挑童。這是減少內存占用量的有效方法累铅。
- 禁止或避免I/O操作
- I/O操作對性能損耗極大,不要在自定義View中做IO操作站叼。
- onDraw中避免冗余代碼娃兽、避免創(chuàng)建對象
- onDraw中禁止new對象.如:不應該在ondraw中創(chuàng)建Paint對象。Paint類提供了reset方法大年』槐。可以在初始化View時創(chuàng)建對象玉雾。
- 要避免冗余代碼,提高效率轻要。
- 復合View复旬,要減少布局層級。
- 復合控件:繼承自現(xiàn)有的LinearLayout等ViewGroup冲泥,然后組合多個控件來實現(xiàn)效果驹碍。這種實現(xiàn)方法要注意減少布局層級,層級越高性能越差凡恍。
- 狀態(tài)和恢復和保存
- Activity還會因為內存不足或者旋轉屏幕而導致重建Activity志秃,自定義View也要去進行自我狀態(tài)的保存和讀取。
- 在onSaveInstanceState()保存狀態(tài)嚼酝;在onRestoreInstanceState()恢復狀態(tài)
- 開啟硬件加速
- 合理使用invalidate的參數版本浮还。技術博客大總結
- 避免任何請款下之際調用默認參數的invalidate
- 調用有參數的invalidate進行局部和子View刷新,能夠提高性能闽巩。
- 減少冗余代碼:不要使用Handler钧舌,因為已經有post系列方法
- View已經有post系列方法,沒有必要重復去寫涎跨。
- 可以直接使用洼冻,最終會投遞到主線程的Handler中
- 使用的線程和動畫,要在onDetachedFromWindow中進行清理工作隅很。
- View如果有線程或者動畫撞牢,需要及時停止.
- View的onDetachedFromWindow會在View被remove時調用,在該方法內進行終止
- 這樣能避免內存泄露
- 要妥善處理滑動沖突叔营。
- View如果有滑動嵌套情形屋彪,需要處理好滑動沖突
關于其他內容介紹
01.關于博客匯總鏈接
02.關于我的博客
- 我的個人站點:www.yczbj.org, www.ycbjie.cn
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 簡書:http://www.reibang.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
- 開源中國:https://my.oschina.net/zbj1618/blog
- 泡在網上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 郵箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e