?# 優(yōu)化自定義View
編寫:kesenhoo - 原文:http://developer.android.com/training/custom-views/optimizing-view.html
前面的課程學(xué)習(xí)到了如何創(chuàng)建設(shè)計(jì)良好的View,并且能夠使之在手勢(shì)與狀態(tài)切換時(shí)得到正確的反饋。下面要介紹的是如何使得view能夠執(zhí)行更快。為了避免UI顯得卡頓,你必須確保動(dòng)畫(huà)能夠保持在60fps。
為了加速你的view,對(duì)于頻繁調(diào)用的方法,需要盡量減少不必要的代碼。先從onDraw開(kāi)始,需要特別注意不應(yīng)該在這里做內(nèi)存分配的事情,因?yàn)樗鼤?huì)導(dǎo)致GC,從而導(dǎo)致卡頓。在初始化或者動(dòng)畫(huà)間隙期間做分配內(nèi)存的動(dòng)作。不要在動(dòng)畫(huà)正在執(zhí)行的時(shí)候做內(nèi)存分配的事情。
你還需要盡可能的減少onDraw被調(diào)用的次數(shù),大多數(shù)時(shí)候?qū)е耾nDraw都是因?yàn)檎{(diào)用了invalidate().因此請(qǐng)盡量減少調(diào)用invaildate()的次數(shù)。如果可能的話,盡量調(diào)用含有4個(gè)參數(shù)的invalidate()方法而不是沒(méi)有參數(shù)的invalidate()。沒(méi)有參數(shù)的invalidate會(huì)強(qiáng)制重繪整個(gè)view。
另外一個(gè)非常耗時(shí)的操作是請(qǐng)求layout。任何時(shí)候執(zhí)行requestLayout(),會(huì)使得Android UI系統(tǒng)去遍歷整個(gè)View的層級(jí)來(lái)計(jì)算出每一個(gè)view的大小。如果找到有沖突的值,它會(huì)需要重新計(jì)算好幾次。另外需要盡量保持View的層級(jí)是扁平化的,這樣對(duì)提高效率很有幫助。
如果你有一個(gè)復(fù)雜的UI,你應(yīng)該考慮寫一個(gè)自定義的ViewGroup來(lái)執(zhí)行他的layout操作。與內(nèi)置的view不同,自定義的view可以使得程序僅僅測(cè)量這一部分,這避免了遍歷整個(gè)view的層級(jí)結(jié)構(gòu)來(lái)計(jì)算大小。這個(gè)PieChart 例子展示了如何繼承ViewGroup作為自定義view的一部分。PieChart 有子views,但是它從來(lái)不測(cè)量它們。而是根據(jù)他自身的layout法則,直接設(shè)置它們的大小。
從Android 3.0開(kāi)始,Android的2D圖像系統(tǒng)可以通過(guò)GPU (Graphics Processing Unit))來(lái)加速。GPU硬件加速可以提高許多程序的性能。但是這并不是說(shuō)它適合所有的程序。Android framework讓你能過(guò)隨意控制你的程序的各個(gè)部分是否啟用硬件加速。
參考 Android Developers Guide 中的Hardware Acceleration 來(lái)學(xué)習(xí)如何在application, activity, 或 window 層啟用加速。注意除了 Android Guide 的指導(dǎo)之外,你必須要設(shè)置你的應(yīng)用的target API為11,或更高,通過(guò)在你的AndroidManifest.xml 文件中增加 < uses-sdk android:targetSdkVersion="11"/> 。
一旦你開(kāi)啟了硬件加速,性能的提示并不一定可以明顯察覺(jué)到。移動(dòng)設(shè)備的GPU在某些例如scaling,rotating與translating的操作中表現(xiàn)良好。但是對(duì)其他一些任務(wù),比如畫(huà)直線或曲線,則表現(xiàn)不佳。為了充分發(fā)揮GPU加速,你應(yīng)該最大化GPU擅長(zhǎng)的操作的數(shù)量,最小化GPU不擅長(zhǎng)操作的數(shù)量。
在下面的例子中,繪制pie是相對(duì)來(lái)說(shuō)比較費(fèi)時(shí)的。解決方案是把pie放到一個(gè)子view中,并設(shè)置View使用LAYER_TYPE_HARDWARE來(lái)進(jìn)行加速。
private class PieView extends View {
public PieView(Context context) {
super(context);
if (!isInEditMode()) {
setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Item it : mData) {
mPiePaint.setShader(it.mShader);
canvas.drawArc(mBounds,
360 - it.mEndAngle,
it.mEndAngle - it.mStartAngle,
true, mPiePaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mBounds = new RectF(0, 0, w, h);
}
RectF mBounds;
}
通過(guò)這樣的修改以后,PieChart.PieView.onDraw()只會(huì)在第一次現(xiàn)實(shí)的時(shí)候被調(diào)用。之后,pie chart會(huì)被緩存為一張圖片,并通過(guò)GPU來(lái)進(jìn)行重畫(huà)不同的角度。GPU特別擅長(zhǎng)這類的事情,并且表現(xiàn)效果突出。
緩存圖片到hardware layer會(huì)消耗video memory,而video memory又是有限的?;谶@樣的考慮,僅僅在用戶觸發(fā)scrolling的時(shí)候使用LAYER_TYPE_HARDWARE,在其他時(shí)候,使用LAYER_TYPE_NONE。