http://www.oschina.net/question/163910_27566
在Android的佈局體系中,父View負責刷新、佈局顯示子View;而當子View需要刷新時,則是通知父View來完成。這種處理邏輯在View的代碼中明確的表現出來:
1
2
3
4
5
6
7
8
9
10
11
|
public
void
invalidate() { final
ViewParent p = mParent; final
AttachInfo ai = mAttachInfo; if
(p != null
&& ai != null )
{ final
Rect r = ai.mTmpInvalRect; //
設置刷新區域爲自己的尺寸 r.set( 0 ,
0 ,
mRight - mLeft, mBottom - mTop); p.invalidateChild( this ,
r); } } |
子View調用invalidate時,首先找到自己父View(View的成員變量mParent記錄自己的父View),然後將AttachInfo中保存的信息告訴父View刷新自己。
View的父子關係的建立分爲兩種情況:
1) View加入ViewGroup中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private
void
addViewInner(View child, int
index, LayoutParams params, boolean
preventRequestLayout) { ..... //
tell our children if
(preventRequestLayout) { child.assignParent( this ); }
else
{ child.mParent
= this ; } ..... } public
void
setView(View view, WindowManager.LayoutParams attrs, View panelParentView){ ..... view.assignParent( this ); .... } |
AttachInfo是在View第一次attach到Window時,ViewRoot傳給自己的子View的。這個AttachInfo之後,會順着佈局體系一直傳遞到最底層的View。
View.java
1
2
3
4
5
|
void
dispatchAttachedToWindow(AttachInfo info, int
visibility) { mAttachInfo
= info; ..... } |
ViewGroup.java
1
2
3
4
5
6
7
|
void
dispatchAttachedToWindow(AttachInfo info, int
visibility) { super .dispatchAttachedToWindow(info,
visibility); for
( int
i = 0 ;
i < count; i++) { children[i].dispatchAttachedToWindow(info,
visibility); } } |
並且在新的View被加入ViewGroup時,也會將該AttachInfo傳給加入的View
ViewGroup.java
1
2
3
4
5
|
private
void
addViewInner(View child, int
index, LayoutParams params, boolean
preventRequestLayout) { child.dispatchAttachedToWindow(mAttachInfo,
(mViewFlags&VISIBILITY_MASK)); } |
到這裏明白了mParent與AttachInfo代表的意義,可以繼續刷新過程的分析。
在invalidate中,調用父View的invalidateChild,這是一個從第向上回溯的過程,每一層的父View都將自己的顯示區域與傳入的刷新Rect做交集。
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
|
public
final
void
invalidateChild(View child, final
Rect dirty) { ViewParent
parent = this ; final
AttachInfo attachInfo = mAttachInfo; if
(attachInfo != null )
{ final
int []
location = attachInfo.mInvalidateChildLocation; //
需要刷新的子View的位置 location[CHILD_LEFT_INDEX]
= child.mLeft; location[CHILD_TOP_INDEX]
= child.mTop; //
If the child is drawing an animation, we want to copy this flag onto //
ourselves and the parent to make sure the invalidate request goes through final
boolean
drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; //
Check whether the child that requests the invalidate is fully opaque final
boolean
isOpaque = child.isOpaque() && !drawAnimation && child.getAnimation() != null ; //
Mark the child as dirty, using the appropriate flag //
Make sure we do not set both flags at the same time final
int
opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; do
{ View
view = null ; if
(parent instanceof
View) { view
= (View) parent; } if
(drawAnimation) { if
(view != null )
{ view.mPrivateFlags
|= DRAW_ANIMATION; }
else
if
(parent instanceof
ViewRoot) { ((ViewRoot)
parent).mIsAnimating = true ; } } //
If the parent is dirty opaque or not dirty, mark it dirty with the opaque //
flag coming from the child that initiated the invalidate if
(view != null
&& (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { view.mPrivateFlags
= (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; } parent
= parent.invalidateChildInParent(location, dirty); }
while
(parent != null ); } } public
ViewParent invalidateChildInParent( final
int []
location, final
Rect dirty) { if
((mPrivateFlags & DRAWN) == DRAWN) { if
((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE)
{ //
根據父View的位置,偏移刷新區域 dirty.offset(location[CHILD_LEFT_INDEX]
- mScrollX, location[CHILD_TOP_INDEX] - mScrollY); final
int
left = mLeft; final
int
top = mTop; //計算實際可刷新區域 if
(dirty.intersect( 0 ,
0 ,
mRight - left, mBottom - top) || (mPrivateFlags
& DRAW_ANIMATION) == DRAW_ANIMATION) { mPrivateFlags
&= ~DRAWING_CACHE_VALID; location[CHILD_LEFT_INDEX]
= left; location[CHILD_TOP_INDEX]
= top; return
mParent; } }
else
{ mPrivateFlags
&= ~DRAWN & ~DRAWING_CACHE_VALID; location[CHILD_LEFT_INDEX]
= mLeft; location[CHILD_TOP_INDEX]
= mTop; dirty.set( 0 ,
0 ,
mRight - location[CHILD_LEFT_INDEX], mBottom
- location[CHILD_TOP_INDEX]); return
mParent; } } return
null ; } |
這個向上回溯的過程直到ViewRoot那裏結束,由ViewRoot對這個最終的刷新區域做刷新。
ViewRoot.java
1
2
3
4
5
|
public
void
invalidateChild(View child, Rect dirty) { scheduleTraversals(); } |
原文鏈接:http://blog.csdn.net/dragondog/article/details/6454551