1. 说明
下边我们看一个小示例,看下什么时候可以获取到mTextViewHeight高度,什么时候不能获取到。
2. 代码如下
public class MainActivity extends AppCompatActivity {
private TextView text_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 下边这个获取不到view的高度,因为参数3是null,即就是父布局是null,说明你还没有把activity_main添加到父布局中,所以不能获取到宽高
View view = View.inflate(this, R.layout.activity_main, null);
// 这个可以获取到宽高,因为 参数3ViewGroup表示父布局,下边代码就表示,你已经把activity_main布局添加到父布局中了,所以可以获取到宽高
// View view = View.inflate(this, R.layout.activity_main, ViewGroup);
text_view = (TextView) findViewById(R.id.text_view);
Log.e("TAG" , "height1 -> " + text_view.getMeasuredHeight()) ; // 0
text_view.post(new Runnable() {
@Override
public void run() {
Log.e("TAG" , "height2 -> " + text_view.getMeasuredHeight()) ; // 高度:51
}
}) ;
}
@Override
protected void onResume() {
super.onResume();
Log.e("TAG" , "height3 -> " + text_view.getMeasuredHeight()) ; // 0
}
}
View view = View.inflate(this, R.layout.activity_main, null)为什么获取不到高度?
参数3表示父布局,而这里的参数3是null,表示没有把activity_main添加到父布局中,所以不能获取到宽高;
View view = View.inflate(this, R.layout.activity_main, ViewGroup)为什么可以获取到高度?
参数3表示父布局,这里的参数3是 ViewGroup,表示父布局,这里为了形象表示就直接把父布局写成了ViewGroup,其实只要是父布局就行。这里就表示把activity_main添加到父布局中,所以可以获取到高度;
分析其余3个mTextViewHeight的高度:
由以上可知:
03-19 21:29:23.491 18696-18696/? E/TAG: height1 -> 0
03-19 21:29:23.492 18696-18696/? E/TAG: height3 -> 0
03-19 21:29:23.591 18696-18696/? E/TAG: height2 -> 51
height1 = 0;height3 = 0 ;height2 = 51(高度)
分析原因:
我们需要知道,我们在onCreate()方法中只是调用了setContentView(),也需要知道setContentView()到底干了什么?
在PhoneWindow中,setContentView只是new DecorView()
之所以能够拿到控件的宽高,是因为调用了onMeasure()方法,而在我们之前写的那些自定义View效果的时候,其实都是在 onMeasure()方法中获取到宽高后,都会重新调用setMeasuredDimension(width , height);
setContentView 只是创建DecorView,并且把我们的布局加载进DecorView,并没有调用onMeasure()方法;
分析PhoneWindow的源码如下:
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
只要installDecor()方法执行完,就会形成这样一个局面:
图片.png
onCreate()中为什么获取不到 mTextViewHeight 高度?
因为在PhoneWindow中,setContentView()只是new DecorView(),然后把我们的布局加载到了DecorView(),其余什么都没做,也并没有调用onMeasure()方法,所以在onCreate()方法中不能获取到TextView的宽高;
onResume()中为什么也获取不到 mTextViewHeight 高度?
这个其实就涉及到Activity的启动流程的分析,通过下边对Activity启动流程的分析,即就是分析 ActivityThread源码,可以知道:
Activity的启动流程是:
先调用handleLaunchActivity(),在这个方法中调用performLaunchActivity(),在performLaunchActivity()中会调用onCreate() ->
然后调用handleResumeActivity(),在这个方法中调用performResumeActivity() ->
然后调用Activity的onResume() ->
然后调用 wm.addView(decor , 1) ,这个时候才开始把我们的DecorView 加载到 WindowManager中,View的绘制流程在这个时候才刚刚开始,才开始onMeasure()(测量)、onLayout()(摆放)、onDraw()(绘制)draw()自己、draw()孩子;
所以说View的绘制流程是在onResume()方法之后才开始,所以说在onResume()方法中也是不能获取 mTextViewHeight高度的,必须要等调用完onResume()之后,才可以获取宽高的。
下边的text_view.post为什么可以获取到宽高?
text_view.post(new Runnable() {
@Override
public void run() {
Log.e("TAG" , "height2 -> " + text_view.getMeasuredHeight()) ; // 高度:51
}
}) ;
源码分析:
View中源码:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
View中源码:
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
View中源码:
/**
* @param info the {@link android.view.View.AttachInfo} to associated with
* this view
*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;
// We will need to evaluate the drawable state at least once.
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
if (mFloatingTreeObserver != null) {
info.mTreeObserver.merge(mFloatingTreeObserver);
mFloatingTreeObserver = null;
}
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
}
HandlerActionQueue源码:
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
这里只是把Runnable保存到Queue中,什么都没干,run()方法会在dispatchAttachedToWindow()方法会在测量完毕然后调用executeActions()方法,即就是onMeasure()方法之后调用executeActions()方法,所以只要一调用text_view.post(new Runnable()) ,就马上可以获取宽高。
网友评论