首页 编程 软件学院 查看内容

Android视图加载到窗口的过程分析

2015-3-17 22:08 859 0

摘要: 这篇主要从onCreate方法里面我们必须要写的方法setContentView开始,研究布局视图是如何加载到手机窗口上的。 当在执行到setContentView时,实际上执行的是 [jav...
关键词: nbsp Window 布局 mContentParent setContentView makeNewWindow 方法 mDecor view java

这篇主要从onCreate方法里面我们必须要写的方法setContentView开始,研究布局视图是如何加载到手机窗口上的。 当在执行到setContentView时,实际上执行的是 [java] view plaincopypublic void setContentView(int layoutResID) {          getWindow().setContentView(layoutResID);          initActionBar();  }   可以看到实际上是Window类的setContentView方法 [java] view plaincopyprivate Window mWindow;  public Window getWindow() {          return mWindow;  }   Window类是一个抽象类,下面主要是找到他的实现类。mWindow初始化在 [java] view plaincopyfinal void attach(……) {          ……          mWindow = PolicyManager.makeNewWindow(this);          mWindow.setCallback(this);          mWindow.getLayoutInflater().setPrivateFactory(this);      ……      }   Attach方法在main方法中。具体查看上一篇博客。调用了PolicyManager类的静态方法makeNewWindow生成Window对象 [java] view plaincopyprivate static final String POLICY_IMPL_CLASS_NAME =          "com.android.internal.policy.impl.Policy";  private static final IPolicy sPolicy;  static {          try {              Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);              sPolicy = (IPolicy)policyClass.newInstance();          }  ……      }  public static Window makeNewWindow(Context context) {          return sPolicy.makeNewWindow(context);  }   可以看到是通过Policy类的makeNewWindow方法得到的Window对象,这里是通过反射机制获取的Policy的实例。 [java] view plaincopypublic Window makeNewWindow(Context context) {          return new PhoneWindow(context);  }   可以看到实际上是一个PhoneWindow。那么根据多态,其实在上面调用的就是PhoneWindow#setContentWindow [java] view plaincopypublic void setContentView(int layoutResID) {          if (mContentParent == null) {              installDecor();//1          } else {              mContentParent.removeAllViews();          }          mLayoutInflater.inflate(layoutResID, mContentParent);//2 将layoutResID填充,他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor);得到的          final Callback cb = getCallback();          if (cb != null && !isDestroyed()) {              cb.onContentChanged();          }  }   执行到installDecor() [java] view plaincopyprivate void installDecor() {      if (mDecor == null) {          mDecor = generateDecor();//生成装饰窗口,装饰窗口继承自FrameLayout          mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);          mDecor.setIsRootNamespace(true);          if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {              mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);          }      }      if (mContentParent == null) {          mContentParent = generateLayout(mDecor);// 产生布局,返回父布局,暂时这样理解,具体进去看代码          mDecor.makeOptionalFitsSystemWindows();          mTitleView = (TextView)findViewById(com.android.internal.R.id.title);          ......          }      }  }   generateLayout的代码如下 [java] view plaincopyprotected ViewGroup generateLayout(DecorView decor) {          // Apply data from current theme.            TypedArray a = getWindowStyle();// 获取当前设置的主题          ......          if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {              requestFeature(FEATURE_NO_TITLE);//可以看到平时在AndroidManifest配置的窗口等各其实在代码里都是在这里修改的          } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {              // Don't allow an action bar if there is no title.              requestFeature(FEATURE_ACTION_BAR);          }            if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {              requestFeature(FEATURE_ACTION_BAR_OVERLAY);          }      ......      //19-63行根据我们指定的有无标题等各种窗口风格得到对应的默认布局,      //这些布局在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout          int layoutResource;          int features = getLocalFeatures();          if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {              if (mIsFloating) {                  TypedValue res = new TypedValue();                  getContext().getTheme().resolveAttribute(                          com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);                  layoutResource = res.resourceId;              } else {                  layoutResource = com.android.internal.R.layout.screen_title_icons;              }              removeFeature(FEATURE_ACTION_BAR);          } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0                  && (features & (1 << FEATURE_ACTION_BAR)) == 0) {              layoutResource = com.android.internal.R.layout.screen_progress;          } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {              if (mIsFloating) {                  TypedValue res = new TypedValue();                  getContext().getTheme().resolveAttribute(                          com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);                  layoutResource = res.resourceId;              } else {                  layoutResource = com.android.internal.R.layout.screen_custom_title;              }              removeFeature(FEATURE_ACTION_BAR);          } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {              if (mIsFloating) {                  TypedValue res = new TypedValue();                  getContext().getTheme().resolveAttribute(                          com.android.internal.R.attr.dialogTitleDecorLayout, res, true);                  layoutResource = res.resourceId;              } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {                  if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {                      layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;                  } else {                      layoutResource = com.android.internal.R.layout.screen_action_bar;                  }              } else {                  layoutResource = com.android.internal.R.layout.screen_title;              }          } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {              layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;          } else {              layoutResource = com.android.internal.R.layout.screen_simple;          }            mDecor.startChanging();            View in = mLayoutInflater.inflate(layoutResource, null);//根据上面的判断选择的layoutResource填充成View          decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//调用装饰窗口的addView方法将上一步生成的View添加到最外层的装饰窗口                ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT对应的都是@android:id/content其实是一个FrameLayout          ......          mDecor.finishChanging();          return contentParent;      }   下面是最常用的layoutResource布局文件他们在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout文件夹下 Screen-title.xml [html] view plaincopy<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:orientation="vertical"      android:fitsSystemWindows="true">      <!-- Popout bar for action modes -->      <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" />      <FrameLayout          android:layout_width="match_parent"           android:layout_height="?android:attr/windowTitleSize"          style="?android:attr/windowTitleBackgroundStyle">          <TextView android:id="@android:id/title"               style="?android:attr/windowTitleStyle"              android:background="@null"              android:fadingEdge="horizontal"              android:gravity="center_vertical"              android:layout_width="match_parent"              android:layout_height="match_parent" />      </FrameLayout>      <FrameLayout android:id="@android:id/content"          android:layout_width="match_parent"           android:layout_height="0dip"          android:layout_weight="1"          android:foregroundGravity="fill_horizontal|top"          android:foreground="?android:attr/windowContentOverlay" />  </LinearLayout>   最外层是线性布局,包含帧布局(包含一个TextView其实就是标题)和帧布局。其实这个就是最常见的样子。默认生成的程序就是这个样子。 Screen-simple.xml [html] view plaincopy<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" />      <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>   显而易见这个就是全屏设置时默认加载的布局。 我们可以看到id为content的FrameLayout都存在的,因为他要装载我们填充的xml 依Screen-title.xml为例,大致是这样子的 通过以上分析明白了以下几点: 1. Activity呈现出来的界面其实是一个PhoneWindow类在管理的,这个类中有一个DecorView成员就是最外层的一个容器。 2. 上面这张图是非常重要的,显示了窗口的结构。 3. Activity到底是个什么东西?还真不好说清楚…^_^总之和刚开始的认识是不同的。 4. 遇到不懂得问题就去查看源码。代码是最好的老师!
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部