Android View的测量

  • 时间:2021-03-20 00:43 作者:Good包籽 来源: 阅读:629
  • 扫一扫,手机访问
摘要:Android 的View是一个树结构,而View的大小,ViewGroup的大小,会相互影响,其中有两个重要参数1.LayoutParams布局参数1.理解LayoutParamsLayoutParams就是布局参数,子View通过LayoutParams告诉父容器(ViewGroup)应该如何放
  • Android 的View是一个树结构,而View的大小,ViewGroup的大小,会相互影响,其中有两个重要参数

1.LayoutParams布局参数

1.理解LayoutParams

  • LayoutParams就是布局参数,子View通过LayoutParams告诉父容器(ViewGroup)应该如何放置自己。
  • 每个ViewGroup的子类都有自己对应的LayoutParams类,典型的如LinearLayout.LayoutParams和
    FrameLayout.LayoutParams等,可以看出来LayoutParams都是对应ViewGroup子类的内部类。
  • MarginLayoutParams是和外间距有关的。和LayoutParams相比,MarginLayoutParams只是添加了对上下左右外间距的支持。实际上大部分LayoutParams的实现类都是继承自MarginLayoutParams,由于基本所有的父容器都是支持子View设置外间距的。


    image.png

2.优先级问题

  • 属性优先级问题 MarginLayoutParams主要就是添加了上下左右4种外间距。在构造方法中,先是获取了margin属性;假如该值不合法,就获取horizontalMargin;假如该值不合法,再去获取leftMargin和rightMargin属性(verticalMargin、topMargin和bottomMargin同理)。我们可以据此总结出这几种属性的优先级
  • 优先级 Margin > HorizontalMargin和VerticalMargin > LeftMargin和RightMargin、TopMargin和BottomMargin

3.LayoutParams参数

  • 在XML中定义View
    在Java代码中直接生成View对应的实例对象
  • 3.1.假如xml布局里面是具体的dp,那么layoutParams的值就是大于0。
  • 3.2.假如是MATCH_PARENT
    public static final int MATCH_PARENT = -1;
  • 3.3.假如是WRAP_CONTENT
    public static final int WRAP_CONTENT = -2;


    LayoutParams.png

4.addView

/** * 重载方法1:增加一个子View * 假如这个子View还没有LayoutParams,就为子View设置当前ViewGroup默认的LayoutParams * * @param child */@Overridepublic void addView(View child) {    addView(child, -1);}  /** * 重载方法2:在指定位置增加一个子View * 假如这个子View还没有LayoutParams,就为子View设置当前ViewGroup默认的LayoutParams * * @param child * @param index View将在ViewGroup中被增加的位置(-1代表增加到末尾) */@Overridepublic void addView(View child, int index) {    if (child == null) {        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");    }    LayoutParams params = child.getLayoutParams();    if (params == null) {        params = generateDefaultLayoutParams();        if (params == null) {            throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");        }    }    addView(child, index, params);}  /** * 重载方法3:增加一个子View * 使用当前ViewGroup默认的LayoutParams * * @param child * @param width  传入参数作为LayoutParams的width * @param height 传入参数作为LayoutParams的height */@Overridepublic void addView(View child, int width, int height) {    final LayoutParams params = generateDefaultLayoutParams();    params.width = width;    params.height = height;    addView(child, -1, params);}  /** * 重载方法4:增加一个子View * * @param child * @param params 使用传入的LayoutParams */@Overridepublic void addView(View child, LayoutParams params) {    addView(child, -1, params);}  /** * 重载方法5:增加一个子View, * * @param child * @param index  View将在ViewGroup中被增加的位置 * @param params 使用传入的LayoutParams */@Overridepublic void addView(View child, int index, LayoutParams params) {    super.addView(child, index, params);}

5.generateDefaultLayoutParams方法

  • addView源码往下找,就能找到generateDefaultLayoutParams方法,获取默认的LayoutParams
  • ViewGroup 的 generateDefaultLayoutParams方法
protected LayoutParams generateDefaultLayoutParams() {    return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);}

6.checkLayoutParams方法

  • addView源码一直往下找,就能找到checkLayoutParams方法
  • ViewGroup 的 checkLayoutParams方法
 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {        return  p != null;   }
  • 非ViewGroup 的 checkLayoutParams方法,具体源码去看看
@Overrideprotected boolean checkLayoutParams(ViewGroup.LayoutParams p) {       return p instanceof LinearLayout.LayoutParams;}

2.MeasureSpec

1.测量View大小(onMeasure)

  • View的大小不仅由自身所决定,同时也会受到父控件的影响,自身大小可能还会受到子view的影响,而且会屡次调用onMeasure方法。

2.MeasureSpec参数

  • MeasureSpec是一个32位的int值,高2位存的是测量模式,低30位存的是具体测量大小,手机像素点几乎不可能超过这个值。
  • 源码我们能看到定义


    MeasureSpec.png

3.获取参数

@Overrideprotected void onMeasure(intwidthMeasureSpec,intheightMeasureSpec){      //取出宽度确实切数值      int widthsizeMeasureSpec.getSize(widthMeasureSpec);      //取出宽度的测量模式      int widthmodeMeasureSpec.getMode(widthMeasureSpec);      //取出高度确实切数值      int heightsizeMeasureSpec.getSize(heightMeasureSpec);      //取出高度的测量模式      int heightmodeMeasureSpec.getMode(heightMeasureSpec);}

4.三种测量模式

  • 测量模式一共有三种
模式二进制的值形容
UNSPECIFIED00默认值,父控件没有给子view任何限制,子View可以设置为任意大小。
EXACTLY01表示父控件已经确切的指定了子View的大小。
AT_MOST10表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小。

5.获取子View的MeasureSpec(getChildMeasureSpec)

  • ViewGroup要测量时,假如受到子view影响,就要先测量子view,getChildMeasureSpec方法,度量子view。
  • 子view的测量,需要父布局VIewGroup的MeasureSpec,加上自己的LayoutParams布局参数来测量。
  • 假如对View的宽高进行修改了,不要调用 super.onMeasure( widthMeasureSpec, heightMeasureSpec);
    要调用 setMeasuredDimension( widthsize, heightsize); 这个函数。
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) {            //子view的布局参数也是确定大小,那子view模式就是准确模式            resultSize = childDimension;            resultMode = MeasureSpec.EXACTLY;        } else if (childDimension == LayoutParams.MATCH_PARENT) {            // Child wants to be our size. So be it.            //子view是MATCH_PARENT,那也是准确模式,父布局多大,子view就多大            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.            //子view是WRAP_CONTENT,那最大不能超过父布局的大小,就为AT_MOST            resultSize = size;            resultMode = MeasureSpec.AT_MOST;        }        break;   。   。     。    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}
  • Android 开发艺术这书里面这张图大家应该都看过


    image.png

6.确定View大小(onSizeChanged)

  • 这个函数在视图大小发生改变时调用。
  • 由于View的大小不仅由View本身控制,而且受父控件的影响,所以我们在确定View大小的时候最好使用系统提供的onSizeChanged回调函数。

7.获取测量后的大小(getMeasuredWidth)

  • 在measure()过程结束后即可以获取到对应的值;
  • 通过setMeasuredDimension()方法来进行设置的.

8.获取宽高(getWidth)

  • 在layout()过程结束后才能获取到;
  • 通过视图右边的坐标减去左边的坐标计算出来的

3.具体使用

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    //父布局的宽高    int viewGroupWidth = MeasureSpec.getSize(widthMeasureSpec);    int viewGroupHeight = MeasureSpec.getSize(heightMeasureSpec);    //遍历所有子View    for (int i = 0; i < getChildCount(); i++) {        View childView = getChildAt(i);        //假如view可见        if (childView.getVisibility() == View.VISIBLE) {            //子view的布局参数            LayoutParams childLayoutParams = childView.getLayoutParams();            //子view的measureSpec,传入父布局的measureSpec,还有父布局设置的内边距,子view的大小(大于0就是确定的大小,-1是match_parent,-2是wrap_content)            int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, leftPadding + rightPadding, childLayoutParams.width);            int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, topPadding + bottomPadding, childLayoutParams.height);            //子view调用了measure才能得出确切的宽高            childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);            //子View的宽高            int childMeasureWidth = childView.getMeasuredWidth();            int childMeasureHeight = childView.getMeasuredHeight();               }    //再测量ViewGroup    int widthMode = MeasureSpec.getMode(widthMeasureSpec);    int heightMode = MeasureSpec.getMode(heightMeasureSpec);    int realWidth = widthMode == MeasureSpec.EXACTLY ? viewGroupWidth : viewGroupNeedWidth;    int realHeight = heightMode == MeasureSpec.EXACTLY ? viewGroupHeight : viewGroupNeedHeight;    setMeasuredDimension(realWidth, realHeight);}

4.参考资料

  • 《Android开发艺术探究》
  • 安卓自己设置View进阶
  • 全部评论(0)
最新发布的资讯信息
【系统环境|】2FA验证器 验证码如何登录(2024-04-01 20:18)
【系统环境|】怎么做才能建设好外贸网站?(2023-12-20 10:05)
【系统环境|数据库】 潮玩宇宙游戏道具收集方法(2023-12-12 16:13)
【系统环境|】遥遥领先!青否数字人直播系统5.0发布,支持真人接管实时驱动!(2023-10-12 17:31)
【系统环境|服务器应用】克隆自己的数字人形象需要几步?(2023-09-20 17:13)
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
【系统环境|】ZORRO佐罗软件安装教程及一键新机使用方法详细简介(2023-02-10 21:56)
【系统环境|】阿里云 centos 云盘扩容命令(2023-01-10 16:35)
【系统环境|】补单系统搭建补单源码搭建(2022-05-18 11:35)
【系统环境|服务器应用】高端显卡再度登上热搜,竟然是因为“断崖式”的降价(2022-04-12 19:47)
手机二维码手机访问领取大礼包
返回顶部