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.youkuaiyun.com/dragondog/article/details/6454551