源码剖判

日期:2019-09-07编辑作者:编程应用

一部分时候,我们必要想赢得LinearLayout宽高

1. 说明


  1. 获取LinearLayout宽高
那节课来看下View的绘图流程,大家由上边的套路来一步一步引出并深入分析View的绘图流程 —— 依据一个小示例,怎样能博得mTextViewHeight中度,来引出setContentView到底做了什么样、Activity的起步流程、最终引出View的绘图流程(即正是深入分析onMeasure()、onLayout()、onDraw());

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
    }
}
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout ll = (LinearLayout) findViewById(R.id.layInfo); Log.i("w", ll.getWidth()+"L"+ll.getHeight;}

View view = View.inflate(this, R.layout.activity_main, null)为何获得不到中度?

参数3表示父布局,而那边的参数3是null,表示未有把activity_main增加到父布局中,所以无法获取到宽高;

你会发掘打印出来是0那是因为在onCreate方法的时候LinearLayout还并不曾绘制实现,所以获得的惊人均为0,大概试着把这段代码放到onResume()方法中去,照旧是0。假若大家用取得LinearLayout的宽高能够由此电火花计时器不断的监听LinearLayout的宽高,等绘制完毕后,关闭电磁照望计时器就可以。

View view = View.inflate(this, R.layout.activity_main, ViewGroup)为啥可以取获得高度?

参数3表示父布局,这里的参数3是 ViewGroup,表示父布局,这里为了形象代表就径直把父布局写成了ViewGroup,其实只假设父布局就行。这里就表示把activity_main增多到父布局中,所以可以得到到中度;

final Handler handler= new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what == 1) { if(ll.getWidth { Log.i("w", ll.getWidth()+"L"+ll.getHeight; timer.cancel(); } } } }; timer = new Timer(); TimerTask task = new TimerTask(){ public void run() { Message message = new Message(); message.what = 1; myHandler.sendMessage; } }; timer.schedule(task,10,1000); }

浅析任何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()方法施行完,就能够产生这样三个圈圈:

图片 1

图片.png

恍如,要是想在Activity运行后随即弹出PopupWindow,大家清楚,在Activity的onCreate()方法中一贯写弹出PopupWindow方法会报错,因为activity未有完全运营是不能够弹出PopupWindow。大家得以尝尝用三种方法达成:

onCreate()中为啥得到不到 mTextViewHeight 中度?

因为在PhoneWindow中,setContentView()只是new DecorView(),然后把大家的布局加载到了DecorView(),别的什么都没做,也并从未调用onMeasure()方法,所以在onCreate()方法中不能够博取到TextView的宽高;


  1. 用onWindowFocusChanged方法
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()) ,就立时能够拿走宽高。

@Overridepublic void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged; showPopupWindow();}

3:Activity的开发银行流程?

这是ActivityThread源码

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        if (localLOGV) Slog.v(
            TAG, "Handling launch of " + r);

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        } else {
        }
    }

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
            return;
        }

        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
            }
    }

分析ActivityThread源码可知:
先调用handleLaunchActivity(),在那一个措施中调用performLaunchActivity(),在performLaunchActivity()中会调用onCreate() ->
下一场调用handleResumeActivity(),在这几个艺术中调用performResumeActivity() ->
下一场调用Activity的onResume() ->
然后调用 wm.addView(decor , 1) ,那个时候才起来把我们的DecorView 加载到 WindowManager中,View的绘图流程在今年才刚刚最早,也等于说在今年才起来onMeasure()(衡量)、onLayout()(摆放)、onDraw()(绘制)draw()自身、draw()孩子;

故而说View的绘图流程是在 onResume()之后才起来,如若我们以往想要获取控件的宽高的话,就非得等调用完onResume()之后,再去获得宽高就足以。

  1. 用Handler和Runnable,延时

自定义View的入口便是ViewRootImpl中的requestLayout()方法,所以先来看下ViewRootImpl的关系,如下图所示:

图片 2

ViewRootImpl包裹着DecorView.png

在WindowManagerImpl源码中:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

        ViewRootImpl root;
        View panelParentView = null;

            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 {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            }
            throw e;
        }
    }

剖析以上源码可见:
wm.addView(decor , 1) ->
调用WindowManagerImpl.addView() ->
接下来调用root.setView(view, wparams, panelParentView)方法 ->
调用requestLayout() -> 调用scheduleTraversals() ->
调用doTraversal() -> performTraversals() (英特网的篇章都以从这么些方法初始上课的)

4. 起首View的绘图流程

@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mHandler.postDelayed(mRunnable, 1000);}private Runnable mRunnable = new Runnable() { public void run() { showPopupWindow(); }};

1>:onMeasure()源码剖析:

ViewRootImpl源码如下:

    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;

                mAttachInfo.mDisplayState = mDisplay.getState();
                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);

                // 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();
            }
        }
    }

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            performTraversals();
        }
    }

private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;

        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals");
            host.debug();
        }

                    if (measureAgain) {
                        if (DEBUG_LAYOUT) Log.v(mTag,
                                "And hey let's measure once more: width=" + width
                                + " height=" + height);
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    }
            }
        } else 
        if (didLayout) {
            performLayout(lp, mWidth, mHeight);
            }

            performDraw();
        } else {

        }

        mIsInTraversal = false;
    }

LinearLayout的onMeasure()源码:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

LinearLayout的

void measureChildBeforeLayout(View child, int childIndex,
            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
            int totalHeight) {
        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
                heightMeasureSpec, totalHeight);
    }

 protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

由上边大家分析到 WindowManagerImpl的performTraversals()方法,今年就正式开首了View的绘图流程;

第二个调用performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 那几个主意用于给控件钦赐宽高 ->

调用mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ->

调用onMeasure(widthMeasureSpec, heightMeasureSpec); 那一年衡量正式启幕 ->
调用父布局,即就是LinearLayout.onMeasure()方法(因为这里是以LinearLayout包裹了3个TextView为例,当然你用RelativeLayout包裹,就调用RelativeLayout.onMeasure()衡量方法也是能够的) ->

调用 LinearLayout.onMeasure()中的measureVertical(widthMeasureSpec, heightMeasureSpec) (这个是activity_main文件中最外层根布局中的LinearLayout) ->

measureChildWithMargins() ->

调用child.measure(childWidthMeasureSpec, childHeightMeasureSpec); (这一个是最外层根布局中的子孩子的LinearLayout,纵然有八个LinearLayout的子孩子,那么就能够直接调用那一个办法) ->

调用TextView的onMeasure()(这一个便是子LinearLayout包裹的子孩子TextView)

像这种类型获取LinearLayout宽高难题就缓和了。

在上方涉及到2个衡量形式

childWidthMeasureSpec, childHeightMeasureSpec

protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);

由上述源码可见:

childWidthMeasureSpec, childHeightMeasureSpec那三个度量格局是经过getChildMeasureSpec()方法去总括的,具体育项目检查评定算是:

在getChildMeasureSpec()中,先获得本人的衡量格局和大小(即正是父布局),判定自身的衡量格局是match_parent也许是贰个恒定的值,然后回到决断子孩子的衡量形式和分寸,具体决断方法如下:

假如协和衡量形式(即就是父布局)是 EXACTLY,并且子孩子的深浅是match_parent,就给子孩子的衡量情势EXACTLY;
设若本身度量方式(即就是父布局)是 EXACTLY,何况子孩子的大小是wrap_content,就给子孩子的度量情势是 AT_MOST;
一经本身衡量方式(即正是父布局)是 AT_MOST,即便子孩子大小是match_parent,就给子孩子的度量方式 AT_MOST;
万一协和衡量形式(即就是父布局)是 AT_MOST,况且子孩子的高低是wrap_content,就给子孩子的度量格局 AT_MOST;

在结尾会把收获到的度量格局和尺寸,即正是resultSize, resultMode重返回去
即就是再次来到到了measureChildWithMargins()方法中,如下图所示:

图片 3

图片.png

回到那一个衡量方式和分寸后,那一年我们都会调用 setMeasuredDimesion()方法,今年我们的布局,才真正的钦赐了小幅和惊人

/**
 * Email: 2185134304@qq.com
 * Created by JackChen 2018/3/24 9:44
 * Version 1.0
 * Params:
 * Description:    测量模式计算方式
*/
public class TextView extends View {
    public TextView(Context context) {
        super(context);
    }

    public TextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // 指定宽高
        // widthMeasureSpec = childWidthMeasureSpec
        // heightMeasureSpec = childHeightMeasureSpec

        // 我们之前讲的
        // wrap_content = AT_MOST
        // match_parent 、fill_parent、100dp = Exactly

        // 测量模式和大小是由父布局和它的孩子决定的,比方说:

        // 父布局是包裹内容,就算子布局是match_parent,这个是时候的测量模式还是 AT_MOST
        // 父布局是match_parent,就算子布局是match_parent,这个时候的测量模式是 EXACTLY

        setMeasuredDimension();
    }
}

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int opticalWidth  = insets.left + insets.right;
            int opticalHeight = insets.top  + insets.bottom;

            measuredWidth  += optical ? opticalWidth  : -opticalWidth;
            measuredHeight += optical ? opticalHeight : -opticalHeight;
        }
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }
而setMeasuredDimension();方法其实什么都没干,正是在setMeasuredDimensionRaw()方法中给宽高赋值,在那年 mMeasuredWidth和mMeasureHeight才真正的有值
下一场度量全部子孩子的宽高,源码中是经过for循环,获取全部子孩子,然后去调用child.measure(childWidthMeasureSpec, childHeightMeasureSpec)方法度量子View的冲天,即就是TextView的万丈,等衡量出TextView的宽高,然后去衡量自身的宽高(即就是父布局):
若果和谐(即正是父布局)是LinearLayout何况是垂直方向,那么和睦惊人就是连连的叠合子View的惊人; childHeight = child.getMeasuredHeight();
借使自个儿(即正是父布局)是RelativeLayout,那么父布局的冲天是,钦点子孩子中最高的;
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:id="@+id/text_view" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:id="@+id/text_view2" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:id="@+id/text_view3" />
    </LinearLayout>

总结:

衡量是从外往里递归,也便是说:
ViewRootImpl会把本人的度量情势传递给 -> DecorView,然后DecorView会把团结的衡量方式传递给 activity_main中的LinearLayout ->
接下来LinearLayout通过for循环把温馨的衡量情势传递给 TextView,然后就能够调用 TextView的onMeasure()方法,然后依照传递过来的LinearLayout的衡量情势来内定TextView的宽高,衡量完毕后透过childHeight = child.getMeasuredHeight();获取到 子View的宽高,即就是获得到3个TextView的宽高后,来估测计算父布局,即就是LinearLayout
和煦的宽高 ,然后再把温馨的宽高向外传递给DecorView ->
下一场DecorView依据 LinearLayout传回来的宽高,然后总结自个儿的宽高 , 把团结宽高总结好后,然后再把本人的宽高向外传递给 ViewRootImpl 。


onMeasure()源码解析总括如下:
度量是从外往里递归的:
从外往里:

第一从最外层的 ViewRootImpl伊始,它把它的 衡量方式传递给 DecorView,然后DecorView把自身的衡量形式 传递给 父布局LinearLayout,然后LinearLayout再把团结的衡量方式 传递给 子View;

从里往外:

等子View计算出团结宽高后,然后把本人宽高传递给父布局LinearLayout,然后LinearLayout遵照子View的宽高,来总结本人的宽高,这里总计格局就是:
譬如父布局是 LinearLayout,且是笔直方向,父布局中度正是累加子布局中度;
假若父布局是RelativeLayout,那么父布局中度便是内定 子孩子中最高的;
,然后LinearLayout把团结的可观传递给 它的父布局,就是这么一路都把团结的中度传递给父布局,最终传递给 DecorView、传递给 ViewRootImpl,

onMeasure()源码中正是如此衡量的,如下图所示:

图片 4

onMeasure()源码解析.png

代码已上传至github:
https://github.com/shuai999/View_day08_2

本文由今晚最快开奖现场直播发布于编程应用,转载请注明出处:源码剖判

关键词:

利用Intent来开展App间的主导交互,系统深远学习

Intent是Android中存放一个操作的抽象描述的数据结构,可用于在不同的组件以及不同的app间进行传递,是消息的载体,有显...

详细>>

源码分析,Glide使用简介及流程分析

Glide 那篇小说将会为大家梳理一下Glide3.5版本的大旨流程,为了便利阅读,文章中的源码部分将轻松部分有关至极捕获...

详细>>

Android难题之——自定义View(上)

1、每个Activity包含一个Window对象(Window是abstract的,一般是由PhoneWindow实现)。2、PhoneWindow将DecorView设置为整个应用的...

详细>>

热修复热点框架简析,Android插件框架机制研商

笔者 百度任玉刚 插件化框架 途牛已上线使用帮衬代码能源文件以插件情势加入到host。 插件化的主干形式是将二个...

详细>>