面試問題
- onMeasure onLayout onDraw的作用
- 自定義View和自定義ViewGroup的區(qū)別
- getMeasureWidth和getWidth的區(qū)別
- getLeft getRight getWidth表示的意義
- MeasureSpec是什么
- 如何優(yōu)化自定義View
- 自定義view效率高于xml定義嗎
- Draw的基本流程
- setWillNotDraw的作用
- invalidate()列牺、postInvalidate()变过、requestLayout()的區(qū)別
onMeasure onLayout onDraw的作用
- onMeasure:用來計算控件的尺寸叙赚,告訴父控件此控件需要的尺寸。
- onLayout:父容器的onLayout()調(diào)用子類的layout()來確定子view在viewGroup中的位置燎竖。
- onDraw:自定義view的關(guān)鍵方法,用于繪制界面,可以重寫此方法以繪制自定義View驳遵。
自定義View和自定義ViewGroup的區(qū)別
- ViewGroup是一個容器怨喘,而這個容器是繼承與View津畸。
- ViewGroup是一個基類,并且是Layout和一些View組件的基類必怜。
getMeasureWidth和getWidth的區(qū)別
- getMeasureWidth()在走完onMeasure()方法之后有值
- getWidth()在layout()之后有值,是布局完成之后的確切值
- getMeasureWidth()在onMeasure()之后調(diào)用
- getWidth()在layout()之后調(diào)用
- getMeasureWidth()是view的一個測量大小
- getWidth()是view被父布局擺放的大小
getLeft getRight getWidth表示的意義
getWidth()的值就是getRight()-getLeft()的值肉拓。
MeasureSpec是什么
從MeasureSpec類的定義我們知道,它封裝了對子View的布局要求梳庆,由尺寸和模式組成暖途,其實MeasureSpec代表一個32位的int值,高2位表示SpecMode膏执,低30位表示SpecSize,而SpecSize是指在某種SpecMode下的規(guī)格大小驻售,從源碼我們看出它內(nèi)部定義了很多常量,從api17以后開始采用位運算更米,因為位運算的效率最高欺栗,我們看下三種模式:
UNSPECIFIED:未指定模式,不對View大小做限制,如:ListView迟几,ScrollView消请。
EXACTLY:精確模式,確切的大小类腮,如:100dp或者march_parent臊泰。
AT_MOST:最大模式,大小不可超過某數(shù)值蚜枢,如:wrap_content缸逃。
如何優(yōu)化自定義View
為了加速你的view,對于頻繁調(diào)用的方法祟偷,需要盡量減少不必要的代碼察滑。先從onDraw開始,需要特別注意不應(yīng)該在這里做內(nèi)存分配的事情修肠,因為它會導(dǎo)致GC贺辰,從而導(dǎo)致卡頓。在初始化或者動畫間隙期間做分配內(nèi)存的動作嵌施。不要在動畫正在執(zhí)行的時候做內(nèi)存分配的事情饲化。
你還需要盡可能的減少onDraw被調(diào)用的次數(shù),大多數(shù)時候?qū)е耾nDraw都是因為調(diào)用了invalidate()吗伤,因此請盡量減少調(diào)用invaildate()的次數(shù)吃靠。如果可能的話,盡量調(diào)用含有4個參數(shù)的invalidate()方法而不是沒有參數(shù)的invalidate()足淆。沒有參數(shù)的invalidate會強制重繪整個view巢块。
另外一個非常耗時的操作是請求layout。任何時候執(zhí)行requestLayout()巧号,會使得Android UI系統(tǒng)去遍歷整個View的層級來計算出每一個view的大小族奢。如果找到有沖突的值,它會需要重新計算好幾次丹鸿。另外需要盡量保持View的層級是扁平化的越走,這樣對提高效率很有幫助。
如果你有一個復(fù)雜的UI靠欢,你應(yīng)該考慮寫一個自定義的ViewGroup來執(zhí)行他的layout操作廊敌。與內(nèi)置的view不同,自定義的view可以使得程序僅僅測量這一部分门怪,這避免了遍歷整個view的層級結(jié)構(gòu)來計算大小骡澈。
自定義view效率高于xml定義嗎
自定義view效率高于xml定義:
- 少了解析xml。
- 自定義View減少了ViewGroup與View之間的測量薪缆,包括父量子秧廉,子量自身伞广,子在父中位置擺放,當(dāng)子view變化時疼电,父的某些屬性都會跟著變化嚼锄。
Draw的基本流程
繪制基本上可以分為六個步驟:
- 首先繪制View的背景;
- 如果需要的話蔽豺,保持canvas的圖層区丑,為fading做準(zhǔn)備;
- 然后修陡,繪制View的內(nèi)容沧侥;
- 接著,繪制View的子View魄鸦;
- 如果需要的話宴杀,繪制View的fading邊緣并恢復(fù)圖層;
- 最后拾因,繪制View的裝飾(例如滾動條等等)旺罢。
setWillNotDraw的作用
如果一個View不需要繪制任何內(nèi)容,那么設(shè)置這個標(biāo)記位為true以后绢记,系統(tǒng)會進行相應(yīng)的優(yōu)化扁达。
- 默認情況下,View沒有啟用這個優(yōu)化標(biāo)記位蠢熄,但是ViewGroup會默認啟用這個優(yōu)化標(biāo)記位跪解。
- 當(dāng)我們的自定義控件繼承于ViewGroup并且本身不具備繪制功能時,就可以開啟這個標(biāo)記位從而便于系統(tǒng)進行后續(xù)的優(yōu)化签孔。
- 當(dāng)明確知道一個ViewGroup需要通過onDraw來繪制內(nèi)容時叉讥,我們需要顯示地關(guān)閉WILL_NOT_DRAW這個標(biāo)記位。
invalidate()饥追、postInvalidate()节吮、requestLayout()的區(qū)別
- invalidate()與postInvalidate()都用于刷新View,主要區(qū)別是invalidate()在主線程中調(diào)用判耕,若在子線程中使用需要配合handler;而postInvalidate()可在子線程中直接調(diào)用翘骂。
- invalidate()和postInvalidate():View(非容器類)調(diào)用invalidate方法只會重繪自身壁熄,ViewGroup調(diào)用則會重繪整個View樹。
- requestLayout:View執(zhí)行requestLayout方法碳竟,會向上遞歸到頂級父View中草丧,再執(zhí)行這個頂級父View的requestLayout,所以其他View的onMeasure莹桅,onLayout也可能會被調(diào)用昌执。