SetContentView source code analysis

setContentView starts with a new activity we created ourselves

public class ActivityTest extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

    }

}
                ↓ setContentView(R.layout.activity_test);
public class AppCompatActivity extends ... {

     public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
     }


                ↓ getDelegate().setContentView(layoutResID);先找getDelegate()
                ↓ getDelegate()也在AppCompatActivity 中
 @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
}
                ↓  getDelegate() = AppCompatDelegate.create(this, this);
public abstract class AppCompatDelegate {
  public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
    }   
}

So the last call is the setContentView method in AppCompatDelegateImpl:

class AppCompatDelegateImpl extends ...{

    public void setContentView(int resId) {
        this.ensureSubDecor();
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
        contentParent.removeAllViews();
        LayoutInflater.from(this.mContext).inflate(resId, contentParent);
        this.mOriginalWindowCallback.onContentChanged();
    }

}

  Then look at the ensureSubDecor method:

ensureSubDecor 

private void ensureSubDecor() {
        if (!this.mSubDecorInstalled) {
            this.mSubDecor = this.createSubDecor();//核心代码

           //省略其它代码。。。
        }

    }

createSubDecor 

 private ViewGroup createSubDecor() {
        TypedArray a = this.mContext.obtainStyledAttributes(styleable.AppCompatTheme);
        if (!a.hasValue(styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException("You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        } else {
             

            this.mIsFloating = a.getBoolean(styleable.AppCompatTheme_android_windowIsFloating, false);
            a.recycle();
            this.mWindow.getDecorView(); ---> 核心代码 1 创建DecirView
            LayoutInflater inflater = LayoutInflater.from(this.mContext);
            ViewGroup subDecor = null;
            if (!this.mWindowNoTitle) {
                if (this.mIsFloating) {
                    subDecor = (ViewGroup)inflater.inflate(layout.abc_dialog_title_material, (ViewGroup)null);
                    
                } else if (this.mHasActionBar) {
                    
                    subDecor = (ViewGroup)LayoutInflater.from((Context)themedContext).inflate(layout.abc_screen_toolbar, (ViewGroup)null); 
                    
                }
            } else {
                if (this.mOverlayActionMode) {
                    subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple_overlay_action_mode, (ViewGroup)null);
                } else {
                    subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple, (ViewGroup)null);
                }

                //其它代码省略...
                
                
            }
            通过上面subDecor = inflate(布局文件)可以看出,subDecor 的布局文件是下面四种布局文件之一:
                  1  abc_dialog_title_material
                  2  abc_screen_toolbar
                  3  abc_screen_simple_overlay_action_mode
                  4  abc_screen_simple
            if (subDecor == null) {
                如果subDecor为空就抛出异常 
                throw new IllegalArgumentException("AppCompat does not support the current theme features: { windowActionBar: " + this.mHasActionBar + ", windowActionBarOverlay: " + this.mOverlayActionBar + ", android:windowIsFloating: " + this.mIsFloating + ", windowActionModeOverlay: " + this.mOverlayActionMode + ", windowNoTitle: " + this.mWindowNoTitle + " }");
            } else {
                if (this.mDecorContentParent == null) {
                    this.mTitleView = (TextView)subDecor.findViewById(id.title);
                }

                ViewUtils.makeOptionalFitsSystemWindows(subDecor);
                 // 上面说了 subDecor 是四个布局文件中的一个创建,
                 // 每个布局文件都有一action_bar_activity_content   
                ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);
                ViewGroup windowContentView = (ViewGroup)this.mWindow.findViewById(16908290);
                if (windowContentView != null) {
                    while(windowContentView.getChildCount() > 0) {
                        View child = windowContentView.getChildAt(0);
                        windowContentView.removeViewAt(0);
                        contentView.addView(child);
                    }

                    windowContentView.setId(-1);
                    contentView.setId(16908290);---> 核心代码 2 contentView的id被设置成16908290
                    if (windowContentView instanceof FrameLayout) {
                        ((FrameLayout)windowContentView).setForeground((Drawable)null);
                    }
                }

                this.mWindow.setContentView(subDecor);---> 核心代码 3 把subDecor填到mWindow中
                contentView.setAttachListener(new OnAttachListener() {
                    public void onAttachedFromWindow() {
                    }

                    public void onDetachedFromWindow() {
                        AppCompatDelegateImpl.this.dismissPopups();
                    }
                });
                return subDecor;
            }
        }
    }

Summarize the createSubDecor method:

1 this.mWindow.getDecorView(); Create Decorview, detailed below

2 Loading subDecor is a ViewGroup, after loading is completed:

        ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);

        ViewGroup windowContentView =  (ViewGroup)this.mWindow.findViewById(16908290);

3 this.mWindow.setContentView(subDecor); Set subDecor to this.mWindow

The following analysis of these three steps

First look at 1 this.mWindow.getDecorView(); What is this.mWindow? look down

 AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback) {
        ......

       mWindow 的初始化是在AppCompatDelegateImpl构造函数里
       this.mWindow = window;

        .....
       
    }
        想要知道mWindow是啥就要找到AppCompatDelegateImpl(context,window,callback)这个
        构造函数初始化的时候传入的window是啥
        还记得最开始我们从setContentView点进来的代码么
        
*********************************************************************** 
public class ActivityTest extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

    }

}
                ↓ setContentView(R.layout.activity_test);
public class AppCompatActivity extends ... {

     public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
     }


                ↓ getDelegate().setContentView(layoutResID);先找getDelegate()
                ↓ getDelegate()也在AppCompatActivity 中
 @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
}
                ↓  getDelegate() = AppCompatDelegate.create(this, this);
public abstract class AppCompatDelegate {
  public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        在这里初始化的,activity就是AppCompatActivity ,window就是activity.getWindow()
        return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
    }   
}
***************************************************************************************

window就是AppCompatActivity.getWindow(),但是AppCompatActivity中没有getWindow()方法,
getWindow()是在其父类Activity中实现
public class Activity extends ... ... {

       private Window mWindow;
        
      final void attach(Context context, ......) {
        attachBaseContext(context);

           

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        
        ......
       }

      public @Nullable Window getWindow() {
            return mWindow;
       }
}



Finally find the window is the PhoneWindow object.

Then we go to PhoneWindow to view the key this.mWindow.getDecorView();

1 this.mWindow.getDecorView();

 @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();  --->  核心代码
        }
        return mDecor;
    }

 private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1); --->  核心代码
           
            ....省略其它代码....
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor); --->  核心代码
            
            ....省略其它代码.....
            }
}

First look at mDecor = generateDecor(-1); The generateDecor method is very simple to create a DecorView object and return the value to mDecor

 protected DecorView generateDecor(int featureId) {
      
        ...其它代码省略...        

        return new DecorView(context, featureId, this, getAttributes());
    }

Then look at mContentParent = generateLayout(mDecor); 

1: mContentParent is a ViewGroup;

2: generateLayout(mDecor); Pass the DecorView just created above as a parameter

protected ViewGroup generateLayout(DecorView decor) {
        
        ...其它代码省略...

        // Inflate the window decor.

        这里精简保留的,layoutResource = R.layout.布局文件 
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
              
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            
            layoutResource = R.layout.screen_progress;
           
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
           
            if (mIsFloating) {
               
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
           
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
             
            if (mIsFloating) {
              
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
             
            layoutResource = R.layout.screen_simple;
            
        }

        mDecor.startChanging();
        layoutResource就是上面布局文件之中的一个,加载到mDecor即DecorView中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
       
        而上面布局文件中它们都有一个id是content的
        而且这个findViewById是在PhoneWindow直接使用?它是Activity?文章最后分析
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        
        return contentParent; ← 返回DecorView的布局文件中id为content的控件
    }

To summarize, the generateLayout(mDecor) method is to load a layout file into DecorView, and return the control whose id is content in the layout to mContentParent. In this way, PhoneWindow creates a new DecorView object, loads the layout file for it, and prepares the content container of the layout file.

Here 1 this.mWindow.getDecorView() is complete.

2 is the container that is ready to load the layout file we created

    ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);

    ViewGroup windowContentView =  (ViewGroup)this.mWindow.findViewById(16908290);

     windowContentView.setId(-1);

     contentView.setId(16908290); //In fact, this is the container that loads the layout file of the Activity created by ourselves

Then we will take a look at 3 this.mWindow.setContentView(subDecor); the same is this.mWindow, we will find the setContentView method in PhoneWindow

3 this.mWindow.setContentView(subDecor)

@Override
    public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        // 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)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            核心方法
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

 mContentParent.addView(view, params);  

|--- mContentParent is the content control that loads the layout file in the DecorView above

|--- view is the subDecor we passed in

Load the subDecor into the content control of the DecorView layout file. Remember the action_bar_activity_content in the subDecor layout file:

ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);

contentView.setId(16908290);
                

Let’s look at the initial setContentView

 public void setContentView(int resId) {
        this.ensureSubDecor();
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
        contentParent.removeAllViews();
        LayoutInflater.from(this.mContext).inflate(resId, contentParent);
        this.mOriginalWindowCallback.onContentChanged();
    }

Our own layout file is loaded into ContentFrameLayout with id action_bar_activity_content in subDecor layout.

The subDecor is loaded into the FrameLayout whose id is content in the DecorView layout;

DecorView is created by PhoneWindow

PhoneWindow is initialized in the Activity attach method.

This way the entire loading process is chained up.

It's time to put up such a picture

The xml file loaded by DecorView is one of the following:

screen_swipe_dismiss 

screen_title_icons 

screen_progress

screen_custom_title

screen_action_bar

screen_title

screen_simple_overlay_action_mode

screen_simple

And they all have a FrameLayout control with an id of content to load subDcor.

The xml file loaded by subDcor is one of the following:

abc_dialog_title_material
abc_screen_toolbar
abc_screen_simple_overlay_action_mode
abc_screen_simple

And they all have a ContentFrameLayout control with an id of action_bar_activity_content to load our custom layout file, which is the xml file created when we create the Activity.

 

There is another question above, why can PhoneWindow findViewById directly? Click it and you will find that it is implemented in its parent class Window

public abstract class Window {
     @Nullable
    public <T extends View> T findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }

    public abstract View getDecorView();

}

The specific implementation of getDecorView is back to PhoneWindow;

    @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

This method looks very familiar, it is the method we initialize the DecorView object, and what we return is the DecorView that we initialized and loaded the layout.

So PhoneWindow's findViewById is actually operating on the DecorView it holds.

 

 

Guess you like

Origin blog.csdn.net/u011288271/article/details/110517719