美文网首页
2020-02-20-Android的View加载

2020-02-20-Android的View加载

作者: 耿望 | 来源:发表于2020-02-20 19:22 被阅读0次

    Activity的View加载

    Android的View加载 (2).jpg

    创建一个Hello World,Android Studio会帮我们自动生成MainActivity。

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    

    进入OnCreate代码,看到调用super方法,进入AppCompatActivity 。

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            final AppCompatDelegate delegate = getDelegate();
            delegate.installViewFactory();
            delegate.onCreate(savedInstanceState);
            if (delegate.applyDayNight() && mThemeId != 0) {
                // If DayNight has been applied, we need to re-apply the theme for
                // the changes to take effect. On API 23+, we should bypass
                // setTheme(), which will no-op if the theme ID is identical to the
                // current theme ID.
                if (Build.VERSION.SDK_INT >= 23) {
                    onApplyThemeResource(getTheme(), mThemeId, false);
                } else {
                    setTheme(mThemeId);
                }
            }
            super.onCreate(savedInstanceState);
        }
    

    首先获取一个AppCompatDelegate对象,看下getDelegate()方法。这里是个单例模式,确保每个Activity只有一个Delegate实例。

        @NonNull
        public AppCompatDelegate getDelegate() {
            if (mDelegate == null) {
                mDelegate = AppCompatDelegate.create(this, this);
            }
            return mDelegate;
        }
    

    接着看到create()方法,这里类似一个工厂模式,根据SDK版本不同,选择不同的实现。仔细看就会发现,AppCompatDelegateImplV9是所有实现类的父类,所以这里假设实现类就是V9。

        private static AppCompatDelegate create(Context context, Window window,
                AppCompatCallback callback) {
            if (Build.VERSION.SDK_INT >= 24) {
                return new AppCompatDelegateImplN(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 23) {
                return new AppCompatDelegateImplV23(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 14) {
                return new AppCompatDelegateImplV14(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 11) {
                return new AppCompatDelegateImplV11(context, window, callback);
            } else {
                return new AppCompatDelegateImplV9(context, window, callback);
            }
        }
    

    回到我们的代码,下一步是setContentView()。这是个抽象方法,具体实现在ImplV9中。

        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            getDelegate().setContentView(view, params);
        }
    
        @Override
        public void setContentView(int resId) {
            F();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            LayoutInflater.from(mContext).inflate(resId, contentParent);
            mOriginalWindowCallback.onContentChanged();
        }
    

    第一步是调用ensureSubDecor(),这里创建了一个ViewGroup对象mSubDecor。

        private void ensureSubDecor() {
            if (!mSubDecorInstalled) {
                mSubDecor = createSubDecor();
    
                // If a title was set before we installed the decor, propagate it now
                CharSequence title = getTitle();
                if (!TextUtils.isEmpty(title)) {
                    onTitleChanged(title);
                }
    
                applyFixedSizeWindow();
    
                onSubDecorInstalled(mSubDecor);
    
                mSubDecorInstalled = true;
    
                // Invalidate if the panel menu hasn't been created before this.
                // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
                // being called in the middle of onCreate or similar.
                // A pending invalidation will typically be resolved before the posted message
                // would run normally in order to satisfy instance state restoration.
                PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                if (!isDestroyed() && (st == null || st.menu == null)) {
                    invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
                }
            }
        }
    

    createSubDecor的代码量比较大,重点看下注释1处,这里获取了一个DecorView对象。其中mWindow是在AppCompatDelegate对象创建的时候传递的参数,实际上是一个PhoneWindow对象。具体可以看下Activity类的代码。然后注释2处,调用了mWindow.setCotentView()。

        private ViewGroup createSubDecor() {
            TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
    
            if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
                a.recycle();
                throw new IllegalStateException(
                        "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
            }
    
            if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
                requestWindowFeature(Window.FEATURE_NO_TITLE);
            } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
                // Don't allow an action bar if there is no title.
                requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
            }
            if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
                requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
            }
            if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
                requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
            }
            mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
            a.recycle();
    //1 Important
            // Now let's make sure that the Window has installed its decor by retrieving it
            mWindow.getDecorView();
    
            final LayoutInflater inflater = LayoutInflater.from(mContext);
            ViewGroup subDecor = null;
    
    
            if (!mWindowNoTitle) {
                if (mIsFloating) {
                    // If we're floating, inflate the dialog title decor
                    subDecor = (ViewGroup) inflater.inflate(
                            R.layout.abc_dialog_title_material, null);
    
                    // Floating windows can never have an action bar, reset the flags
                    mHasActionBar = mOverlayActionBar = false;
                } else if (mHasActionBar) {
                    /**
                     * This needs some explanation. As we can not use the android:theme attribute
                     * pre-L, we emulate it by manually creating a LayoutInflater using a
                     * ContextThemeWrapper pointing to actionBarTheme.
                     */
                    TypedValue outValue = new TypedValue();
                    mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
    
                    Context themedContext;
                    if (outValue.resourceId != 0) {
                        themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
                    } else {
                        themedContext = mContext;
                    }
    
                    // Now inflate the view using the themed context and set it as the content view
                    subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                            .inflate(R.layout.abc_screen_toolbar, null);
    
                    mDecorContentParent = (DecorContentParent) subDecor
                            .findViewById(R.id.decor_content_parent);
                    mDecorContentParent.setWindowCallback(getWindowCallback());
    
                    /**
                     * Propagate features to DecorContentParent
                     */
                    if (mOverlayActionBar) {
                        mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
                    }
                    if (mFeatureProgress) {
                        mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
                    }
                    if (mFeatureIndeterminateProgress) {
                        mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
                    }
                }
            } else {
                if (mOverlayActionMode) {
                    subDecor = (ViewGroup) inflater.inflate(
                            R.layout.abc_screen_simple_overlay_action_mode, null);
                } else {
                    subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
                }
    
                if (Build.VERSION.SDK_INT >= 21) {
                    // If we're running on L or above, we can rely on ViewCompat's
                    // setOnApplyWindowInsetsListener
                    ViewCompat.setOnApplyWindowInsetsListener(subDecor,
                            new OnApplyWindowInsetsListener() {
                                @Override
                                public WindowInsetsCompat onApplyWindowInsets(View v,
                                        WindowInsetsCompat insets) {
                                    final int top = insets.getSystemWindowInsetTop();
                                    final int newTop = updateStatusGuard(top);
    
                                    if (top != newTop) {
                                        insets = insets.replaceSystemWindowInsets(
                                                insets.getSystemWindowInsetLeft(),
                                                newTop,
                                                insets.getSystemWindowInsetRight(),
                                                insets.getSystemWindowInsetBottom());
                                    }
    
                                    // Now apply the insets on our view
                                    return ViewCompat.onApplyWindowInsets(v, insets);
                                }
                            });
                } else {
                    // Else, we need to use our own FitWindowsViewGroup handling
                    ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
                            new FitWindowsViewGroup.OnFitSystemWindowsListener() {
                                @Override
                                public void onFitSystemWindows(Rect insets) {
                                    insets.top = updateStatusGuard(insets.top);
                                }
                            });
                }
            }
    
            if (subDecor == null) {
                throw new IllegalArgumentException(
                        "AppCompat does not support the current theme features: { "
                                + "windowActionBar: " + mHasActionBar
                                + ", windowActionBarOverlay: "+ mOverlayActionBar
                                + ", android:windowIsFloating: " + mIsFloating
                                + ", windowActionModeOverlay: " + mOverlayActionMode
                                + ", windowNoTitle: " + mWindowNoTitle
                                + " }");
            }
    
            if (mDecorContentParent == null) {
                mTitleView = (TextView) subDecor.findViewById(R.id.title);
            }
    
            // Make the decor optionally fit system windows, like the window's decor
            ViewUtils.makeOptionalFitsSystemWindows(subDecor);
    
            final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                    R.id.action_bar_activity_content);
    
            final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
            if (windowContentView != null) {
                // There might be Views already added to the Window's content view so we need to
                // migrate them to our content view
                while (windowContentView.getChildCount() > 0) {
                    final View child = windowContentView.getChildAt(0);
                    windowContentView.removeViewAt(0);
                    contentView.addView(child);
                }
    
                // Change our content FrameLayout to use the android.R.id.content id.
                // Useful for fragments.
                windowContentView.setId(View.NO_ID);
                contentView.setId(android.R.id.content);
    
                // The decorContent may have a foreground drawable set (windowContentOverlay).
                // Remove this as we handle it ourselves
                if (windowContentView instanceof FrameLayout) {
                    ((FrameLayout) windowContentView).setForeground(null);
                }
            }
    //2 Important
            // Now set the Window's content view with the decor
            mWindow.setContentView(subDecor);
    
            contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
                @Override
                public void onAttachedFromWindow() {}
    
                @Override
                public void onDetachedFromWindow() {
                    dismissPopups();
                }
            });
    
            return subDecor;
        }
    

    进入PhoneWindow类,getDecorView方法调用了installDecor。installDecor方法代码量比较大,比较重要的是对两个变量的初始化,mDecor和mContentParent,分别是generateDecor()和generateLayout(),完成布局加载,这里不放代码了。

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

    最后回到setContentView(),完成contentParent的布局加载。
    参考:

    https://blog.csdn.net/yewei02538/article/details/60979075

    相关文章

      网友评论

          本文标题:2020-02-20-Android的View加载

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