android2.3 View视图框架源码分析之一:android是如何创建一个view的?

View是所有控件的一个基类,无论是布局(Layout),还是控件(Widget)都是继承自View类。只不过layout是一个特殊的view,它里面创建一个view的数组可以包含其他的view而已。 
这一篇文章把所有的layout和widget都统称为view,那么android是如何创建一个view的呢? 


一。在代码中直接new出来。 
比如说你要创建一个TextView的实例,那么你可以这样写: 

Java代码  收藏代码
  1. TextView text = new TextView(c);  //c为context对象,表明textview是在此对象中运行的。  




二。把控件写在xml文件中然后通过LayoutInflater初始化一个view。
 
注意:下面的内容不是顺序的看的而是交替的看的。否则可能弄迷糊。 
可以通过 

Java代码  收藏代码
  1. //通过系统提供的实例获得一个LayoutInflater对象  
  2. LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  3. //第一个参数为xml文件中view的id,第二个参数为此view的父组件,可以为null,android会自动寻找它是否拥有父组件  
  4. View view = inflater.inflate(R.layout.resourceid, null);  


这样也得到了一个view的实例,让我们一步一步来看,这个view是怎么new出来的。 
看类android.view.LayoutInflater 

Java代码  收藏代码
  1.    public View inflate(int resource, ViewGroup root) {  
  2.        return inflate(resource, root, root != null);  
  3.    }  
  4.   
  5.   public View inflate(int resource, ViewGroup root, boolean attachToRoot) {  
  6.        /*可以看到通过resource id返回了一个XmlResourceParser,通过类名就可以猜测 
  7.        这是一个xml的解析类。但点进去一看,发现它只是一个接口,它继承自 XmlPullParser用于pull方式解析xml的接口。和AttributeSet用于获取此view的所有属性。 
  8.     那么需要能找到它的实现类。先看下面resource类。 
  9.        */  
  10.        XmlResourceParser parser = getContext().getResources().getLayout(resource);  
  11.        try {  
  12.            return inflate(parser, root, attachToRoot);  
  13.        } finally {  
  14.            parser.close();  
  15.        }  
  16.    }  
  17.   
  18.    /** 
  19.   * 终于到了重点,获取一个这个View的实例 
  20.   */  
  21. public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {  
  22.        synchronized (mConstructorArgs) {  
  23.         /** 
  24.           * 获取一个实现此AttributeSet的实例。因为此XmlPullParser是继承自AttributeSet 
  25.           * 的,所以parser对象可以直接作为一个AttributeSet对象。也可以用组合的方式 
  26.           * 把parser传递给另外一个实现自AttributeSet的对象,来获取一个AttributeSet实例。 
  27.         **/  
  28.            final AttributeSet attrs = Xml.asAttributeSet(parser);  
  29.            mConstructorArgs[0] = mContext;      //构造函数的参数,第一个值是此view运行所在的对象context  
  30.            View result = root;  
  31.   
  32.            try {  
  33.                // parser同时也继承了xmlPullParser,所以可以用pull解析来获取此view的根节点  
  34.                int type;  
  35.                while ((type = parser.next()) != XmlPullParser.START_TAG &&  
  36.                        type != XmlPullParser.END_DOCUMENT) {  
  37.                    // Empty  
  38.                }  
  39.   
  40.                if (type != XmlPullParser.START_TAG) {  
  41.                    throw new InflateException(parser.getPositionDescription()  
  42.                            + ": No start tag found!");  
  43.                }  
  44.                //获得根节点标签的名字  
  45.                final String name = parser.getName();  
  46.                  
  47.             //如果它的根节点是一个merge对象,则必须手动设置此view的父节点,否则抛出异常  
  48.             //因为由merge创建的xml文件,常常被其他layout所包含  
  49.                if (TAG_MERGE.equals(name)) {  
  50.                    if (root == null || !attachToRoot) {  
  51.                        throw new InflateException("<merge /> can be used only with a valid "  
  52.                                + "ViewGroup root and attachToRoot=true");  
  53.                    }  
  54.   
  55.                    rInflate(parser, root, attrs);  
  56.                } else {  
  57.                    // 此inflate的xml文件中的root view。即我们通过inflate返回得到的view  
  58.                    View temp = createViewFromTag(name, attrs);  
  59.   
  60.                    ViewGroup.LayoutParams params = null;  
  61.   
  62.                    if (root != null) {  
  63.                        // Create layout params that match root, if supplied  
  64.                        params = root.generateLayoutParams(attrs);  
  65.                        if (!attachToRoot) {  
  66.                            // Set the layout params for temp if we are not  
  67.                            // attaching. (If we are, we use addView, below)  
  68.                            temp.setLayoutParams(params);  
  69.                        }  
  70.                    }  
  71.   
  72.   
  73.                    // 加载temp下所有的子view  
  74.                    rInflate(parser, temp, attrs);  
  75.   
  76.   
  77.                    //如果给出了root,则把此view添加到root中去  
  78.                    if (root != null && attachToRoot) {  
  79.                        root.addView(temp, params);  
  80.                    }  
  81.   
  82.                    // Decide whether to return the root that was passed in or the  
  83.                    // top view found in xml.  
  84.                    if (root == null || !attachToRoot) {  
  85.                        result = temp;  
  86.                    }  
  87.                }  
  88.   
  89.            } catch (XmlPullParserException e) {  
  90.                InflateException ex = new InflateException(e.getMessage());  
  91.                ex.initCause(e);  
  92.                throw ex;  
  93.            } catch (IOException e) {  
  94.                InflateException ex = new InflateException(  
  95.                        parser.getPositionDescription()  
  96.                        + ": " + e.getMessage());  
  97.                ex.initCause(e);  
  98.                throw ex;  
  99.            }  
  100.   
  101.            return result;  
  102.        }  
  103.    }  
  104.   
  105.   
  106. /** 
  107.     *  
  108.  * 有上至下递归的初始化所有子view和子view的子view。在此方法被调用完成后 
  109.  * 会调用此view的parent view的onFinishInflate方法。表明其子view全部加载完毕 
  110.     */  
  111.    private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)  
  112.            throws XmlPullParserException, IOException {  
  113.   
  114.        final int depth = parser.getDepth();  
  115.        int type;  
  116.   
  117.        while (((type = parser.next()) != XmlPullParser.END_TAG ||  
  118.                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {  
  119.   
  120.            if (type != XmlPullParser.START_TAG) {  
  121.                continue;  
  122.            }  
  123.   
  124.            final String name = parser.getName();  
  125.              
  126.            if (TAG_REQUEST_FOCUS.equals(name)) {  
  127.                parseRequestFocus(parser, parent);  
  128.            } else if (TAG_INCLUDE.equals(name)) {  
  129.                if (parser.getDepth() == 0) {  
  130.                    throw new InflateException("<include /> cannot be the root element");  
  131.                }  
  132.                parseInclude(parser, parent, attrs);  
  133.            } else if (TAG_MERGE.equals(name)) {  
  134.                throw new InflateException("<merge /> must be the root element");  
  135.            } else {         //看这里,创建view的方法。而且这里已经重新获得了它的  
  136.                final View view = createViewFromTag(name, attrs);  
  137.                final ViewGroup viewGroup = (ViewGroup) parent;  
  138.                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);  
  139.                rInflate(parser, view, attrs);  
  140.                viewGroup.addView(view, params);  
  141.            }  
  142.        }  
  143.   
  144.        parent.onFinishInflate();  
  145.    }  
  146.   
  147.   
  148. View createViewFromTag(String name, AttributeSet attrs) {  
  149.        if (name.equals("view")) {  
  150.            name = attrs.getAttributeValue(null, "class");  
  151.        }  
  152.   
  153.        if (DEBUG) System.out.println("******** Creating view: " + name);  
  154.   
  155.        try {  
  156.            View view = (mFactory == null) ? null : mFactory.onCreateView(name,  
  157.                    mContext, attrs);  
  158.   
  159.            if (view == null) {  
  160.                if (-1 == name.indexOf('.')) {       //这里只是为了判断xml文件中tag的属性是否加了包名  
  161.                    view = onCreateView(name, attrs);  
  162.                } else {  
  163.                    view = createView(name, null, attrs);  
  164.                }  
  165.            }  
  166.   
  167.            if (DEBUG) System.out.println("Created view is: " + view);  
  168.            return view;  
  169.   
  170.        } catch (InflateException e) {  
  171.            throw e;  
  172.   
  173.        } catch (ClassNotFoundException e) {  
  174.            InflateException ie = new InflateException(attrs.getPositionDescription()  
  175.                    + ": Error inflating class " + name);  
  176.            ie.initCause(e);  
  177.            throw ie;  
  178.   
  179.        } catch (Exception e) {  
  180.            InflateException ie = new InflateException(attrs.getPositionDescription()  
  181.                    + ": Error inflating class " + name);  
  182.            ie.initCause(e);  
  183.            throw ie;  
  184.        }  
  185.    }  
  186.   
  187. /** 
  188. * 真正创建一个view的方法, 
  189. * 此方法是用反射获取构造器来实例对象而不是直接new出来这是为了处于性能优化考虑, 
  190. * 同一个类名的不同对象,可以直接得到缓存的构造器直接获取一个构造器对象实例。而不需要 
  191. * 重复进行new操作。 
  192.    * 
  193. * @param name 此View的全名 
  194. * @param prefix 前缀,值为 "android.view."其实就是是否包含包名 
  195. * @param attrs 此view的属性值,传递给此view的构造函数 
  196. */  
  197.  public final View createView(String name, String prefix, AttributeSet attrs)  
  198.            throws ClassNotFoundException, InflateException {  
  199.        Constructor constructor = sConstructorMap.get(name); //缓存中是否已经有了一个构造函数  
  200.        Class clazz = null;  
  201.   
  202.        try {  
  203.            if (constructor == null) {  
  204.                //通过类名获得一个class对象  
  205.                clazz = mContext.getClassLoader().loadClass(  
  206.                        prefix != null ? (prefix + name) : name);  
  207.                  
  208.                if (mFilter != null && clazz != null) {  
  209.                    boolean allowed = mFilter.onLoadClass(clazz);  
  210.                    if (!allowed) {  
  211.                        failNotAllowed(name, prefix, attrs);  
  212.                    }  
  213.                }  
  214.             //通过参数类型获得一个构造器,参数列表为context,attrs  
  215.                constructor = clazz.getConstructor(mConstructorSignature);  
  216.                sConstructorMap.put(name, constructor);      //把此构造器缓存起来  
  217.            } else {  
  218.                // If we have a filter, apply it to cached constructor  
  219.                if (mFilter != null) {  
  220.                    // Have we seen this name before?  
  221.                    Boolean allowedState = mFilterMap.get(name);  
  222.                    if (allowedState == null) {  
  223.                        // New class -- remember whether it is allowed  
  224.                        clazz = mContext.getClassLoader().loadClass(  
  225.                                prefix != null ? (prefix + name) : name);  
  226.                          
  227.                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);  
  228.                        mFilterMap.put(name, allowed);  
  229.                        if (!allowed) {  
  230.                            failNotAllowed(name, prefix, attrs);  
  231.                        }  
  232.                    } else if (allowedState.equals(Boolean.FALSE)) {  
  233.                        failNotAllowed(name, prefix, attrs);  
  234.                    }  
  235.                }  
  236.            }  
  237.   
  238.            Object[] args = mConstructorArgs;  
  239.            args[1] = attrs;     //args[0]已经在前面初始好了。这里只要初始化args[1]  
  240.            return (View) constructor.newInstance(args);     //通过反射new出一个对象。。大功告成  
  241.   
  242.        } catch (NoSuchMethodException e) {  
  243.            InflateException ie = new InflateException(attrs.getPositionDescription()  
  244.                    + ": Error inflating class "  
  245.                    + (prefix != null ? (prefix + name) : name));  
  246.            ie.initCause(e);  
  247.            throw ie;  
  248.   
  249.        } catch (ClassNotFoundException e) {  
  250.            // If loadClass fails, we should propagate the exception.  
  251.            throw e;  
  252.        } catch (Exception e) {  
  253.            InflateException ie = new InflateException(attrs.getPositionDescription()  
  254.                    + ": Error inflating class "  
  255.                    + (clazz == null ? "<unknown>" : clazz.getName()));  
  256.            ie.initCause(e);  
  257.            throw ie;  
  258.        }  
  259.    }  



在类android.content.res.Resources类中获取XmlResourceParser对象; 

Java代码  收藏代码
  1.  public XmlResourceParser getLayout(int id) throws NotFoundException {  
  2.      return loadXmlResourceParser(id, "layout");  
  3.  }  
  4.   
  5. ackage*/ XmlResourceParser loadXmlResourceParser(int id, String type)  
  6.          throws NotFoundException {  
  7.      synchronized (mTmpValue) {  
  8. /*TypedValue对象保存了一些有关resource 的数据值,比如说,对于一个view来说,在xml 
  9.     文件中可以定义许多属性,TypedValue保存了其中一个属性的相关信息,包括此属性的值的类型 
  10.     type,是boolean还是color还是reference还是String,这些在attr.xml文件下都有定义。 
  11.     它的值的字符串名称;一个属性有多个值时,它从xml文件中获取的值它的顺序data;如果此属性的值 
  12.     的类型是一个reference则保存它的resource id的等等。 
  13. */  
  14.          TypedValue value = mTmpValue;  
  15.          getValue(id, value, true);  
  16.          if (value.type == TypedValue.TYPE_STRING) {  
  17.              return loadXmlResourceParser(value.string.toString(), id,  
  18.                      value.assetCookie, type);  
  19.          }  
  20.          throw new NotFoundException(  
  21.                  "Resource ID #0x" + Integer.toHexString(id) + " type #0x"  
  22.                  + Integer.toHexString(value.type) + " is not valid");  
  23.      }  
  24.  }  
  25.   
  26.  /** 
  27.     *  getValue方法,id表示要查找的控件的 id,outValue是一个对象,用于保存一些属性相关信息 
  28.  *  resolveRefs为true表明,当通过属性id找到xml文件中的标签时,比如是一个<Button  android:id="@+id/button"/> 
  29.  * 它的值是一个引用reference,则继续解析获得这个id的值。这里看AssetManager类的实现*/  
  30.  public void getValue(int id, TypedValue outValue, boolean resolveRefs)  
  31.          throws NotFoundException {  
  32.      boolean found = mAssets.getResourceValue(id, outValue, resolveRefs);  
  33.      if (found) {  
  34.          return;  
  35.      }  
  36.      throw new NotFoundException("Resource ID #0x"  
  37.                                  + Integer.toHexString(id));  
  38.  }  
  39.    
  40.  /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,  
  41.          int assetCookie, String type) throws NotFoundException {  
  42.      if (id != 0) {  
  43.          try {  
  44.     //取缓存  
  45.              synchronized (mCachedXmlBlockIds) {  
  46.                  // First see if this block is in our cache.  
  47.                  final int num = mCachedXmlBlockIds.length;  
  48.                  for (int i=0; i<num; i++) {  
  49.                      if (mCachedXmlBlockIds[i] == id) {  
  50.                          //System.out.println("**** REUSING XML BLOCK!  id="  
  51.                          //                   + id + ", index=" + i);  
  52.                          return mCachedXmlBlocks[i].newParser();  
  53.                      }  
  54.                  }  
  55.   
  56.                 //第一次加载时,会打开这个文件获取一个xml数据块对象。  
  57.        // 这里先看AssetManager类的实现  
  58.                  XmlBlock block = mAssets.openXmlBlockAsset(  
  59.                          assetCookie, file);  
  60.   
  61.         //下面会把此xmlBlock对象缓存起来,保存id和block,  
  62.         //以后如果是同样的id,直接在缓存中取XmlBlock。  
  63.         //这样就不用再在本地方法中打开文件创建解析树了。  
  64.                  if (block != null) {    
  65.                      int pos = mLastCachedXmlBlockIndex+1;  
  66.                      if (pos >= num) pos = 0;  
  67.                      mLastCachedXmlBlockIndex = pos;  
  68.                      XmlBlock oldBlock = mCachedXmlBlocks[pos];  
  69.                      if (oldBlock != null) {  
  70.                          oldBlock.close();  
  71.                      }  
  72.                      mCachedXmlBlockIds[pos] = id;  
  73.                      mCachedXmlBlocks[pos] = block;  
  74.                      //返回的内部类继承了XmlResourceParser,在APi中此类是隐藏的  
  75.                      return block.newParser();  
  76.                  }  
  77.              }  
  78.          } catch (Exception e) {  
  79.              NotFoundException rnf = new NotFoundException(  
  80.                      "File " + file + " from xml type " + type + " resource ID #0x"  
  81.                      + Integer.toHexString(id));  
  82.              rnf.initCause(e);  
  83.              throw rnf;  
  84.          }  
  85.      }  
  86.   
  87.      throw new NotFoundException(  
  88.              "File " + file + " from xml type " + type + " resource ID #0x"  
  89.              + Integer.toHexString(id));  
  90.  }  



android.content.res.AssetManager类 

Java代码  收藏代码
  1.   /*package*/ final boolean getResourceValue(int ident,  
  2.                                              TypedValue outValue,  
  3.                                              boolean resolveRefs)  
  4.   {  
  5.       int block = loadResourceValue(ident, outValue, resolveRefs);  
  6.       if (block >= 0) {  
  7.           if (outValue.type != TypedValue.TYPE_STRING) {  
  8.               return true;  
  9.           }  
  10.     //mStringBlocks通过本地方法保存所有布局文件的文件名  
  11.           outValue.string = mStringBlocks[block].get(outValue.data);  
  12.           return true;  
  13.       }  
  14.       return false;  
  15.   }  
  16.   
  17.    //这是一个本地方法,是在本地方法中获取这个控件信息,返回通过此控件的id找到的文件名  
  18. //的位置,由于个人对c++不是很了解,只初略的解释本地方法的一些功能。  
  19. //对于的JNI文件位于:frameworksasecorejniandroid_util_AssetManager.cpp  
  20. private native final int loadResourceValue(int ident, TypedValue outValue,  
  21.                                              boolean resolve);  
  22.   
  23.   
  24.   
  25.   
  26. /** 
  27.  * 通过文件名,在本地方法中找到这个xml文件,并且在本地方法中生成一个xml解析对象。 
  28.  * 返回一个id,这个id对应java中的xmlBlock对象。这样xml文件就被load进了内存。 
  29.   * 也就是android所说的预编译,以后再访问只要直接去取数据即可 
  30.  */  
  31.    /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)  
  32.       throws IOException {  
  33.       synchronized (this) {  
  34.           if (!mOpen) {  
  35.               throw new RuntimeException("Assetmanager has been closed");  
  36.           }  
  37.           int xmlBlock = openXmlAssetNative(cookie, fileName);  
  38.           if (xmlBlock != 0) {  
  39.         /* 
  40.         * 在XmlBlock对象中,终于到找了实现XmlResourceParser接口的类 
  41.         * Parser,它是XmlBlock的一个内部类。这里面可以获取所有xml文件中的内容。 
  42.         * 不管是属性还是Tag标签。这里xmlBlock是用来与本地类中的解析树对象对应的。 
  43.         * 所有的解析方法,其实都是调用的本地xml解析树中的方法。所以此类中有大量的 
  44.         * 本地方法。 
  45.         */  
  46.               XmlBlock res = new XmlBlock(this, xmlBlock);  
  47.               incRefsLocked(res.hashCode());  
  48.               return res;  
  49.           }  
  50.       }  
  51.       throw new FileNotFoundException("Asset XML file: " + fileName);  
  52.   }  




三 。通过view.findViewById(resourceid)获得一个view的实例 
android.View.View类中 

Java代码  收藏代码
  1.    //调用了通过id检索view的方法  
  2.    public final View findViewById(int id) {  
  3.        if (id < 0) {  
  4.            return null;  
  5.        }  
  6.        return findViewTraversal(id);  
  7.    }  
  8.      
  9. //不是吧,这不是坑爹吗?猜想肯定是被viewgroup重写了  
  10. protected View findViewTraversal(int id) {  
  11.        if (id == mID) {  
  12.            return this;  
  13.        }  
  14.        return null;  
  15.    }  



android.View.ViewGroup类中 

Java代码  收藏代码
  1. //哈哈,果然重写了此方法。其实就是在viewgroup包含的  
  2. //子view数组中进行遍历。那么view是什么时候被加入进  
  3. //viewgroup中的呢?如果是在代码中写,肯定是直接使用  
  4. //addView方法把view加入viewGroup。如果写在xml布局文件  
  5. //中,其实是在第二种方法中被加入view的。inflate加载父view  
  6. //时会同时把其所有的子view加载完,同时addView到父view中  
  7.  protected View findViewTraversal(int id) {  
  8.         if (id == mID) {  
  9.             return this;  
  10.         }  
  11.   
  12.         final View[] where = mChildren;  
  13.         final int len = mChildrenCount;  
  14.   
  15.         for (int i = 0; i < len; i++) {  
  16.             View v = where[i];  
  17.   
  18.             if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {  
  19.                 v = v.findViewById(id);  
  20.   
  21.                 if (v != null) {  
  22.                     return v;  
  23.                 }  
  24.             }  
  25.         }  
  26.   
  27.         return null;  
  28.     }  




四。通过activity的setContentView方法和findViewById获取一个view的实例。 
它是通过 
getWindow().setContentView(layoutResID);设置window对象的view 
再来看看window对象是在哪里获得到的,在类Activity中找到 
mWindow = PolicyManager.makeNewWindow(this); 
它是由PolicyManager生成的。 
找到com.android.internal.policy.PolicyManager,找到方法 

Java代码  收藏代码
  1.      
  2.   
  3. //window是由sPolicy对象创建的  
  4.    public static Window makeNewWindow(Context context) {  
  5.        return sPolicy.makeNewWindow(context);  
  6.    }  
  7.   
  8.    //sPolicy对象是通过反射,获取的一个实例  
  9. //此类的实现在com.android.internal.policy.impl.Policy中  
  10.    private static final String POLICY_IMPL_CLASS_NAME =  
  11.        "com.android.internal.policy.impl.Policy";  
  12.   
  13.    private static final IPolicy sPolicy;  
  14.   
  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.    }  




找到com.android.internal.policy.impl.Policy类 

Java代码  收藏代码
  1. public PhoneWindow makeNewWindow(Context context) {  
  2.     return new PhoneWindow(context);  
  3. }  




它其实是一个phoneWindow对象,继承自window对象 
找到com.android.internal.policy.impl.phoneWindow 看它内部是如何把resourceid加载成一个view的 

Java代码  收藏代码
    1. private ViewGroup mContentParent;  
    2. //这是window的顶层视图,它包含一些窗口的装饰,比图title bar,状态栏等等  
    3.    private DecorView mDecor;  
    4.   
    5.    //这里的layoutResID也是由mLayoutInflater进行加载的,加载的方式与第二种方法一样。  
    6. //只不过这里把的到的view变成了mContentParent的子view  
    7.   @Override  
    8.    public void setContentView(int layoutResID) {  
    9.        if (mContentParent == null) {  
    10.            installDecor();  
    11.        } else {  
    12.            mContentParent.removeAllViews();  
    13.        }  
    14.        mLayoutInflater.inflate(layoutResID, mContentParent);  
    15.        final Callback cb = getCallback();  
    16.        if (cb != null) {        //这是回调方法,表明mContentParent的子view已经发生改变  
    17.            cb.onContentChanged();  
    18.        }  
    19.    }  
    20.   
    21.    //再来看看mContentParent究竟是何物,它肯定是一个viewGroup  
    22. private void installDecor() {  
    23.        if (mDecor == null) {  
    24.            mDecor = generateDecor();  
    25.            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  
    26.            mDecor.setIsRootNamespace(true);  
    27.        }  
    28.        if (mContentParent == null) {  
    29.            mContentParent = generateLayout(mDecor);  
    30.   
    31.            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
    32.            if (mTitleView != null) {        //这里设置的是是否隐藏titleContainer,即头部titlebar  
    33.                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  
    34.                    View titleContainer = findViewById(com.android.internal.R.id.title_container);  
    35.                    if (titleContainer != null) {  
    36.                        titleContainer.setVisibility(View.GONE);  
    37.                    } else {  
    38.                        mTitleView.setVisibility(View.GONE);  
    39.                    }  
    40.                    if (mContentParent instanceof FrameLayout) {  
    41.                        ((FrameLayout)mContentParent).setForeground(null);  
    42.                    }  
    43.                } else {  
    44.                    mTitleView.setText(mTitle);  
    45.                }  
    46.            }  
    47.        }  
    48.    }  
    49.    
    50.  //当顶层view为null是,new了一个DecorView  
    51.  protected DecorView generateDecor() {  
    52.        return new DecorView(getContext(), -1);  
    53.    }  
    54.   
    55.    //这里生成了mContentParent。  
    56. protected ViewGroup generateLayout(DecorView decor) {  
    57.    
    58.   
    59.   
    60.        mDecor.startChanging();  
    61.        //根据window的不同参数选择layoutResource  
    62.        View in = mLayoutInflater.inflate(layoutResource, null);  
    63.        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  
    64.   
    65.        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
    66.        if (contentParent == null) {  
    67.            throw new RuntimeException("Window couldn't find content container view");  
    68.        }  
    69.        return contentParent;  
    70.    }  
    71.   
    72.    //顶层view是一个framelayout  
    73. private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {  
    74.          public DecorView(Context context, int featureId) {  
    75.             super(context);  
    76.             mFeatureId = featureId;  
    77.        }  
    78. }  
    79.   
    80.   
    81.   
    82. //下面说明findVIewById  
    83.   
    84. //首先是获取顶层view,即继承自FrameLayout的viewgorup  
    85. @Override  
    86.    public final View getDecorView() {  
    87.        if (mDecor == null) {  
    88.            installDecor();  
    89.        }  
    90.        return mDecor;  
    91.    }  
    92.   
    93. //然后mDecor.findViewById根据id获取它的子view  
    94. //这里就是通过第三种方法获取它的子view  
原文地址:https://www.cnblogs.com/Free-Thinker/p/4048551.html