his);
mAttachInfo = info;
mWindowAttachCount++;
onAttachedToWindow();
final CopyOnWriteArrayList
listeners =
mOnAttachStateChangeListeners;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
}
}
在这里onAttachedToWindow() 的注释带来了一个问题:仅保证会在onDraw 前调用,而不保证在onMeasure 之前或者之后调用 onAttachedToWindow。
/**
* This is called when the view is attached to a window. At this point it
* has a Surface and will start drawing. Note that this function is
* guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
* however it may be called any time before the first onDraw -- including
* before or after {@link #onMeasure(int, int)}.
*
* @see #onDetachedFromWindow()
*/
protected void onAttachedToWindow() {
protected void onAttachedToWindow() {
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
resolveLayoutDirectionIfNeeded();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
}
}
3 fitSystemWindows(), 如果是系统window 则使用padding的值计算一下insets,并开始android.view.View.requestLayout()
protected boolean fitSystemWindows(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
mPaddingLeft = insets.left;
mPaddingTop = insets.top;
mPaddingRight = insets.right;
mPaddingBottom = insets.bottom;
requestLayout();
return true;
}
return false;
}
在android.view.View.requestLayout()中,就是简单的一层一层向上检查parent是否存在,若存在调用parent的requestLayout();
/**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
* tree.
*/
public void requestLayout() {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
}
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
if (mParent != null) {
if (mLayoutParams != null) {
mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
}
if (!mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
}但是每层调用完成,并不是立即执行layout操作,而是通过赋值标志位mPrivateFlags |= FORCE_LAYOUT;,来标识一下而已。真正的layout过程在后面。
4. 有一段代码要说一下。
if (mLayoutRequested && !mStopped) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);RunQueue 是在handler 没有初始化的时候用来处理事件的消息队列。 把给ViewRootImpl post的事件 类型是runnable , 等到handler 构造好后,再发给handler 处理。不是本文的重点,就简单提一下。
5 接下来是measure 的部分
boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetr