美文网首页
组件测量那些事

组件测量那些事

作者: cxlin007 | 来源:发表于2016-09-12 11:02 被阅读13次

简介

组件在页面上的展示要经过测量、布局、绘制三个过程,而测量就是把我们定义的组件宽和高转换为具体的大小的这个过程。
我们常见的组件的宽高定义如下:

<TextView
    android:id="@+id/name1"
    android:layout_width="100dp"
    android:layout_height="100dp" />
<TextView
    android:id="@+id/name1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
<TextView
    android:id="@+id/name1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

宽高定义为100dp,那么最后组件的宽高就是100dp,这很好理解,那wrap_content和match_parent呢,我们都知道,wrap_content根据组件自身的大小变化而变化,match_parent指的要占满父视图。那么系统具体是如何解析的呢?

系统把组件的长宽定义转换相应的测量规则

在页面测量的时候都会调用measureChild或measureChildWithMargins方法对组件进行测量,已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);}

关于MeasureSpec的定义就不再这里阐述了,大家可以查看其他文章。
这段代码很短,主要就是描述了通过getChildMeasureSpec把父视图的测量规则和组件的布局参数转换为子视图的测量规则,进行测量。再来看getChildMeasureSpec方法:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    int size = Math.max(0, specSize - padding);
    int resultSize = 0;
    int resultMode = 0;
    switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Child wants a specific size... so be it
            resultSize = childDimension; 
           resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us. 
           resultSize = size;
            resultMode = MeasureSpec.AT_MOST; 
       } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us. 
           resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
       ... 
   }
   return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

1、如果childDimension >0,指的就是组件定义了具体的大小(MATCH_PARENT为-1,WRAP_CONTENT为-2),例如:

<TextView
    android:id="@+id/name1"
    android:layout_width="100dp"
    android:layout_height="100dp" />

组件的大小就是childDimension ,测量模式为MeasureSpec.EXACTLY。
2、如果childDimension == LayoutParams.MATCH_PARENT,组件大小就是父视图提供的大小size,而测量模式也是MeasureSpec.EXACTLY。
3、如果childDimension == LayoutParams.WRAP_CONTENT,组件的测量模式为MeasureSpec.AT_MOST,大小为父视图提供的大小size,这里的大小实际上指的是组件的最大大小,组件的具体大小的测量还需要组件自己实现。
4、如果父视图的测量模式为MeasureSpec.AT_MOST,及时组件childDimension == LayoutParams.MATCH_PARENT,其测量模式也为MeasureSpec.AT_MOST。

组件如何根据测量模式测量出其大小的

child.measure(childWidthMeasureSpec, childHeightMeasureSpec)方法最终就会调用组件的onMeasure方法,在这里组件会根据测量规则计算出具体的宽高大小。

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

getDefaultSize方法就是计算出具体的宽高,setMeasuredDimension方法则是设置宽高。
基类View的具体计算方法是getDefaultSize:

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

无论是MeasureSpec.AT_MOST还是MeasureSpec.EXACTLY模式,最终得到的具体大小都为父视图提供大小,这与我们对wrap_content的理解是有出入的。其实具体的组件都需要重写onMeasure方法的。
例如TextView:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int width;
    int height;
    if (widthMode == MeasureSpec.EXACTLY) {
        // Parent has told us how big to be. So be it.
        width = widthSize;
    } else {
      ...这里计算textView的大小width....       
        if (widthMode == MeasureSpec.AT_MOST) {
            width = Math.min(widthSize, width);
        }
    }
...
setMeasuredDimension(width, height);
}

1、如果模式为MeasureSpec.EXACTLY,大小就是父视图提供的大小
2、如果模式是MeasureSpec.AT_MOST,会取textView的真实大小与与父视图提供的大小中小的那个,这样与我们理解的wrap_content就吻合了。

相关文章

  • 组件测量那些事

    简介 组件在页面上的展示要经过测量、布局、绘制三个过程,而测量就是把我们定义的组件宽和高转换为具体的大小的这个过程...

  • 组件的测量

    1、简介 组件在显示到界面上要经过三个过程测量、布局、绘制。测量就是计算组件的大小,布局就是摆放组件的位置,而绘制...

  • Android组件功耗测量

    原文:https://source.android.com/devices/tech/power/componen...

  • Vue组件命名的一些问题

    原文:聊聊 Vue 组件命名那些事 组件注册 我们以一个最简单的例子来研究 Vue 组件的注册过程: 通过跟踪代码...

  • vue-组件那些事

    组件注册 全局注册 之后就可以在任何地方使用HelloWorld组件了。 单文件组件注册 slot分发我们可以发现...

  • vue组件开发那些事

    使用vue开发感悟 刚开始开发vue的组件有些不太习惯,对vue templte的模板语法对比react渲染的内容...

  • React Native 测量组件高度

    方法一 :onLayout this.lay...

  • 关于Android组件化那些事

    背景 我司之前一直采用MVP+Dagger2+Retrofit+Rxjava的项目结构。这种结构对于我们这种只有几...

  • iOS组件化的那些事 - CTMediator

    转载自: 知乎-henry磊 iOS组件化的那些事 - CTMediator[https://zhuanlan.z...

  • RecyclerView源码学习笔记

    RecyclerView包含以下几个重要的组件:1.LayoutManager: 测量和布局子View2.Recy...

网友评论

      本文标题:组件测量那些事

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