美文网首页程序员Android开发Android技术知识
View绘制过程(一)从Activity开始

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

作者: 风风风筝 | 来源:发表于2016-07-02 22:17 被阅读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()

    相关文章

      网友评论

        本文标题:View绘制过程(一)从Activity开始

        本文链接:https://www.haomeiwen.com/subject/qliqjttx.html