• View源码分析如何创建






                本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!



    文/jj120522



    博主导读:View是Android中最重要的控件,几乎所有的控件都与View相关,所以从View开始研究是最合适不过的!



    View是所有控件的一个基类,无论是布局(Layout),还是控件(Widget)都是继承自View类。只不过layout是一个特殊的view,它里面创建一个view的数组可以包含其他的view而已。 

    这一篇文章把所有的layout和widget都统称为view,那么android是如何创建一个view的呢? 

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

    [java] view plaincopy
    1. TextView text = new TextView(c);  //c为context对象,表明textview是在此对象中运行的。  

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

    [java] view plaincopy
    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] view plaincopy
    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] view plaincopy
    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] view plaincopy
    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] view plaincopy
    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] view plaincopy
    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] view plaincopy
    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] view plaincopy
    1. public PhoneWindow makeNewWindow(Context context) {  
    2.     return new PhoneWindow(context);  
    3. }  
    它其实是一个phoneWindow对象,继承自window对象 
    找到com.android.internal.policy.impl.phoneWindow 看它内部是如何把resourceid加载成一个view的 

    [java] view plaincopy
    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  




  • 相关阅读:
    SQL Server事务、视图和索引
    软件系统的分层开发
    OOP应用:实体类
    Oracle/MySql/SQL Sqlserver分页查询
    数据库连接语句
    SQL连接查询
    MySQL基本手册
    C# 其他
    numpy的loadtxt()用法
    Pytorch从一个输入目录中加载所有的PNG图像,并将它们存储在张量中
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6147368.html
Copyright © 2020-2023  润新知