浅析Android View的Measure过程
2015-11-07 16:47
489 查看
我们知道,Android的View从创建到展示给用户的过程包含了三个阶段:measure阶段、layout阶段、和draw阶段,即测量、布局、绘制。而measure则是其中之首,它测量并确定了view的宽高。当我们自定义的View的时候应该怎么样处理measure过程,重写measure过程的哪些方法呢?带着这些疑问,现简单总结下View的measure过程。
measure过程较为复杂,但核心方法只有两个:onMeasure()和measure() 方法,其中measure()方法是final的,不可重写,它调用了onMeasure()方法,在自定义View的时候我们可以重写onMeasure()方法。可以看到,这是典型的模板方法模式。
onMeasure() 方法:
因此,当我们在自定义布局重构measure过程时,只需要在onMeasure()方法中调用measureChildren(),就可以递归调用子类的measure过程了。
总结如下:
1、measure过程是View绘制的三大流程之首,优先于layout过程和draw过程;
measure过程较为复杂,但核心方法只有两个:onMeasure()和measure() 方法,其中measure()方法是final的,不可重写,它调用了onMeasure()方法,在自定义View的时候我们可以重写onMeasure()方法。可以看到,这是典型的模板方法模式。
onMeasure() 方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }和measure() 方法:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int oWidth = insets.left + insets.right; int oHeight = insets.top + insets.bottom; widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); } ..... onMeasure(widthMeasureSpec, heightMeasureSpec); }
我们可以看到,measure()方法是final的,这意味着子类不能够重写这个方法,measure()是整个measure过程的核心,它测量了该view的宽高,并将宽高存放在mMeasuredwidth和mMeasuredHeight中(注:此时View中的mHeight和mWidth都未初始化,都还是0),有兴趣的朋友可以阅读源码来具体看看mMeasureWidth和mMeasuredHeight是如何初始化。此外,measure()方法内部还会调用onMeasure()方法,我们可以重写onMeasure()方法来进行其它测量操作,最常见的就是对子View进行测量。一般来说,在ViewGroup中,不仅要对自己的大小进行测量,还要依次调用各个子View的measure过程来对子View进行测量,因此我们在自定义ViewGroup时就需要重写onMeasure方法来对子类进行Measure。事实上,ViewGroup基类并没有重写onMeasure方法,只是提供了measureChildren方法来调用每个子view的measure():
ViewGroup.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); } } }
measureChildren则会调用measureChild,measureChild方法则十分简单,只是获取了子view的layoutParam,调用了子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); }
因此,当我们在自定义布局重构measure过程时,只需要在onMeasure()方法中调用measureChildren(),就可以递归调用子类的measure过程了。
总结如下:
1、measure过程是View绘制的三大流程之首,优先于layout过程和draw过程;
2、measure过程的核心是measure()方法,measure()方法对自己的宽高进行测量,初始化mMeasuredWidth和mMeasuredHeight,并调用onMeasure方法,此时layout过程还未调用,与layout过程有关的参数都未初始化;
3、重写ViewGroup的onMeasure()方法时,在计算自己的大小同时,还要调用子View的measure,这一步骤被封装成了measureChildern(),我们可以方便地调用;
相关文章推荐
- Android动画学习(一)——Android动画系统框架简介
- DiskLruCache详解与应用
- Android ActionBar的基本用法
- Fragment的展现与Activity状态丢失 IllegalStateException:Can not perform this action after onSaveInstanceState
- AndroidManifest 中original-package标签
- android svg
- Android 之 Binder与进程间通信
- 编译和测试android的驱动程序学习笔记
- 简易易懂的android回调的实现
- Android之Socket通信、List加载更多、Spinner下拉列表
- Xposed (一) Android hook框架入门
- Intellij IDEA配置Android Annotations注解框架
- 使用ContentObserver实现短信提醒功能
- Android如何防止apk程序被反编译
- Android APK反编译就这么简单 详解(附图)
- Android过度绘制深度优化---View提前绘制
- Android笔记(五十三) 利用有道OPENAPI做简单的翻译demo
- Android adb devices显示no permission
- 显示和隐藏动作栏(Action Bar)
- Android开发中用于替代Enum的@IntDef的使用