android开发艺术探索第四章

view的工作原理

DecorView和ViewRoot
ViewRoot对应ViewRootImpl,他是链接windowsmanager和DecorView的枢纽
viewroot控制view的三大流程
这里写图片描述
流程如图所示
DecorView中包含一个标题栏和一个内容栏
内容栏的id为content,在setcontentview的时候设置view进去

MeasureSpec

MeasureSpec.getSize(heightMeasureSpec);
MeasureSpec.getMode(heightMeasureSpec);

获取测量模式
测量模式有三种

/**
         * Measure specification mode: The parent has not imposed any constraint
         * on the child. It can be whatever size it wants.
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
         * Measure specification mode: The parent has determined an exact size
         * for the child. The child is going to be given those bounds regardless
         * of how big it wants to be.
         */
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        /**
         * Measure specification mode: The child can be as large as it wants up
         * to the specified size.
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;

AT_MOST对应wrap_content
EXACTLY对应具体的数值或者match_parent
UNSPECIFIED一般为系统内部使用

如果测量模式是AT_MOST
需要手动计算自定义的宽高,比如自定义view是由俩个圆形组成,横向排列
那么宽为俩个圆的半径+左边距+右边距
那么高为一个圆的半径+上边距+下边距

为什么测量模式是AT_MOST要特殊处理?
通过view的源码分析得知
下面是view的onMeasure源码

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

   public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

从源码中可以看到测量模式AT_MOST,EXACTLY的处理结果是一样的,所以如果自定义view你使用wrap_content,他的效果和match_parent是一样的

view的测量模式规则如下表
这里写图片描述

继续分析view的onMeasure过程源码,上面的俩个方法

 protected int getSuggestedMinimumHeight() {
        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());

    }

    /**
     * Returns the suggested minimum width that the view should use. This
     * returns the maximum of the view's minimum width
     * and the background's minimum width
     *  ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).
     * <p>
     * When being used in {@link #onMeasure(int, int)}, the caller should still
     * ensure the returned width is within the requirements of the parent.
     *
     * @return The suggested minimum width of the view.
     */
    protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }

这个最小宽高可以在xml文件中设置,如果view设置了背景,就返回max(mMinWidth, mBackground.getMinimumWidth());他们的最大值


    public int getMinimumWidth() {
        final int intrinsicWidth = getIntrinsicWidth();
        return intrinsicWidth > 0 ? intrinsicWidth : 0;
    }

getMinimumWidth方法返回的就是drawable的原始宽度
比如bitmapdrawable就还有原始宽度

继续分析viewGroup的onMeasure过程

viewgroup没有重写view的onMeasure方法
而是measureChildren方法

  protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

viewgroup会遍历自己的子view,然后继续调用他们的measure方法

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

viewgroup是个抽象类,没有具体的onmeasure实现过程。所以我们找一个他的实现类来分析,比如linearlayout

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

选择其中一个measureVertical继续分析,代码很长,抽取了一部门重要的

  void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);
            if (child == null) {
                mTotalLength += measureNullChild(i);
                continue;
            }

            if (child.getVisibility() == View.GONE) {
               i += getChildrenSkipCount(child, i);
               continue;
            }

          final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
                measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                        heightMeasureSpec, usedHeight);

}

  }

可以看到linearlayout也会遍历自己的子元素,然后调用measureChildBeforeLayout
这个方法就是调用子元素调用他们的onmeasure方法

  void measureChildBeforeLayout(View child, int childIndex,
            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
            int totalHeight) {
        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
                heightMeasureSpec, totalHeight);
    }
   protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

linearlayout在测量完子view之后,根据子view的宽高得到自己的宽高

在onmeasure之后。就能通过getMeasureHeight获得view的宽高,但是这个获取的时机是需要注意的,它与activity的生命周期并没有很直接的关系,一般建议在以下几种地方能获取到

1,onWindowFocusChanged

  @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        if (hasWindowFocus){
            //获取宽高
        }else {

        }
    }

2,onlayout

3,通过post一个runnable到消息队列,

  getRootView().post(new Runnable() {
                @Override
                public void run() {
                    //获取宽高
                }
            })

4,通过viewtree

  ViewTreeObserver observer=view.getViewTreeObserver();

        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //获取宽高
            }
        });

接下来是三大流程之onlayout

//未完待续,持续更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值