setContentView流程

本文详细剖析了AppCompatActivity如何加载布局,从ensureSubDecor、createSubDecor到setContentView的过程,涉及Theme设置、DecorView生成、内容视图添加等关键步骤,揭示了Android应用界面构建的内部机制。
摘要由CSDN通过智能技术生成
AppCompatActivity的方法实际是调用

先获取AppCompatDelegate委托类,最终调用AppCompatDelegateImplV9,只分析最常用的方法:

所有方法都用到了ensureSubDecor

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);//设置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);
      }
  }
}
创建SubDecor
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);
  } 
...省略

  mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
  a.recycle();//设置主题

  // 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);

          ...省略生成设置subDecor

  // 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;
}
最终设置contentview
@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);//addview在这
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
安装Decor
private void installDecor() {
  mForceDecorInstall = false;
  if (mDecor == null) {
      mDecor = generateDecor(-1);//创建
      mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
      mDecor.setIsRootNamespace(true);
      if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
          mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
      }
  } else {
      mDecor.setWindow(this);
  }
  if (mContentParent == null) {
      mContentParent = generateLayout(mDecor);//生成布局

      。。。省略
      
  }
}
创建的DecorView是一个FrameLayout
protected DecorView generateDecor(int featureId) {
  // System process doesn't have application context and in that case we need to directly use
  // the context we have. Otherwise we want the application context, so we don't cling to the
  // activity.
  Context context;
  if (mUseDecorContext) {//Activity才为true
      Context applicationContext = getContext().getApplicationContext();
      if (applicationContext == null) {
          context = getContext();
      } else {
          context = new DecorContext(applicationContext, getContext());
          if (mTheme != -1) {
              context.setTheme(mTheme);
          }
      }
  } else {
      context = getContext();
  }
  return new DecorView(context, featureId, this, getAttributes());
}
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

生成布局

protected ViewGroup generateLayout(DecorView decor) {
  // Apply data from current theme.

  ...省略跟主题和样式相关

} else {
  // Embedded, so no decoration is needed.
  layoutResource = R.layout.screen_simple;
  // System.out.println("Simple!");
}

  mDecor.startChanging();
  mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//contentParent就是id为content的FrameLayout
  if (contentParent == null) {
      throw new RuntimeException("Window couldn't find content container view");
  }

  if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
      ProgressBar progress = getCircularProgressBar(false);
      if (progress != null) {
          progress.setIndeterminate(true);
      }
  }

  if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
      registerSwipeCallbacks(contentParent);
  }

  // Remaining setup -- of background and title -- that only applies
  // to top-level windows.
  if (getContainer() == null) {
      final Drawable background;
      if (mBackgroundResource != 0) {
          background = getContext().getDrawable(mBackgroundResource);
      } else {
          background = mBackgroundDrawable;
      }
      mDecor.setWindowBackground(background);

      final Drawable frame;
      if (mFrameResource != 0) {
          frame = getContext().getDrawable(mFrameResource);
      } else {
          frame = null;
      }
      mDecor.setWindowFrame(frame);

      mDecor.setElevation(mElevation);
      mDecor.setClipToOutline(mClipToOutline);

      if (mTitle != null) {
          setTitle(mTitle);
      }

      if (mTitleColor == 0) {
          mTitleColor = mTextColor;
      }
      setTitleColor(mTitleColor);
  }

  mDecor.finishChanging();

  return contentParent;
}

查看布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

嵌套关系如图

AppCompatDelegateImplV9中生成的view布局 abc_screen_simple

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<android.support.v7.widget.FitWindowsLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/action_bar_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <android.support.v7.widget.ViewStubCompat
        android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/abc_action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include layout="@layout/abc_screen_content_include" />

</android.support.v7.widget.FitWindowsLinearLayout>

嵌套关系图最后加到content中

 

最后看

@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;
    }

分析到这基本上流程已经梳理清晰了。

总的布局结构

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值