Android自定义View

何时可以获取mTextViewHeight高度

2018-03-26  本文已影响1人  世道无情

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()) ,就马上可以获取宽高。

上一篇 下一篇

猜你喜欢

热点阅读