Viewペイントプロセス(一)Activityから

5784 ワード

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

この行を見てinflate(layoutResID,mContentParent)はActivityのsetContentView()を知っていますが、実は主にmContentParentです.addView(view)、mContentParentはView Group
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 ;
}

追跡findView ById()
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);
    //...
    
}

追跡するsetView()、View RootImplクラス内
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がView RootImplであり、View RootImplがmeasure()、layout()、draw()のいくつかの方法でchild Viewを実行するmeasure()、layout()はもちろん1つのViewの1回のrequestLyaout()ではなく、すべてのViewの再描画がトリガーされ、View描画方法ではchild Viewを再描画する必要があるかどうかを判断します.invalidate()メソッド全体のフロー原理も大きく異なり,再帰的に向上し,最終的にはViewRootImplに入りscheduleTransversals()を呼び出す.