View的工作流程---Measure过程
来源:互联网 发布:淘宝招聘官网首页 编辑:程序博客网 时间:2024/05/01 19:18
measure过程:
measure过程要分情况来看,如果只是一个原始的View,通过measure方法就可以完成测量过程,如果是ViewGroup,除了完成自己的测量过程外,还要遍历去调用所有子元素的measure方法,各个子元素在递归去执行这个流程。
ViewGroup的measure过程
对于ViewGroup除了完成自身的measure过程,还要遍历去调用子元素measure方法,各个子元素在递归执行这个过程。与View不同的是,ViewGroup是一个抽象类,因此它没有重写View的onMeasure方法,但是它提供了一个叫measureChildren的方法:
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec){> final int size = mChildrenCount;> for(int i = 0 ; i < size ; i ++){> final View child = children[i];> if((child.mViewFlags & VISIBILITY_MASK) != GONE){> measureChild (child,widthMeasureSpec,heightMeasureSpec);> }> }}
ViewGroup在measure时,会对每一个子元素进行measure,measureChild方法如下:
protected void measureChild(View child,int parentWidthMeasureSpec,int parentHeightMeasureSpec){ final LayoutParams layoutParams = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidth-MeasureSpec,mPaddingLeft + mPaddingRight , layoutParams.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}
measureChild就是取出子元素的LayoutParams,再通过getChildMeasrueSpec创建子元素的MeasureSpec(子元素的MeasureSpec的创建与父容器的MeasureSpec和子元素本身的LayoutParams有关此外还和View的margin及padding有关。具体可查看ViewGroup的getChildMeasureSpec方法),接着将MeasureSpec直接传递给View的measure方法进行测量。
getChildMeasureSpec的工作过程:
public static int getChildMeasureSpec(int spec , int padding ,int child-Dimension){ 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 imprsed 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 out size . So be it . resultSize = size; resultMode = MeasureSpec.EXACTLY; }else if (childDimension == LayoutParams.WARP_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){ resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; }else if (childDimension == LayoutParams.MATCH_PARENT){ //Child wants to be out size ,but our size is not fixed. //Constrain child to mot be biger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; }else if (childDimension == LayoutParams.WARP_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: if(childDimension >=0){ //Child wants a specific size ... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; }else if (childDimension == LayoutParams.MATCH_PARENT){ //Child wants to be out size ,find out how big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; }else if (childDimension == LayoutParams.WARP_CONTENT){ //Child wants to determine its own size. find out how //big it should be . resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}
上述方法的作用就是根据父容器的MeasureSpec同时结合View本身的LayoutParams来确定子元素的MeasureSpec,参数中padding值父容器中已占用的空间大小,因此子元素可用的大小为父容器的尺寸减去padding,上代码:
int specSize = MeasureSpec.getSize(spec);int szie = Math.max(0,specSize - padding);
对于普通View,它的MeasureSpec有父容器的MeasureSpec 和自身LayoutParams来共同决定,针对不同的父容器和View本身不同的LayoutParams,View就可以有多种MeasureSpec.
当View采用固定宽/高时,不管父容器的MeasureSpec是什么,View的MeasureSpec都是精确模式并且其大小遵循LayoutParams中的大小。当View的宽/高是match_parent时,如果父容器的模式是精准模式,那么View也是精准模式并且其大小不会超过父容器的剩余空间。当View的宽/高是wrap_content时,不管父容器的模式是精准还是最大化,View的模式总是最大化并且大小不能超过父容器的剩余空间。
getChildMeasureSpec的工作过程结束
回到ViewGroup的Measure:
ViewGroup并没有定义其测量具体过程,因为ViewGroup是一个抽象类,其测量过程的onMeasure方法需要各个子类去具体实现,比如LinearLayout、RelativeLayout、等,为什么ViewGroup不像View一样对其onMeasure方法作统一实现呢?
因为不同的ViewGroup子类有不同的布局特性,导致它们的测量细节各不相同,因此无法统一实现。接下来通过LinearLayout的onMeasure方法来分析ViewGroup的measure过程
LinearLayout的onMeasure方法如下:
protected void onMeasure(int widthMeasureSpec , int heightMeasureSpec){ if(mOrientation == VERTICAL){ measureVertical(widthMeasureSpec, heightMeasureSpec); }esle{ measureHorizontal(widthMeasureSpec, heightMeasureSpec); }}
关于view.post(runnable)方法的用法之一
如果我们像在Activity已启动的时候就执行一个任务,这个任务需要获取某个View的宽/高,如果我们在Activity,onCreate()或onStart()或onResume()中去获取View的宽高,是无法获取到正确的宽/高信息的,因为View的measure的过程和Activity的生命周期方法不是同步的,所以就无法保证在Activity执行了onCreate()或onStart()或onResume()时某个View已经测量完成,如果View没有测量完,获取到的宽/高就为0。我们就可以通过post可以将一个runnable投递到消息队列的尾部,然后等待Looper调用此runnable的时候,View也已经初始化好了,典型代码:
protected void onStart(){ super.onStart(); view.post(new Runnable(){ @override public void run(){ int width = view.getMeasuredWidth(); int height = view.getMeasuredHeight(); } });}
关于上述的问题还有一种解决办法:
onWindowFocusChanded方法,这个方法会在View已经初始化完成,宽/高已经准备好了,这时获取宽/高是没有问题的。需要的注意的是,这个方法会被调用多次,当Activity的窗口得到焦点时均会被调用一次。具体来说,当Activity继续执行和暂停执行时,这个方法(onWindowFocusChanded)均会被调用,如果频繁进行onResume与onPause,那么这个方法也会被频繁调用,代码:
public void onWindowFocusChanged(boolean hasFocus){ super.onWindowFocusChanged(hasFocus); if(hasFocus){ int width = view.getMeasuredWidth(); int height = view.getMeasuredHeight(); }}
- View的工作流程---Measure过程
- View的工作原理之measure过程
- View的工作流程-measure、layout、draw三大流程
- View的Measure流程
- View的绘制流程(二)--------view的measure过程
- View的measure过程
- View 的 Measure 过程
- view的measure过程
- View的measure过程
- Android View的工作流程总结分析(二)-Measure
- view工作流程解析之measure测量
- 018.View的Measure过程
- View的Measure过程解析
- View的工作原理(二)--从measure说View的测量流程
- android view的讲解 之 view的工作流程(measure,layout,draw)(二)
- 【自定义view系列】View的measure过程
- View的工作流程(layout过程)
- View的工作流程-Layout过程
- 使用python进行图像处理
- Topological Sorting
- [LeetCode]475. Heaters
- 常见的动态规划问题分析与求解
- iframe的低边会留白的处理
- View的工作流程---Measure过程
- 3月23
- [code static]Go基础语法
- iOS原生OPENGL之贴图
- Android 自定义控件原理
- C语言中常用的几个内存申请函数
- 记录Android开发中使用HorizontalScrollView的坑
- 欢迎使用CSDN-markdown编辑器
- 3月24号