为什么在View.post()方法中可以获取View 的高度?

为什么在View.post()方法中可以获取View 的高度?

在平常的开发中,经常遇到要在onCreate()方法中获取某个View的高度或者是某个View获取到焦点,或者滑动到指定的位置,但是直接用view.getHeight()常常拿到的高度为0,当我们使用View.post()方法或者使用View.postDelay()即可以获得想要的结果

mTvResult.post(() -> {
            int measuredHeight = mTvResult.getMeasuredHeight();
            ELog.i(TAG, "onCreate: "+measuredHeight);
        });
 mTvResult.postDelayed(new Runnable() {
            @Override
            public void run() {
                int measuredHeight = mTvResult.getMeasuredHeight();
                ELog.i(TAG, "onCreate: "+measuredHeight);  
            }
        },1000);

下面分析原因

首先我们看看View.post()方法的源码

  /**
     * 

Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.

* * @param action The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * * @see #postDelayed * @see #removeCallbacks */ public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; //A set of information given to a view when it is attached to its parent window. //由于在onCreate方法中View还没有attached 此时attchInfo为null if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); //HandlerActionQueue Class used to enqueue pending work from Views when no Handler is attached. return true; }
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view;

import android.os.Handler;

import com.android.internal.util.GrowingArrayUtils;

/**
 * Class used to enqueue pending work from Views when no Handler is attached.
 *
 * @hide Exposed for test framework only.
 */
public class HandlerActionQueue {
    private HandlerAction[] mActions;
    private int mCount;

    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++;
        }
    }

    public void removeCallbacks(Runnable action) {
        synchronized (this) {
            final int count = mCount;
            int j = 0;

            final HandlerAction[] actions = mActions;
            for (int i = 0; i < count; i++) {
                if (actions[i].matches(action)) {
                    // Remove this action by overwriting it within
                    // this loop or nulling it out later.
                    continue;
                }

                if (j != i) {
                    // At least one previous entry was removed, so
                    // this one needs to move to the "new" list.
                    actions[j] = actions[i];
                }

                j++;
            }

            // The "new" list only has j entries.
            mCount = j;

            // Null out any remaining entries.
            for (; j < count; j++) {
                actions[j] = null;
            }
        }
    }

    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;
        }
    }

    public int size() {
        return mCount;
    }

    public Runnable getRunnable(int index) {
        if (index >= mCount) {
            throw new IndexOutOfBoundsException();
        }
        return mActions[index].action;
    }

    public long getDelay(int index) {
        if (index >= mCount) {
            throw new IndexOutOfBoundsException();
        }
        return mActions[index].delay;
    }

    private static class HandlerAction {
        final Runnable action;
        final long delay;

        public HandlerAction(Runnable action, long delay) {
            this.action = action;
            this.delay = delay;
        }

        public boolean matches(Runnable otherAction) {
            return otherAction == null && action == null
                    || action != null && action.equals(otherAction);
        }
    }
}

private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        //注:该方法有800行代码,已省略
        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        getRunQueue().executeActions(attachInfo.mHandler);

    ...
    performMeasure();//从DecorView开始完成View树的测量
    ...
    performLayout();//从DecorView开始完成View树的布局
    ...
    performDraw();//从DecorView开始绘制View树
 }

结论:view.post()方法在整个view树的performMeasure, performLayout, performDraw执行完后,才被主线程轮询到,才得到执行。
getRunQueue().executeActions(attachInfo.mHandler)里面会遍历数组然后把所有的view.post(Runnable)里的Runnable都用和主线程Looper关联的Hanlder#sendMessage出去,放到主线程Looper里轮询,等待调用。那么由于当前的performTraversals()本身就是由主线程Looper回调给刚才的Choreographer里面去执行的,因此主线程一定会等待performTraversals()整个方法执行完,才去接着执行由view.post()推送到主线程的Runnable。因此整个View树都完成了测量,布局,绘制。然后view.post()里面百分百的可以拿到view的宽高了。

你可能感兴趣的:(为什么在View.post()方法中可以获取View 的高度?)