关闭

Activity 中view 的布局

71人阅读 评论(0) 收藏 举报
分类:
       android 中view 代表一块图形区域。每一个view 都与一个依赖一个父view。在每一个Activity 中有一个Window  代表一个屏幕,包含一个View 树和窗口的layout 参数。View tree的root View可以通过getDecorView得到。 在 new 一个activity 时,通常把APK的view和布局通过setContentView(R.layout.activity_main)设置到View树中。  
      一.在activity中有一个:  private Window mWindow; 看一看这个mWindow是怎么来的:在Activity 的attach函数中:

点击(此处)折叠或打开

  1. mWindow = PolicyManager.makeNewWindow(this);
追踪一下这个函数,看一下调用流程:
./frameworks/base/core/java/com/android/internal/policy/PolicyManager.java:

点击(此处)折叠或打开

  1. package com.android.internal.policy;

  2. import android.content.Context;
  3. import android.view.FallbackEventHandler;
  4. import android.view.LayoutInflater;
  5. import android.view.Window;
  6. import android.view.WindowManagerPolicy;

  7. import com.android.internal.policy.IPolicy;

  8. /**
  9.  * {@hide}
  10.  */

  11. public final class PolicyManager {
  12.     private static final String POLICY_IMPL_CLASS_NAME =
  13.         "com.android.internal.policy.impl.Policy";

  14.     private static final IPolicy sPolicy;

  15.     static {
  16.         // Pull in the actual implementation of the policy at run-time
  17.         try {
  18.             Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
  19.             sPolicy = (IPolicy)policyClass.newInstance();
  20.         } catch (ClassNotFoundException ex) {
  21.             throw new RuntimeException(
  22.                     POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
  23.         } catch (InstantiationException ex) {
  24.             throw new RuntimeException(
  25.                     POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
  26.         } catch (IllegalAccessException ex) {
  27.             throw new RuntimeException(
  28.                     POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
  29.         }
  30.     }

  31.     // Cannot instantiate this class
  32.     private PolicyManager() {}

  33.     // The static methods to spawn new policy-specific objects
  34.     public static Window makeNewWindow(Context context) {
  35.         return sPolicy.makeNewWindow(context);
  36.     }

  37.     public static LayoutInflater makeNewLayoutInflater(Context context) {
  38.         return sPolicy.makeNewLayoutInflater(context);
  39.     }

  40.     public static WindowManagerPolicy makeNewWindowManager() {
  41.         return sPolicy.makeNewWindowManager();
  42.     }

  43.     public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
  44.         return sPolicy.makeNewFallbackEventHandler(context);
  45.     }
  46. }
这是一个 final 类,通过反射的方法获取一个com.android.internal.policy.impl.Policy类的静态对象:sPolicy,调用这个类的makeNewWindow
./frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java

点击(此处)折叠或打开

  1. package com.android.internal.policy.impl;

  2. import android.content.Context;
  3. import android.util.Log;
  4. import android.view.FallbackEventHandler;
  5. import android.view.LayoutInflater;
  6. import android.view.Window;
  7. import android.view.WindowManagerPolicy;

  8. import com.android.internal.policy.IPolicy;
  9. import com.android.internal.policy.impl.PhoneLayoutInflater;
  10. import com.android.internal.policy.impl.PhoneWindow;
  11. import com.android.internal.policy.impl.PhoneWindowManager;

  12. /**
  13.  * {@hide}
  14.  */

  15. // Simple implementation of the policy interface that spawns the right
  16. // set of objects
  17. public class Policy implements IPolicy {
  18.     private static final String TAG = "PhonePolicy";

  19.     private static final String[] preload_classes = {
  20.         "com.android.internal.policy.impl.PhoneLayoutInflater",
  21.         "com.android.internal.policy.impl.PhoneWindow",
  22.         "com.android.internal.policy.impl.PhoneWindow$1",
  23.         "com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback",
  24.         "com.android.internal.policy.impl.PhoneWindow$DecorView",
  25.         "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
  26.         "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
  27.     };

  28.     static {
  29.         // For performance reasons, preload some policy specific classes when
  30.         // the policy gets loaded.
  31.         for (String s : preload_classes) {
  32.             try {
  33.                 Class.forName(s);
  34.             } catch (ClassNotFoundException ex) {
  35.                 Log.e(TAG, "Could not preload class for phone policy: " + s);
  36.             }
  37.         }
  38.     }

  39.     public Window makeNewWindow(Context context) {
  40.         return new PhoneWindow(context);
  41.     }

  42.     public LayoutInflater makeNewLayoutInflater(Context context) {
  43.         return new PhoneLayoutInflater(context);
  44.     }

  45.     public WindowManagerPolicy makeNewWindowManager() {
  46.         return new PhoneWindowManager();
  47.     }

  48.     public FallbackEventHandler makeNewFallbackEventHandler(Context context) {
  49.         return new PhoneFallbackEventHandler(context);
  50.     }
  51. }
在这个类中返回了new PhoneWindow(context)
./frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java  现在我们找到了这个Window 对象。Window 的庐山真面目:

点击(此处)折叠或打开

  1. public class PhoneWindow extends Window implements MenuBuilder.Callback {
继承自 Window。window 的关系搞清楚了。
      二.在Activity中通过mWindow.getDecorView() 获取activity view树的布局。又来到了window 中。

点击(此处)折叠或打开

  1. public final View getDecorView() {
  2.         if (mDecor == null) {
  3.             installDecor();
  4.         }
  5.         return mDecor;
  6.     }
DecorView 是PhoneWindow的一个内部类:

点击(此处)折叠或打开

  1. private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
其实就是一个FrameLayout 布局,FrameLayout extend  ViewGroup。所有Activity 中的view 都添加到这个布局中。installDecor 函数:

点击(此处)折叠或打开

  1. private void installDecor() {
  2.         if (mDecor == null) {
  3.             mDecor = generateDecor();
  4.             mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
  5.             mDecor.setIsRootNamespace(true);
  6.             if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
  7.                 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
  8.             }
  9.         }
  10.         if (mContentParent == null) {
  11.             mContentParent = generateLayout(mDecor);
  12. ......
  13. }
    

点击(此处)折叠或打开

  1. protected DecorView generateDecor() {
  2.         return new DecorView(getContext(), -1);
  3.     }
      
这就是在PhoneWindow 的mDecorView 。需要注意一下mContentParent 这个变量。下面会分析到。
总结一下在一个Activity 中,有一个PhoneWindwo 对象,这个对象有有一个内部类Decorview ,Decorview extend  FrameLayout extend  ViewGroup 。这个Activity 的 view 都保存在这里,形成一个View 树。

      三.
Activity 的 setContentView:

点击(此处)折叠或打开

  1. public void setContentView(int layoutResID) {
  2.         getWindow().setContentView(layoutResID);
  3.         initActionBar();
  4.     }
PhoneWindow 的setContentView:

点击(此处)折叠或打开

  1. public void setContentView(int layoutResID) {
  2.         if (mContentParent == null) {
  3.             installDecor();
  4.         } else {
  5.             mContentParent.removeAllViews();
  6.         }
  7.         mLayoutInflater.inflate(layoutResID, mContentParent);
  8.         final Callback cb = getCallback();
  9.         if (cb != null && !isDestroyed()) {
  10.             cb.onContentChanged();
  11.         }
  12.     }
 mLayoutInflater.inflate(layoutResID, mContentParent); 在这行代码中,view 被设置进了mContentParent 这个父View中,在 PhoneWindow 中:
private ViewGroup  mContentParent

在刚才分析DecorView的时候, 在installDecor中会调用  
mContentParent = generateLayout(mDecor), 生成mContentParent;看一下实现:

点击(此处)折叠或打开

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

  4.         // Inflate the window decor.

  5.         int layoutResource;
  6.         int features = getLocalFeatures();
  7.         System.out.println("Features: 0x" + Integer.toHexString(features));
  8.         if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
  9.             if (mIsFloating) {
  10.                 TypedValue res = new TypedValue();
  11.                 getContext().getTheme().resolveAttribute(
  12.                         com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
  13.                 layoutResource = res.resourceId;
  14.             } else {
  15.                 layoutResource = com.android.internal.R.layout.screen_title_icons;
  16.                 System.out.println("layout.screen.title.icons");
  17.             }
  18.             // XXX Remove this once action bar supports these features.
  19.             removeFeature(FEATURE_ACTION_BAR);
  20.             // System.out.println("Title Icons!");
  21.         } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
  22.                 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
  23.             // Special case for a window with only a progress bar (and title).
  24.             // XXX Need to have a no-title version of embedded windows.
  25.             layoutResource = com.android.internal.R.layout.screen_progress;
  26.             // System.out.println("Progress!");
  27.         } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
  28.             // Special case for a window with a custom title.
  29.             // If the window is floating, we need a dialog layout
  30.             if (mIsFloating) {
  31.                 TypedValue res = new TypedValue();
  32.                 getContext().getTheme().resolveAttribute(
  33.                         com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
  34.                 layoutResource = res.resourceId;
  35.             } else {
  36.                 layoutResource = com.android.internal.R.layout.screen_custom_title;
  37.             }
  38.             // XXX Remove this once action bar supports these features.
  39.             removeFeature(FEATURE_ACTION_BAR);
  40.         } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
  41.             // If no other features and not embedded, only need a title.
  42.             // If the window is floating, we need a dialog layout
  43.             if (mIsFloating) {
  44.                 TypedValue res = new TypedValue();
  45.                 getContext().getTheme().resolveAttribute(
  46.                         com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
  47.                 layoutResource = res.resourceId;
  48.             } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
  49.                 layoutResource = com.android.internal.R.layout.screen_action_bar;
  50.             } else {
  51.                 layoutResource = com.android.internal.R.layout.screen_title;
  52.             }
  53.             // System.out.println("Title!");
  54.         } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
  55.             layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
  56.         } else {
  57.             // Embedded, so no decoration is needed.
  58.             layoutResource = com.android.internal.R.layout.screen_simple;
  59.             // System.out.println("Simple!");
  60.         }

  61.         mDecor.startChanging();

  62.         View in = mLayoutInflater.inflate(layoutResource, null);
  63.         decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

  64.         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  65.         if (contentParent == null) {
  66.             throw new RuntimeException("Window couldn't find content container view");
  67.         }

  68.         if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
  69.             ProgressBar progress = getCircularProgressBar(false);
  70.             if (progress != null) {
  71.                 progress.setIndeterminate(true);
  72.             }
  73.         }

  74.         // Remaining setup -- of background and title -- that only applies
  75.         // to top-level windows.
  76.         if (getContainer() == null) {
  77.             Drawable drawable = mBackgroundDrawable;
  78.             if (mBackgroundResource != 0) {
  79.                 drawable = getContext().getResources().getDrawable(mBackgroundResource);
  80.             }
  81.             mDecor.setWindowBackground(drawable);
  82.             drawable = null;
  83.             if (mFrameResource != 0) {
  84.                 drawable = getContext().getResources().getDrawable(mFrameResource);
  85.             }
  86.             mDecor.setWindowFrame(drawable);

  87.             // System.out.println("Text=" + Integer.toHexString(mTextColor) +
  88.             // " Sel=" + Integer.toHexString(mTextSelectedColor) +
  89.             // " Title=" + Integer.toHexString(mTitleColor));

  90.             if (mTitleColor == 0) {
  91.                 mTitleColor = mTextColor;
  92.             }

  93.             if (mTitle != null) {
  94.                 setTitle(mTitle);
  95.             }
  96.             setTitleColor(mTitleColor);
  97.         }

  98.         mDecor.finishChanging();

  99.         return contentParent;
  100.     }
一开始获取一些窗口的属性,然后根据这些属性确定 layoutResource 的值,layoutResource 为com.android.internal.R.layout.screen_开头;就是 framework-res.apk里面的资源:src/android$ ls frameworks/base/core/res/res/layout/screen*
frameworks/base/core/res/res/layout/screen_action_bar.xml                  frameworks/base/core/res/res/layout/screen_simple.xml
frameworks/base/core/res/res/layout/screen_custom_title.xml                frameworks/base/core/res/res/layout/screen_title_icons.xml
frameworks/base/core/res/res/layout/screen_progress.xml                    frameworks/base/core/res/res/layout/screen_title.xml
frameworks/base/core/res/res/layout/screen_simple_overlay_action_mode.xml  frameworks/base/core/res/res/layout/screen.xml

然后解析:
  1. View in = mLayoutInflater.inflate(layoutResource, null);
  2.         decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

  3.         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  4.         if (contentParent == null) {
  5.             throw new RuntimeException("Window couldn't find content container view");
  6.         }
contentParent 就是这些xml 文件里的一个ID_ANDROID_CONTENT 的view 。查找 ID_ANDROID_CONTENT定义:
frameworks/base/core/java/android/view/Window.java 124行:    
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
打开 screen.xml 文件:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     android:layout_width="match_parent"
  3.     android:layout_height="match_parent"
  4.     android:fitsSystemWindows="true"
  5.     android:orientation="vertical">
  6.     <ViewStub android:id="@+id/action_mode_bar_stub"
  7.               android:inflatedId="@+id/action_mode_bar"
  8.               android:layout="@layout/action_mode_bar"
  9.               android:layout_width="match_parent"
  10.               android:layout_height="wrap_content" />
  11.     <FrameLayout
  12.          android:id="@android:id/content"
  13.          android:layout_width="match_parent"
  14.          android:layout_height="match_parent"
  15.          android:foregroundInsidePadding="false"
  16.          android:foregroundGravity="fill_horizontal|top"
  17.          android:foreground="?android:attr/windowContentOverlay" />
  18. </LinearLayout>
content 是一个FrameLayout的布局,在这几个screen_*.xml文件中 都有一个id为:content的View。也就是说 mDecorView 是一个Activity的根布局,contentParent是mDecorView 一个子布局,提供给程序员的View的根布局。setContentView 的时候以contentParent为父节点。

四.
 Activity的默认布局确定了,可以确定一下,修改xml 布局:

  1. diff --git a/base/core/res/res/layout/screen_simple.xml b/base/core/res/res/layout/screen_simple.xml
  2. index c1914e7..0b7f6e2 100644
  3. --- a/base/core/res/res/layout/screen_simple.xml
  4. +++ b/base/core/res/res/layout/screen_simple.xml
  5. @@ -25,7 +25,8 @@ enabled.
  6.      android:layout_width="match_parent"
  7.      android:layout_height="match_parent"
  8.      android:fitsSystemWindows="true"
  9. - android:orientation="vertical">
  10. + android:orientation="vertical"
  11. + android:background="@android:color/holo_orange_dark">
  12.      <ViewStub android:id="@+id/action_mode_bar_stub"
  13.                android:inflatedId="@+id/action_mode_bar"
  14.                android:layout="@layout/action_mode_bar"
  15. @@ -37,5 +38,6 @@ enabled.
  16.           android:layout_height="match_parent"
  17.           android:foregroundInsidePadding="false"
  18.           android:foregroundGravity="fill_horizontal|top"
  19. - android:foreground="?android:attr/windowContentOverlay" />
  20. + android:foreground="?android:attr/windowContentOverlay"
  21. + android:background="@android:color/holo_green_dark"/>
  22.  </LinearLayout>
当我们进入桌面的时候,桌面效果如下:
homeshell.png
进入HelloWorld,没有任何变化。分析generateLayout的时候已经看到会根据窗口的属性选择不同的资源文件。桌面和一般的Activity使用了不同的资源配置。
修改screen_action_bar.xml

点击(此处)折叠或打开

  1. diff --git a/base/core/res/res/layout/screen_action_bar.xml b/base/core/res/res/layout/screen_action_bar.xml
  2. index f0b2313..62b34f6 100644
  3. --- a/base/core/res/res/layout/screen_action_bar.xml
  4. +++ b/base/core/res/res/layout/screen_action_bar.xml
  5. @@ -23,33 +23,39 @@ This is an optimized layout for a screen with the Action Bar enabled.
  6.      android:layout_height="match_parent"
  7.      android:orientation="vertical"
  8.      android:fitsSystemWindows="true"
  9. - android:splitMotionEvents="false">
  10. + android:splitMotionEvents="false"
  11. + android:background="@android:color/holo_red_dark">
  12.      <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
  13.          android:layout_width="match_parent"
  14.          android:layout_height="wrap_content"
  15. - style="?android:attr/actionBarStyle">
  16. + style="?android:attr/actionBarStyle"
  17. + android:background="@android:color/holo_green_dark">
  18.          <com.android.internal.widget.ActionBarView
  19.              android:id="@+id/action_bar"
  20.              android:layout_width="match_parent"
  21.              android:layout_height="wrap_content"
  22. - style="?android:attr/actionBarStyle" />
  23. + style="?android:attr/actionBarStyle"
  24. + android:background="@android:color/holo_green_light"/>
  25.          <com.android.internal.widget.ActionBarContextView
  26.              android:id="@+id/action_context_bar"
  27.              android:layout_width="match_parent"
  28.              android:layout_height="wrap_content"
  29.              android:visibility="gone"
  30. - style="?android:attr/actionModeStyle" />
  31. + style="?android:attr/actionModeStyle"
  32. + android:background="@android:color/white"/>
  33.      </com.android.internal.widget.ActionBarContainer>
  34.      <FrameLayout android:id="@android:id/content"
  35.          android:layout_width="match_parent"
  36.          android:layout_height="0dip"
  37.          android:layout_weight="1"
  38.          android:foregroundGravity="fill_horizontal|top"
  39. - android:foreground="?android:attr/windowContentOverlay" />
  40. + android:foreground="?android:attr/windowContentOverlay"
  41. + android:background="@android:color/holo_orange_light"/>
  42.      <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
  43.                    android:layout_width="match_parent"
  44.                    android:layout_height="wrap_content"
  45.                    style="?android:attr/actionBarSplitStyle"
  46.                    android:visibility="gone"
  47. - android:gravity="center"/>
  48. + android:gravity="center"
  49. + android:background="@android:color/holo_blue_light"/>
  50.  </LinearLayout>
HelloWorld 的效果:
 activity.png
从图中可以看到根布局是一个FrameLayout布局,就是mDecorView.DecorView extend  FrameLayout。 被选中的区域为FrameLayout,就是mcontentParent,mcontentParent中有一个RelativeLayout,是我们setContentView 的R.layout.activity_main 布局文件。



<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(2053) | 评论(0) | 转发(0) |
0

上一篇:Android ListActivity学习

下一篇:TextView 滚动

给主人留下些什么吧!~~
评论热议
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:33129次
    • 积分:404
    • 等级:
    • 排名:千里之外
    • 原创:90篇
    • 转载:10篇
    • 译文:0篇
    • 评论:0条
    文章分类