ViewGroup
A ViewGroup is a special view that can contain other views. The view group is the base class for layouts and views containers. This class also defines the
android.view.ViewGroup.LayoutParams class which serves as the base class for layouts parameters.
Overview
首先看下ViewGroup的繼承層次:
public abstract class ViewGroup extends View implements ViewParent, ViewManager
可以看到ViewGroup與View第一個不同點在于衙猪,ViewGroup是一個抽象類哮幢,繼承自View竖般,實現(xiàn)了ViewParent和ViewManager兩個接口
ViewParent和ViewManager
首先是ViewParent這個接口,內(nèi)含的部分方法如下:
其次是ViewManager的方法:
ViewManager的方法很好理解习勤,對View進(jìn)行的增刪改3個操作
構(gòu)造方法
和View類似类少,ViewGroup也有4個構(gòu)造函數(shù):
public ViewGroup(Context context)
public ViewGroup(Context context, AttributeSet attrs)
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr)
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
但是實際上砌庄,前3個構(gòu)造函數(shù)最終也只是調(diào)用到第4個昭雌,而第4個構(gòu)造函數(shù)的實現(xiàn)也非常簡單明了:
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initViewGroup();
initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
}
即兩個init方法的調(diào)用:
private void initViewGroup() {
// ViewGroup doesn't draw by default
if (!debugDraw()) {
setFlags(WILL_NOT_DRAW, DRAW_MASK);
}
mGroupFlags |= FLAG_CLIP_CHILDREN;
mGroupFlags |= FLAG_CLIP_TO_PADDING;
mGroupFlags |= FLAG_ANIMATION_DONE;
mGroupFlags |= FLAG_ANIMATION_CACHE;
mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
}
setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
mChildren = new View[ARRAY_INITIAL_CAPACITY];
mChildrenCount = 0;
mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
}
首先initViewGroup()方法對其自身的一些標(biāo)志位等做了初始操作复唤;然后調(diào)用initFromAttributes(),從函數(shù)名稱也可以猜到烛卧,這個函數(shù)作用是從xml屬性中進(jìn)行初始化操作:
private void initFromAttributes(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyleAttr,
defStyleRes);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.ViewGroup_clipChildren:
setClipChildren(a.getBoolean(attr, true));
break;
case R.styleable.ViewGroup_clipToPadding:
setClipToPadding(a.getBoolean(attr, true));
break;
case R.styleable.ViewGroup_animationCache:
setAnimationCacheEnabled(a.getBoolean(attr, true));
break;
……
case R.styleable.ViewGroup_touchscreenBlocksFocus:
setTouchscreenBlocksFocus(a.getBoolean(attr, false));
break;
}
}
a.recycle();
}
繪制相關(guān)
從View的繪制源碼學(xué)習(xí)中已經(jīng)知道佛纫,一個View的繪制,包括measure/layout/draw三個階段总放,那么作為View的子類呈宇,ViewGroup的繪制也無非這3個階段:
measure
因為View的measure()方法是個final方法,因此在ViewGroup中不能重寫改方法间聊,而ViewGroup中也沒有實現(xiàn)onMeasure()方法攒盈,但在measure階段,ViewGroup提供了幾個measure相關(guān)的方法:
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec)
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed)
這幾個方法在實現(xiàn)上哎榴,關(guān)鍵的點是一致的型豁,因此選擇相對簡潔的measureChild()來看:
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
最關(guān)鍵的點在于最后一句,child.measure()尚蝌,從View繪制過程已經(jīng)知道迎变,measure階段的目的在于,讓View得到自己所占據(jù)的大小飘言,因此對ViewGroup來說衣形,measure階段,就需要讓自己的所有子View知道自己的大小姿鸿。
layout
在layout階段谆吴,ViewGroup重寫了View的layout()和onlayout()方法
實際上layout并沒有做什么實質(zhì)的工作:
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
mLayoutCalledWhileSuppressed = true;
}
}
更加關(guān)鍵的是onLayout()方法,這里變成了abstract抽象方法苛预,這也是為什么ViewGroup是一個抽象類的原因
protected abstract void onLayout(b
oolean changed,
int l, int t, int r, int b);
draw
在draw階段句狼,ViewGroup里相關(guān)的函數(shù)是dispatchDraw(),drawChild():在dispatchDraw()中遍歷所有的子View热某,對所有子View執(zhí)行drawChild()方法腻菇,而drawChild()方法的實現(xiàn)非常簡單:
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
ViewGroup的繪制,最終也就是各個子View自身的繪制昔馋。
總結(jié)
最后總結(jié)一下ViewGroup和View的差異:
- View是一個普通類筹吐,而ViewGroup是一個抽象類
- measure階段,View通過measure()和onMeasure()來確定自身的占據(jù)范圍秘遏,ViewGroup通過measureChild()等方法來確定各個子View的占據(jù)范圍
- layout階段丘薛,View通過layout()和onLayout()函數(shù)來確定位置,而ViewGroup中onLayout()變成抽象方法垄提,需要子類實現(xiàn)榔袋;
- draw階段周拐,View通過draw()和onDraw()方法來實現(xiàn)繪制铡俐,ViewGroup通過dispatchDraw()和drawChild()方法來讓各個子View繪制自身凰兑。