程序员Android开发Android技术知识

View绘制过程(一)从Activity开始

2016-07-02  本文已影响596人  风风风筝
1、一切的开端,Activity的setContentView()
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
public Window getWindow() {
    return mWindow;
}

mWindow其实就是一个PhoneWindow

2、PhoneWindow
//...
private ViewGroup mContentParent;
//...
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();
    }
}

看到这一行mLayoutInflater.inflate(layoutResID, mContentParent)
就知道Activity中setContentView(),其实主要就是mContentParent.addView(view),mContentParent是ViewGroup

3、ViewGroup
public void addView(View child, int index, LayoutParams params) {
    if (DBG) {
        System.out.println(this + " addView");
    }

    if (child == null) {
        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
    }

    // addViewInner() will call child.requestLayout() when setting the new LayoutParams
    // therefore, we call requestLayout() on ourselves before, so that the child's request
    // will be blocked at our level
    requestLayout();
    invalidate(true);
    addViewInner(child, index, params, false);
}

重点requestLayout()

4、View的requestLayout()
public void requestLayout() {
    if (mMeasureCache != null) mMeasureCache.clear();

    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
        // Only trigger request-during-layout logic if this is the view requesting it,
        // not the views in its parent hierarchy
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null && viewRoot.isInLayout()) {
            if (!viewRoot.requestLayoutDuringLayout(this)) {
                return;
            }
        }
        mAttachInfo.mViewRequestingLayout = this;
    }

    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;

    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
        mAttachInfo.mViewRequestingLayout = null;
    }
}

重点是mParent.requestLayout(),很明显这个调用会向上递归
回到PhoneWindow里的mContentParent

mContentParent = generateLayout(mDecor);

protected ViewGroup generateLayout(DecorView decor) {
    //...
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    //...
    return contentParent ;
}

跟踪findViewById()

public View findViewById(@IdRes int id) {
    return getDecorView().findViewById(id);
}

得知mContentParent的parent
就是我们常在Activity里写的getWindow().getDecorView()
再看decor的parent是什么,Activity里面

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
public WindowManager getWindowManager() {
    return mWindowManager;
}
//....
mWindowManager = mWindow.getWindowManager();
//....

跟踪wm.addView(),Window类里面

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

public WindowManager getWindowManager() {
    return mWindowManager;
}

跟踪WindowManagerImpl

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

mGlobal就是WindowManagerGlobal

public void addView(View view, ViewGroup.LayoutParams params,
    //...
    ViewRootImpl root;
    //...
    root = new ViewRootImpl(view.getContext(), display);
    //...
    root.setView(view, wparams, panelParentView);
    //...
    
}

跟踪root.setView(),ViewRootImpl类里面

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    //...
    view.assignParent(this);
    //...
}

assignParent(),顾名思义就是设置View的mParent

跟了这么久,得知DecorView的parent就是ViewRootImpl

5、ViewRootImpl
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
private void performTraversals() {
    //...
    performMeasure()
    //...
    performLayout()
    //...
    performDraw()
}

这个类里requestLayout()方法最终会调用performTraversals()

总结:
ViewGroup.addView(view)会调用requestLayout()
View.requestLyaout()每次都会递归回到Activity最上层的那个DecorView的parent就是ViewRootImpl
然后ViewRootImpl开始执行measure()、layout()、draw()
这几个方法里面会再递归遍历执行child View的measure()、layout()、draw()
当然并不是一个View的一次requestLyaout()就会触发所有View的重绘,View绘制方法中会判断child View是否需要重绘。
invalidate()方法整个流程原理也大同小异,也是递归向上,最终还是进入ViewRootImpl,调用scheduleTraversals()

上一篇下一篇

猜你喜欢

热点阅读