Android O: View的绘制流程(三):布局和绘制
前一篇文章Android O: View的繪制流程(二):測(cè)量中,?
我們分析了View的測(cè)量流程。?
當(dāng)View測(cè)量完畢后,就要開始進(jìn)行布局和繪制相關(guān)的工作,?
本篇文章就來分析下這部分流程。
一、View的layout?
我們從ViewRootImpl.java的performLayout函數(shù)開始分析:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
跟進(jìn)View的layout函數(shù):
public void layout(int l, int t, int r, int b) {.............//保留舊數(shù)據(jù)int oldL = mLeft;int oldT = mTop;int oldB = mBottom;int oldR = mRight;//measure時(shí)也判斷過, 當(dāng)前View為ViewGroup且設(shè)置為視覺邊界布局模式時(shí),才返回true//setOpticalFrame最終也會(huì)調(diào)用setFrame//setFrame將會(huì)設(shè)置View的位置(mLeft, mTop, mRight, mBottom)//這四個(gè)參數(shù)描述了View相對(duì)其父View的位置//如果View的位置發(fā)生了變化,就會(huì)返回trueboolean changed = isLayoutModeOptical(mParent) ?setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);//View的measure函數(shù)中, 會(huì)判斷是否增加PFLAG_LAYOUT_REQUIREDif (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {//布局其child viewonLayout(changed, l, t, r, b);.........mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;//如果有觀察者, 回調(diào)通知 ListenerInfo li = mListenerInfo;if (li != null && li.mOnLayoutChangeListeners != null) {ArrayList<OnLayoutChangeListener> listenersCopy =(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();int numListeners = listenersCopy.size();for (int i = 0; i < numListeners; ++i) {listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);}}}........ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
從上面代碼可以看出,layout函數(shù)會(huì)判斷View的位置是否發(fā)生了改變。?
若發(fā)生了改變,則需要調(diào)用onLayout函數(shù)對(duì)子View進(jìn)行重新布局。
由于普通View(非ViewGroup)不含子View,所以View.java中的onLayout方法為空實(shí)現(xiàn)。?
因此接下來,我們看看ViewGroup類的onLayout方法。
二、FrameLayout的onLayout?
ViewGroup中的onLayout為一個(gè)抽象方法,由具體的ViewGroup實(shí)現(xiàn)。?
對(duì)于DecorView而言,將調(diào)用FrameLayout的onLayout方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
上面代碼中,childLeft代表了最終子View的左邊緣距父View左邊緣的距離;?
childTop代表了子View的上邊緣距父View的上邊緣的距離。?
當(dāng)計(jì)算出child view的位置信息后,會(huì)繼續(xù)調(diào)用layout方法,繼續(xù)遞歸布局。
對(duì)于ViewGroup而言,onMeasure和onLayout應(yīng)該是配套使用的。?
我們目前只以比較簡(jiǎn)單的FrameLayout為例,分析了這部分過程。?
對(duì)于其它ViewGroup而言,遞歸的方式與FrameLayout類似,?
但具體的細(xì)節(jié)差異較大。
三、ViewRootImpl的performDraw?
完成了measure和layout階段后,View的大小和位置基本上就確定了,?
接下來就進(jìn)入繪制階段。
我們同樣從ViewRootImpl的performDraw函數(shù)入手:
private void performDraw() {.......final boolean fullRedrawNeeded = mFullRedrawNeeded;mFullRedrawNeeded = false;mIsDrawing = true;........try {draw(fullRedrawNeeded);} finally {mIsDrawing = false;} ........ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
我們跟進(jìn)ViewRootImpl的draw函數(shù):
private void draw(boolean fullRedrawNeeded) {//省略滾動(dòng)、動(dòng)畫相關(guān)的細(xì)節(jié)...........if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {//如果采用硬件渲染繪制且ThreadedRenderer可用,進(jìn)入該流程if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {..........//最后將通過native函數(shù)nDrawRenderNode繪制mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);} else {//如果需要進(jìn)行硬件渲染,但ThreadedRenderer不可用//則進(jìn)行ThreadedRenderer初始化工作(以便下次用)..........// 不用硬件渲染,或硬件渲染不可用,則靠軟件繪制if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {return;}}}......... }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
繼續(xù)分析drawSoftware函數(shù):
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty) {// Draw with software renderer.final Canvas canvas;try {..........//獲取畫布canvas = mSurface.lockCanvas(dirty);.........} catch (Surface.OutOfResourcesException e) {.........} catch (IllegalArgumentException e) {........}try {........try {........//關(guān)鍵在此//此時(shí)調(diào)用的是DecorView的draw函數(shù)mView.draw(canvas);........} finally {........}} finally {try {//unlocksurface.unlockCanvasAndPost(canvas);} catch (IllegalArgumentException e) {...........}.........}........ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
從上述代碼可以看出,在不使用硬件繪制的條件下,?
ViewRootImpl的performDraw函數(shù)最終會(huì)調(diào)用View的draw函數(shù)。
四、View的draw?
View.java中draw函數(shù)的源碼如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
在上面的代碼中,我們目前最關(guān)心的是onDraw和dispatchDraw。?
其中,onDraw用于繪制View自身,需要每個(gè)View自己實(shí)現(xiàn);?
dispatchDraw用于繪制child view,由ViewGroup實(shí)現(xiàn)。
最后,我們來看看ViewGroup中的dispatchDraw函數(shù):
protected void dispatchDraw(Canvas canvas) {boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);final int childrenCount = mChildrenCount;final View[] children = mChildren;int flags = mGroupFlags;//處理動(dòng)畫相關(guān)的繪制if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {.............}................// Only use the preordered list if not HW accelerated, since the HW pipeline will do the// draw reordering internallyfinal ArrayList<View> preorderedList = usingRenderNodeProperties? null : buildOrderedChildList();final boolean customOrder = preorderedList == null&& isChildrenDrawingOrderEnabled();//默認(rèn)先序遍歷繪制for (int i = 0; i < childrenCount; i++) {.......final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {//內(nèi)部還是調(diào)用View的draw函數(shù)more |= drawChild(canvas, child, drawingTime);}}............. }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
上述代碼中我們省略了許多細(xì)節(jié),不過仍可以很清晰地看出,?
整個(gè)View的視圖結(jié)構(gòu)是按照先序遍歷來繪制的(盡管沒有分析具體的實(shí)現(xiàn)細(xì)節(jié),?
但繪制時(shí)肯定會(huì)依賴布局時(shí)得到的信息)。
對(duì)于一個(gè)ViewGroup而言,會(huì)先繪制自身,?
然后繪制child view,最后再繪制一些裝飾組件等。
五、總結(jié)?
至此,View繪制相關(guān)的主要流程全部分析完畢。?
毫無疑問,我們漏掉了太多的細(xì)節(jié)。
其中,有的細(xì)節(jié)不太重要,所以我們不需要關(guān)注;?
有的細(xì)節(jié)則不是行文的重點(diǎn),我們也有意忽略掉了;?
還有些細(xì)節(jié),則需要對(duì)View繪制有更深刻的理解,?
才能進(jìn)一步分析。?
目前,由于自己也是第一次深入看View相關(guān)的源碼,?
故未做進(jìn)一步分析。以后如果碰到相關(guān)的問題,再做進(jìn)一步的補(bǔ)充。
總結(jié)
以上是生活随笔為你收集整理的Android O: View的绘制流程(三):布局和绘制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android O: View的绘制流程
- 下一篇: Android内存分析工具:Memory