在写代码的时候,因为功能、需求等,需要知道一个控件的宽高,demo如下:
<TextView
android:id="@+id/text_view"
android:layout_width="50dp"
android:layout_height="50dp"/>
Activity中
TextView text_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_5);
text_view= (TextView) findViewById(R.id.text_view);
Log.e("onCreate==getMeasuredWidth",text_view.getMeasuredWidth()+"");
Log.e("onCreate==getWidth",text_view.getWidth()+"");
}
@Override
protected void onResume() {
super.onResume();
Log.e("onResume==getMeasuredWidth",text_view.getMeasuredWidth()+"");
Log.e("onResume==getWidth",text_view.getWidth()+"");
}
日志:
E/onCreate==getMeasuredWidth: 0
E/onCreate==getWidth: 0
E/onResume==getMeasuredWidth: 0
E/onResume==getWidth: 0
说明了,在界面上看到布局的时候,还拿不到view的宽高。接下来,(在onCreate中)添加个方法监听
text_view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Log.e("getMeasuredWidth",text_view.getMeasuredWidth()+"");
Log.e("getWidth",text_view.getWidth()+"");
}
});
日志
E/onCreate==getMeasuredWidth: 0
E/onCreate==getWidth: 0
E/onResume==getMeasuredWidth: 0
E/onResume==getWidth: 0
E/getMeasuredWidth: 150
E/getWidth: 150
说明了,绘制(加载)完才能拿到控件的宽高。而在onResume中,控件还没有绘制(加载)。
那么,view是什么时候开始绘制的?
在说明view的绘制之前,我想先说一下,onCreate的流程。算是扩展的知识点吧
在之前的handler源码分析中,我提到了ActivityThread,里面有个handler处理,会处理Activity的各种状态,接下来,去那里的源码看看。这里,我只拿出来onCreate和onResume相关的
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RESUME_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
SomeArgs args = (SomeArgs) msg.obj;
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,args.argi3, "RESUME_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
核心的入口有了,一步步来看。
首先,去看下:handleLaunchActivity(r, null, “LAUNCH_ACTIVITY”);
因为要看的是onCreate相关的,在这个方法下,找onCreate关键字即可,很容易可以找到
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
两种方法,源码对应的是:
/**
* Perform calling of an activity's {@link Activity#onCreate}
* method. The default implementation simply calls through to that method.
* @param activity The activity being created.
* @param icicle The previously frozen state (or null) to pass through to
* @param persistentState The previously persisted state (or null)
*/
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
/**
* Perform calling of an activity's {@link Activity#onCreate}
* method. The default implementation simply calls through to that method.
*
* @param activity The activity being created.
* @param icicle The previously frozen state (or null) to pass through to onCreate().
*/
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
这里,调用了Activity源码下的方法performCreate,去那里看看
final void performCreate(Bundle icicle) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
看着这2个方法,有亲切的感觉了,在Activity下,看到了onCreate(两种参数)
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
}
public void onCreate(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
onCreate(savedInstanceState);
}
现在终于知道了,为什么我们创建了MyActivity,构造方法就是:
import android.app.Activity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
public class MainActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
}
}
好,现在,onCreate相关的,说完了,我们再去看onResume,还是回到ActivityThread下的handler,找到
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
args.argi3, "RESUME_ACTIVITY");
去看它的源码:(我这里只拿出来最关心的一部分代码)
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
......
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
......
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
......
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
......
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
从这段代码,我们就能明确知道,是先处理onResume的事:performResumeActivity,然后才去处理view的事:addView
关于performResumeActivity中的事,我就简单说了,也是和上面的create一样,一步一步走,就能看到
r.activity.performResume();
Activity 下 performResume()
performResume 中 mInstrumentation.callActivityOnResume(this);
去到 Instrumentation 下 public void callActivityOnResume(Activity activity)
callActivityOnResume 中 activity.onResume();
Activity中:
@CallSuper
protected void onResume() {
if (DEBUG_LIFECYCLE)
Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
mCalled = true;
}
至此,以demo为例的话,demo中的,onResume中的代码执行完了。就是去获取view的宽度,然后日志打印,但是,没有走到 addView方法。所以,获取到宽度为0,也是正常的了。
现在,去看看 wm.addView(decor, l);这句话。
如果我们直接跳进去看,会发现:
package android.view;
/** Interface to let you add and remove child views to an Activity. To get an instance
* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*/
public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
是个接口,相关处理呢?
既然直接进去看不到,就看看wm.addView中的wm是怎么来的。
上面的handleResumeActivity源码中有这么两句话:
final Activity a = r.activity;
ViewManager wm = a.getWindowManager();
好,继续看,在Activity源码中,是
public WindowManager getWindowManager() {
return mWindowManager;
}
这是获取,那么,mWindowManager是什么时候被创建或者产生的呢?继续找,终于,在Activity的attach方法中,找到两句话句话:
(Window mWindow)
mWindow = new PhoneWindow(this, window);
mWindowManager = mWindow.getWindowManager();
然后,在Window的源码中找到
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
现在,我们找到了WindowManager的实现:WindowManagerImpl
从流程看过来,所谓的wm.addView(decor, l);,其实调用的是WindowManagerImpl中的addView方法。
WindowManagerImpl 中:
@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) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null&& (context.getApplicationInfo().flags& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
......
root = new ViewRootImpl(view.getContext(), display);
......
// do this last because it fires off messages to start doing things
//最后做这个,因为它会触发消息来开始做事情
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
关注ViewRootImpl的setView方法,进去看,会发现一句关键性的代码
requestLayout();
ViewRootImpl 中:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
看scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
其中,有TraversalRunnable mTraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
doTraversal()方法中,有这么一句
performTraversals();
这个方法中的代码很多,经过海选,最后找到3句我最关心的话:
......
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
这就是view的测量了。
那么,测量完了,在哪里布局呢(即,layout方法在哪里)?在哪里绘制呢(即:draw方法在哪里调用)?
答:还是在performTraversals();方法中。可以找到:(我这里忽略参数)
performLayout()
performDraw();
至此,view的测量、摆放、绘制。都有了。但是要注意,这些的起点,都是addView,而这个方法,是在onResume执行之后的,所以,onResume中,获取不到控件的宽高。