美文网首页面试题
一、View绘制流程

一、View绘制流程

作者: 徒步青云 | 来源:发表于2020-03-04 16:51 被阅读0次

    1、Activity布局添加流程

    1、创建顶层布局容器DecorView,它被PhoneWindow类所持有
    2、在顶层布局DecorView中加载基础布局SubDecor,SubDecor一般包含两部分:title和content
    3、将自定义布局添加到基础布局SubDecor中

    new了一个DecorView,获取其中id为content的控件mContentParent,将subDecor添加到mContentParent,将自定义布局文件添加到subDecor

    image.png

    源码分析:

    //MainActivity.java  
    protected void onCreate(@Nullable Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);
        setContentView(int layoutResID)
    }
    
    //AppCompatActivity.java
    public void setContentView(@LayoutRes int layoutResID) {  
        getDelegate().setContentView(layoutResID);  
    }
    
    public AppCompatDelegate getDelegate() {  
        if (mDelegate == null) {  
            mDelegate = AppCompatDelegate.create(this, this);  
        }  
        return mDelegate;  
    }
    
    //AppCompatDelegate.java
    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        //create静态方法返回AppCompatDelegateImpl对象
        //第二个参数将activity中的mWindow对象,赋值给AppCompatDelegateImpl中mWindow变量
        //而activity中的mWindow对象,是在attach方法中被new成PhoneWindow
        return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
    }
    
    //AppCompatDelegateImpl.java
    public void setContentView(int resId) {
        // ***重点***
        //1、创建SubDecor,并将其添加到DecorView中
        ensureSubDecor();
        //2、获得id为content的FrameLayout
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);  
        contentParent.removeAllViews();  
        //3、将自定义布局添加到content布局中
        LayoutInflater.from(mContext).inflate(resId, contentParent);  
        mAppCompatWindowCallback.getWrapped().onContentChanged();  
    }
      
    private void ensureSubDecor() {  
        if (!mSubDecorInstalled) {  
            mSubDecor = createSubDecor();
            //.......省略设置标题、content.requestLayout等内容  
            mSubDecorInstalled = true;
        }
    }
    
    private ViewGroup createSubDecor() {  
        //.......省略ActionBar风格设置
        
        // Now let's make sure that the Window has installed its decor by retrieving it
        //如果DecorView不存在,则创建它,这里需要注意一下
        mWindow.getDecorView();
        
        final LayoutInflater inflater = LayoutInflater.from(mContext);  
        ViewGroup subDecor = null;
        //.......省略部分内容:根据不同风格、属性,为subDecor设置不同的布局文件
        
        if (mDecorContentParent == null) {  
            //获取title控件
            mTitleView = (TextView) subDecor.findViewById(R.id.title);  
        }  
    
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(  
                R.id.action_bar_activity_content);  
      
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);  
        //如果PhoneWindow中已存在id为content的容器,则将容器中所有子控件转移到DecorView中
        if (windowContentView != null) {
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }
    
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);
        }
    
        //将subDecor设置给mWindow
        mWindow.setContentView(subDecor);
        
        return subDecor;  
    }
    
    //PhoneWindow.java
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }
    
    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //new了一个DecorView,它继承至FrameLayout
            mDecor = generateDecor(-1);
            //........
        } else {
            mDecor.setWindow(this);
            //........
        }
        if (mContentParent == null) {
            //mContentParent控件id为content
            mContentParent = generateLayout(mDecor);
            //........
        }
    }
    
    protected ViewGroup generateLayout(DecorView decor) {
        //省略layoutResource赋值流程
        mDecor.startChanging();
        //将layoutResource布局文件inflate到mDecor中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //省略后续操作
        return contentParent;
    }
    
    //mWindow.setContentView(subDecor),其实是调用PhoneWindow的setContentView方法,将subDecor传入
    
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            //用于mContentParent已经在getDecorView->installDecor中被赋值,所以会调用到下面这个方法
            mContentParent.removeAllViews();
        }
    
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            //将subDecor添加到DecorView中
            mContentParent.addView(view, params);
        }
        //......
    }
    
    
    image.png image.png

    Activity的创建离不开ActivityThread。

    ActiveThread是每一个应用程序所在进程的主线程。

    //ActivityThread有一个ArrayList存储启动的 Activity列表。

    final ArrayList<ActivityClientRecord> mRelaunchingActivities
    = new ArrayList<ActivityClientRecord>();
    ActivityClientRecord存储启动的Activity相关信息。

    https://blog.csdn.net/joedan0104/article/details/82857450

    2、View的绘制流程

    ActivityThread.handleResumeActivity()
    -->WindowManagerImpl.addView()
    -->WindowManagerGlobal.addView()
    -->ViewRootImpl.addView()
    -->ViewRootImpl.requestLayout()
    -->ViewRootImpl.scheduleTraversals()
    -->ViewRootImpl.doTraversal()
    -->ViewRootImpl.performTraversal()
    -->进入View绘制流程
    

    1、绘制入口

    ActivityThread.handleResumeActivity
        ->WindowManagerImpl.addView(decorView,layoutParams)
        ->WindowManagerGlobal.addView()//会创建ViewRootImpl对象
    

    2、绘制的类及方法

    ViewRootImpl.setView(decorView,layoutParams,parentView)
        ->requestLayout()
            ->scheduleTraversals()
            ->doTraversal()
            ->performTraversals()
    

    3、绘制三大步骤

    测量:ViewRootImpl.performMeasure
    布局:ViewRootImpl.performLayout
    绘制:ViewRootImpl.performDraw
    

    3、UI绘制详细步骤

    1、测量performMeasure

    view.measure->view.onMeasure->view.setMeasuredDimension->setMeasuredDimensionRaw
    

    2、布局performLayout

    view.layout->view.onLayout
    

    3、绘制performDraw

    ViewRootImpl.draw(fullRedrawNeeded)->ViewRootImpl.drawSoftware->view.draw(Canvas)
    

    View测量:

    MeasureSpec是一个32位int值,其中前2位表示模式,后30位表示尺寸大小。

    相关方法:

    MeasureSpec.getMode()
    MeasureSpec.getSize()
    MeasureSpec.makeMeasureSpec(size, mode)
    
    模式 含义
    AT_MOST 父容器指定一个可用大小,View的大小不能超过这个值,LayoutPamras、wrap_content
    EXACTLY 父容器检测出View的大小,View的大小就是SpecSize、LayoutPamras、match_parent、固定大小
    UNSPECIFIED 父容器不对View做任何限制,系统内部使用
    image.png image.png

    相关文章

      网友评论

        本文标题:一、View绘制流程

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