Android - View的绘制流程二(layout)

来源:互联网 发布:三星note8画画软件 编辑:程序博客网 时间:2024/06/05 19:36
Android - View的绘制流程一(measure)一文中提到,view绘制的核心逻辑都在ViewRoot的performTraversals()方法中,主要分为三个阶段: 第一个阶段是measure,第二个阶段是layout,第三个阶段是draw
ViewRoot类的performTraversals方法中layout方法的调用,代码如下:
private void performTraversals() 
measure过程结束后,从这里开始layout,同样host是一个DecorView对象。host.mMeasuredWidth 和 host.mMeasuredHeight 则是 measure过程结束之后得到的host的宽和高。
在较低的版本中,定义在view中的layout方法是被final修饰的,不能被复写,本博文参考的是android-19代码,layout没有被final修饰,不过不影响大致流程的理解:
public void 
int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;
    boolean changed = isLayoutModeOptical(mParent) ?setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
protected boolean setFrame(int left, int top, int right, int bottom) {
    boolean changed = false;
    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
        changed = true;
        mLeft = left;
        mTop = top;
        mRight = right;
        mBottom = bottom;
    }
    return changed;
}
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
    mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
}
}
layout方法的参数由父类提供,参数指定了该子视图在父视图中的左、上、右、下的位置,主要逻辑如下:
1、调用setFrame方法,先比对这些参数是否和原来的相同,如果相同,则什么都不做,只要四个参数中有一个发生改变,则将这些参数赋值给view的成员变量(mLeft、mTop、mRight和mBottom)。
2、调用onLayout方法。
// View 中的onLayout方法的定义:
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  }
// ViewGroup 中的onLayout方法的定义:
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
View 中的onLayout方法默认什么都不做,ViewGroup中的onLayout方法则是一个抽象方法,所以,ViewGroup类型的视图都会在自己类中对子视图进行位置计算。
Android - View的绘制流程一(measure)一样,也从MyCustomLinearLayoutA为例,从onLayout()方法开始分析一个ViewGroup类型的视图遍历—layout子视图的详细过程。
LinearLayout类中的onLayout()方法:
protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mOrientation == VERTICAL) {
            layoutVertical(l, t, r, b);
        } else {
            layoutHorizontal(l, t, r, b);
        }
}
根据LinearLayout的布局方向调用layoutVerticallayoutHorizontal,下面以Android - View的绘制流程一(measure)demo中MyCustomLinearLayoutA为例分析竖直方向的layoutVertical
LinearLayout的layoutVertical方法:
void layoutVertical(int left, int top, int right, int bottom) {
        final int paddingLeft = mPaddingLeft;
        int childTop;
        int childLeft;
        // Where right end of child should go
// LinearLayout可用的宽度
        final int width = right - left;
// child最多能够到达的右边位置
        int childRight = width - mPaddingRight;
        // Space available for child
// 计算child的实际可用空间
        int childSpace = width - paddingLeft - mPaddingRight;
       // 计算子view的个数 
        final int count = getVirtualChildCount();
        final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
        final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
// 首先根据gravity的值计算出child的top值
        switch (majorGravity) {
           case Gravity.BOTTOM:
               // mTotalLength contains the padding already
               childTop = mPaddingTop + bottom - top - mTotalLength;
               break;
               // mTotalLength contains the padding already
           case Gravity.CENTER_VERTICAL:
               childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
               break;
           case Gravity.TOP:
           default:
               childTop = mPaddingTop;
               break;
        }
// 然后进行遍历,根据child的水平布局方式(水平居中、靠左或靠右)计算出child的left值
        for (int i = 0; i < count; i++) {
            final View child = getVirtualChildAt(i);
            if (child == null) {
                childTop += measureNullChild(i);
            } else if (child.getVisibility() != GONE) {
// child的宽度和高度已经在measure步骤中计算过了,现在直接获取
                final int childWidth = child.getMeasuredWidth();
                final int childHeight = child.getMeasuredHeight();
                // 获取child的布局参数
                final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
                int gravity = lp.gravity;
                if (gravity < 0) {
                    gravity = minorGravity;
                }
                final int layoutDirection = getLayoutDirection();
                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
                                + lp.leftMargin - lp.rightMargin;
                        break;
                    case Gravity.RIGHT:
                        childLeft = childRight - childWidth - lp.rightMargin;
                        break;
                    case Gravity.LEFT:
                    default:
                        childLeft = paddingLeft + lp.leftMargin;
                        break;
                }
                if (hasDividerBeforeChildAt(i)) {
                    childTop += mDividerHeight;
                }
                childTop += lp.topMargin;
// 计算完child的top和left值,获取到measure步骤中计算的宽和长,再调用setChildFrame方法设置child的位置
                setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight);
private void setChildFrame(View child, int left, int top, int width, int height) {   
//  setChildFrame方法中调用的就是layout方法
        child.layout(left, top, left + width, top + height);
}
                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
                i += getChildrenSkipCount(child, i);
            }
        }
}
至此,view的layout过程就分析完成了。
layout过程的核心就是根据xml文件中的gravity属性计算出top和left的值,再利用第一步 — measure所得到的宽和高的值计算出view对象的坐标(left、top、right和bottom)。
大小和位置都确定了,接下来是第三步 — draw。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 儿子13岁初一不想读书了怎么办 微信不小心点了注册新账号怎么办 在京东买东西商家不发货怎么办 在京东买东西坏了商家不退货怎么办 苯扎氯铵溶液不小心喝了一口怎么办 苯扎氯铵溶液没有稀释就用了怎么办 牛油果切开了但是没熟怎么办 手机安装程序时解析包出错怎么办 因俩人不合适分手了很难受怎么办 中考结束后成绩不好的该怎么办 在京东自营药房买药没有处方怎么办 平安普惠账号不可以注销怎么办? 京东购物非自营货没到降价了怎么办 实体店商家不承认卖的是假货怎么办 衣服上的装饰圆扣掉下来了怎么办 没在京东买东西却收到退款怎么办 小米分期付款买的手机不要了怎么办 唯品会在线支付后商品有问题怎么办 红米手机把时间删了怎么办 红米桌面上的时间删了怎么办 华为手机玩游戏老是闪退怎么办 别人家无线网距离太远信号差怎么办 微信公众号交话费交错了怎么办 手机卡里还有话费销户的话怎么办 号码忘记交话费变成空号怎么办 多屏互动没办法隔空播放怎么办? 一个人长期受一件事的打击怎么办 物流信息显示快递被别人签收怎么办 现在打工的人被领导骂怎么办 加密狗丢了打不开软件了怎么办 手机微信可以打开网页打不开怎么办 手机中国网打开网速慢该怎么办 在韩国用中国软件网速特别慢怎么办 淘宝退货快递一直没显示到货怎么办 高二美术生集训时文化课怎么办 唯品会不支持7天无理由退货怎么办 sy来4am了孤存怎么办 淘宝网买的电器坏了怎么办 在闲鱼上买的电器是坏的怎么办 打开时全屏不知变成小屏怎么办 苹果6s原装数据线不充电怎么办