提示:本文的源码均取自Android 7.0(API 24)
前言
自定义View是Android进阶路线上必须攻克的难题,而在这之前就应该先对View的工作原理有一个系统的理解。本系列将分为4篇博客进行讲解,本文主要对View的测量流程进行讲解。相关内容如下:
从View的角度看measure流程
在上一篇文章讲到整个视图树(ViewTree)的根容器是DecorView,ViewRootImpl通过调用DecorView的measure方法开启测量流程。measure是定义在View中的方法,我们就先从View的角度来看看测量过程中发生了什么。
首先来看一下measure
方法中的逻辑,关键代码如下:
/**
* This is called to find out how big a view should be. The parent
* supplies constraint information in the width and height parameters.
*
* 这个方法将调用onMeasure方法完成真正的测量工作
* 因此View的派生类只需要也只能重写onMeasure方法完成布局逻辑
*/
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);
}
// Suppress sign extension for the low bytes
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
|| heightMeasureSpec != mOldHeightMeasureSpec;
final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
// ① 判断是否需要执行测量过程
if (forceLayout || needsLayout) {
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ?