我们在Activity的onCreate()、onResume()方法里面获取控件的宽高都为0,如下:
public class MainActivity extends BaseSkinActivity {
private static final String TAG = "MainActivity";
private Button mButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
mButton = findViewById(R.id.button_change);
Log.d(TAG, "onCreate>>>>>>" + "button的高度===" + mButton.getMeasuredHeight() + "----button的宽度" + mButton.getMeasuredWidth());
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume>>>>>>" + "button的高度===" + mButton.getMeasuredHeight() + "----button的宽度" + mButton.getMeasuredWidth());
}
}
onCreate>>>>>>button的高度===0----button的宽度0
onResume>>>>>>button的高度===0----button的宽度0
说明Activity在调用onResume方法时还没有测量控件宽高,在Activity的启动流程分析时,我们在ActivityThread的handleResumeActivity方法看到以下代码:
// ViewManager wm = a.getWindowManager(); a是Activity,在Activity的getWindowManager(),
if (r.activity.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
会不会是这个addView方法里面进行控件的测量呢??我们在Activity找到了wm的赋值地方
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//这里给mWindowManager赋值,转换为WindowManagerImpl对象
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
所以最终是在WindowManagerImpl里面调了addView方法。
//mGlobal 是在常量赋值的 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
然后我们再看WindowManagerGlobal的 addView方法
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
省略代码···············
ViewRootImpl root;
View panelParentView = null;
//ViewRootImpl是在Window的最顶层,DecorView的parent是ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//setView是将Decor加入到root里面,测量应该是在该方法里面
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
再进入ViewRootImpl的setView方法
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
省略代码···············
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
//看字面意思就知道是请求布局
requestLayout();
省略代码···············
}
再进入ViewRootImpl的requestLayout方法
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
再进入ViewRootImpl的scheduleTraversals方法
//在这个方法里面有一个postCallback 线程mTraversalRunnable,这个线程就是正式开始进入测量doTraversal方法。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
//mTraversalRunnable线程调用doTraversal方法
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//这个方法应该很熟悉了,我们大部分的View绘制流程文章都是从这个地方开始讲的。
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
看到performTraversals()就是进入onMeasure的入口了。