美文网首页
View的Layout过程

View的Layout过程

作者: 7i昂 | 来源:发表于2019-10-16 18:34 被阅读0次

    layout过程#

    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
    ......
    mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
    

    performTraversals 方法执行完mView.measure 计算出mMeasuredXXX后就开始执行layout 函数来确定View具体放在哪个位置,我们计算出来的View目前只知道view矩阵的大小,具体这个矩阵放在哪里,这就是layout 的工作了。layout的主要作用 :根据子视图的大小以及布局参数将View树放到合适的位置上。

    既然是通过mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); 那我们来看下layout 函数做了什么,mView肯定是个ViewGroup,不会是View,我们直接看下ViewGroup 的layout函数

    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;    
       }
    }
    

    代码可以看个大概,LayoutTransition是用于处理ViewGroup增加和删除子视图的动画效果,也就是说如果当前ViewGroup未添加LayoutTransition动画,或者LayoutTransition动画此刻并未运行,那么调用super.layout(l, t, r, b),继而调用到ViewGroup中的onLayout,否则将mLayoutSuppressed设置为true,等待动画完成时再调用requestLayout()。
    这个函数是final 不能重写,所以ViewGroup的子类都会调用这个函数,layout 的具体实现是在super.layout(l, t, r, b)里面做的,那么我接下来看一下View类的layout函数

     public final void layout(int l, int t, int r, int b) {
           .....
          //设置View位于父视图的坐标轴
           boolean changed = setFrame(l, t, r, b); 
           //判断View的位置是否发生过变化,看有必要进行重新layout吗
           if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
               if (ViewDebug.TRACE_HIERARCHY) {
                   ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
               }
               //调用onLayout(changed, l, t, r, b); 函数
               onLayout(changed, l, t, r, b);
               mPrivateFlags &= ~LAYOUT_REQUIRED;
           }
           mPrivateFlags &= ~FORCE_LAYOUT;
           .....
       }
    

    1、setFrame(l, t, r, b) 可以理解为给mLeft 、mTop、mRight、mBottom赋值,然后基本就能确定View自己在父视图的位置了,这几个值构成的矩形区域就是该View显示的位置,这里的具体位置都是相对与父视图的位置。

    2、回调onLayout,对于View来说,onLayout只是一个空实现,一般情况下我们也不需要重载该函数,:

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
    
        }  
    

    对于ViewGroup 来说,唯一的差别就是ViewGroup中多了关键字abstract的修饰,要求其子类必须重载onLayout函数。

    @Override  
    protected abstract void onLayout(boolean changed,  
            int l, int t, int r, int b); 
    而重载onLayout的目的就是安排其children在父视图的具体位置,那么如何安排子View的具体位置呢?
    
     int childCount = getChildCount() ; 
      for(int i=0 ;i<childCount ;i++){
           View child = getChildAt(i) ;
           //整个layout()过程就是个递归过程
           child.layout(l, t, r, b) ;
        }
    

    代码很简单,就是遍历自己的孩子,然后调用 child.layout(l, t, r, b) ,给子view 通过setFrame(l, t, r, b) 确定位置,而重点是(l, t, r, b) 怎么计算出来的呢。还记得我们之前测量过程,测量出来的MeasuredWidth和MeasuredHeight吗?还记得你在xml 设置的Gravity吗?还有RelativeLayout 的其他参数吗,没错,就是这些参数和MeasuredHeight、MeasuredWidth 一起来确定子View在父视图的具体位置的。具体的计算过程大家可以看下最简单FrameLayout 的onLayout 函数的源码,每个不同的ViewGroup 的实现都不一样,这边不做具体分析了吧。

    3、MeasuredWidth和MeasuredHeight这两个参数为layout过程提供了一个很重要的依据(如果不知道View的大小,你怎么固定四个点的位置呢),但是这两个参数也不是必须的,layout过程中的4个参数l, t, r, b完全可以由我们任意指定,而View的最终的布局位置和大小(mRight - mLeft=实际宽或者mBottom-mTop=实际高)完全由这4个参数决定,measure过程得到的mMeasuredWidth和mMeasuredHeight提供了视图大小测量的值,但我们完全可以不使用这两个值,所以measure过程并不是必须的。如果我们不使用这两个值,那么getMeasuredWidth() 和getWidth() 就很有可能不是同一个值,它们的计算是不一样的:

    public final int getMeasuredWidth() {  
            return mMeasuredWidth & MEASURED_SIZE_MASK;  
        }  
    public final int getWidth() {  
            return mRight - mLeft;  
        }
    

    layout 过程相对简单些,分析就到此为止。

    相关文章

      网友评论

          本文标题:View的Layout过程

          本文链接:https://www.haomeiwen.com/subject/azwumctx.html