安卓

setContentView源码分析

2022-02-23  本文已影响0人  w达不溜w

直奔主题setContentView:AppCompatActivity#setContentView

@Override
public void setContentView(@LayoutRes int layoutResID) {
  initViewTreeOwners();
  //getDelegate()具体实现类是AppCompatDelegateImpl
  getDelegate().setContentView(layoutResID);
}

进入AppCompatDelegateImpl#setContentView

@Override
public void setContentView(int resId) {
  //检测mSubDecor视图是否已经创建,没有则创建mSubDecor视图,同时将视图添加到Window上
  ensureSubDecor();
  //获取根布局
  ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
  contentParent.removeAllViews();
  //将我们的布局添加到根布局上
  LayoutInflater.from(mContext).inflate(resId, contentParent);
  mAppCompatWindowCallback.getWrapped().onContentChanged();
}

查看ensureSubDecor方法

private void ensureSubDecor() {
  //...
  if (!mSubDecorInstalled) {
    //创建mSubDecor视图
    mSubDecor = createSubDecor();
    mSubDecorInstalled = true;
  }
  // ...
}

createSubDecor()创建mSubDecor视图

private ViewGroup createSubDecor() {
  //获得主体样式
  TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
    //我们自定义主体必须继承AppCompat
  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.");
  }

  //...

  // 确认Window上是否已安装DecorView,没有则创建DecorView并添加到Window上
  // 分析①:mWindow.getDecorView()
  mWindow.getDecorView();
    
  final LayoutInflater inflater = LayoutInflater.from(mContext);
  ViewGroup subDecor = null;
  
  //...
  // 填充subDecor
  subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);  
  //...
    //找到id为action_bar_activity_content的contentView
  final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
    //找到PhoneWindow中的ContentView
  final ViewGroup windowContentView = (ViewGroup)mWindow.findViewById(android.R.id.content);
  if (windowContentView != null) {
    // 如果已经有视图添加到窗口,移除并添加到contentView中
    while (windowContentView.getChildCount() > 0) {
      final View child = windowContentView.getChildAt(0);
      windowContentView.removeViewAt(0);
      contentView.addView(child);
    }

    //标记android.R.id.content视图的id为NO_ID
    //并将我们布局父容器FrameLayout设置为android.R.id.content
    windowContentView.setId(View.NO_ID);
    contentView.setId(android.R.id.content);
    
  }

  // 将我们添加视图的父视图添加到Window上
  mWindow.setContentView(subDecor);

  //...

  return subDecor;
}

分析①:mWindow.getDecorView() 创建DecorView

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

installDecor()具体实现

// This is the top-level view of the window, containing the window decor.
//DecorView是Window的顶层View
private DecorView mDecor;

private void installDecor() {
  mForceDecorInstall = false;
  if (mDecor == null) {
        //DecorView为空则需要新建
      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);
     //...
  }
}

通过调用generateDecor创建DecorView

protected DecorView generateDecor(int featureId) {
  //...
  //new一个DecorView
  return new DecorView(context, featureId, this, getAttributes());
}

创建DecorView后,再调用generateLayout

protected ViewGroup generateLayout(DecorView decor) {
  TypedArray a = getWindowStyle();
  //如果设置了Window_windowNoTitle,则会调用requestFeature(FEATURE_NO_TITLE),其它同理
  if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
    requestFeature(FEATURE_NO_TITLE);
  } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
    // Don't allow an action bar if there is no title.
    requestFeature(FEATURE_ACTION_BAR);
  }
  //是否设置全屏
  if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
    setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
  }
  //...
  int layoutResource;
  int features = getLocalFeatures();
  //根据设置的features来加载对应的布局
  if()else if()else{
  // screen_simple就是DecorView加载的布局
    layoutResource = R.layout.screen_simple;
  }
  //把布局解析加载到DecorView,用的addView() 
  mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
  //从系统的layoutResource找一个id是android.R.id.content的FrameLayout
  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  
  return contentParent;
}

generateLayout方法会根据用户设置的主题去设置对应的feature,根据feature来选择加载对应的布局文件,这就是为什么要在setContentView之前调用requestFeature的原因。

看下布局R.layout.screen_simple

<?xml version="1.0" encoding="utf-8"?>
<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>

FrameLayout里面id是@android:id/content,我们setContentView的内容就是要添加到这个FrameLayout中。

LayoutInflater解析xml并创建View实例分析

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
   return inflate(resource, root, root != null);
}
  LayoutInflater.from(mContext).inflate(resId, contentParent)
> LayoutInflater#inflate() xml解析
> LayoutInflater#createViewFromTag()
> LayoutInflater#tryCreateView()
> AppCompatDelegateImpl#onCreateView()
> AppCompatDelegateImpl#createView()
> AppCompatViewInflater#createView()

tryCreateView()

 public final View tryCreateView(@Nullable View parent, @NonNull String name,
        @NonNull Context context,
        @NonNull AttributeSet attrs) {
        View view;
        //可以通过反射的方式修改mFactory2,从而实现换肤插件
        if (mFactory2 != null) {
            //最终调用AppCompatViewInflater中的createView方法
             //可以通过自定义mFactory2让其生成用户所需要的控件
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
            view = mFactory.onCreateView(name, context, attrs);
        } else {
            view = null;
        }

        if (view == null && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }

        return view;
    }

View最终实例化:

final View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs, boolean inheritContext,
            boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
  //...
  View view = null;

  // We need to 'inject' our tint aware Views in place of the standard framework versions
  //如果是系统控件,则直接new出来
  switch (name) {
    case "TextView":
      view = createTextView(context, attrs);
      verifyNotNull(view, name);
      break;
    case "ImageView":
      view = createImageView(context, attrs);
      verifyNotNull(view, name);
      break;
    case "Button":
      view = createButton(context, attrs);
      verifyNotNull(view, name);
      break;
    //...
    default:
      view = createView(context, name, attrs);
  }
  if (view == null && originalContext != context) {
    // 没有匹配,也就是不是系统控件,到则通过反射创建
    view = createViewFromTag(context, name, attrs);
  }
    //...
  return view;
}

createViewFromTag最终会调用createViewByPrefix方法

private View createViewByPrefix(Context context, String name, String prefix)
           throws ClassNotFoundException, InflateException {
 //先从构造缓存中获取
 Constructor<? extends View> constructor = sConstructorMap.get(name);
 try {
   if (constructor == null) {
     // Class not found in the cache, see if it's real, and try to add it
     Class<? extends View> clazz = Class.forName(
       prefix != null ? (prefix + name) : name,
       false,
       context.getClassLoader()).asSubclass(View.class);
           //利用反射构建一个构造函数
     constructor = clazz.getConstructor(sConstructorSignature);
     sConstructorMap.put(name, constructor);
   }
   constructor.setAccessible(true);
   //利用反射创建View实例
   return constructor.newInstance(mConstructorArgs);
 } catch (Exception e) {
   // We do not want to catch these, lets return null and let the actual LayoutInflater
   // try
   return null;
 }
}

解析xml去匹配系统的View,匹配到通过new创建实例,没有匹配到(如自定义View)则通过反射去创建实例。

上一篇下一篇

猜你喜欢

热点阅读