当前位置:操作系统 > 安卓/Android >>

Android中layout过程详解 (结合Android 4.0.4 最新源码)

 与onMeasure过程类似,ViewGroup在onLayout函数中通过调用其children的layout函数来设置子视图相对与父视图中的位置,具体位置由函数layout的参数决定,当我们继承ViewGroup时必须重载onLayout函数(ViewGroup中onLayout是abstract修饰),然而onMeasure并不要求必须重载,因为相对与layout来说,measure过程并不是必须的,具体后面会提到。首先我们来看下View.java中函数layout和onLayout的源码:
[java] 
public void layout(int l, int t, int r, int b) { 
        int oldL = mLeft; 
        int oldT = mTop; 
        int oldB = mBottom; 
        int oldR = mRight; 
        boolean changed = setFrame(l, t, r, b); 
        if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { 
            if (ViewDebug.TRACE_HIERARCHY) { 
                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT); 
            } 
 
            onLayout(changed, l, t, r, b); 
            mPrivateFlags &= ~LAYOUT_REQUIRED; 
 
            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); 
                } 
            } 
        } 
        mPrivateFlags &= ~FORCE_LAYOUT; 
    } 
      函数layout的主体过程还是很容易理解的,首先通过调用setFrame函数来对4个成员变量(mLeft,mTop,mRight,mBottom)赋值,然后回调onLayout函数,最后回调所有注册过的listener的onLayoutChange函数。
      对于View来说,onLayout只是一个空实现,一般情况下我们也不需要重载该函数:
[java]
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
    } 
      接着我们来看下ViewGroup.java中layout的源码:
[java] 
public final void layout(int l, int t, int r, int b) { 
        if (mTransition == null || !mTransition.isChangingLayout()) { 
            super.layout(l, t, r, b); 
        } else { 
            // record the fact that we noop'd it; request layout when transition finishes 
            mLayoutSuppressed = true; 
        } 
    } 
      super.layout(l, t, r, b)调用的即是View.java中的layout函数,相比之下ViewGroup增加了LayoutTransition的处理,LayoutTransition是用于处理ViewGroup增加和删除子视图的动画效果,也就是说如果当前ViewGroup未添加LayoutTransition动画,或者LayoutTransition动画此刻并未运行,那么调用super.layout(l, t, r, b),继而调用到ViewGroup中的onLayout,否则将mLayoutSuppressed设置为true,等待动画完成时再调用requestLayout()。
      上面super.layout(l, t, r, b)会调用到ViewGroup.java中onLayout,其源码实现如下:
[java] view plaincopy
@Override 
protected abstract void onLayout(boolean changed, 
        int l, int t, int r, int b); 
      和前面View.java中的onLayout实现相比,唯一的差别就是ViewGroup中多了关键字abstract的修饰,也就是说ViewGroup类只能用来被继承,无法实例化,并且其子类必须重载onLayout函数,而重载onLayout的目的就是安排其children在父视图的具体位置。重载onLayout通常做法就是起一个for循环调用每一个子视图的layout(l, t, r, b)函数,传入不同的参数l, t, r, b来确定每个子视图在父视图中的显示位置。
      那layout(l, t, r, b)中的4个参数l, t, r, b如何来确定呢?联想到之前的measure过程,measure过程的最终结果就是确定了每个视图的mMeasuredWidth和mMeasuredHeight,这两个参数可以简单理解为视图期望在屏幕上显示的宽和高,而这两个参数为layout过程提供了一个很重要的依据(但不是必须的),为了说明这个过程,我们来看下LinearLayout的layout过程:


[java] 
void layoutVertical() { 
        …… 
        for (int i = 0; i < count; i++) { 
            final View child = getVirtualChildAt(i); 
            if (child == null) { 
                childTop += measureNullChild(i); 
            } else if (child.getVisibility() != GONE) { 
                final int childWidth = child.getMeasuredWidth(); 
                final int childHeight = child.getMeasuredHeight(); 
                …… 
                setChildFrame(child, childLeft, childTop + getLocationOffset(child), 
                        childWidth, childHeight); 
                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); 
 
                i += getChildrenSkipCount(child, i); 
            } 
        } 
    } 
private void setChildFrame(View child, int left, int top, int width, int height) {    

补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,