Android View视图------Android如何创建一个view。

38 阅读10分钟
  1.                }  

  2.            } catch (XmlPullParserException e) {  

  3.                InflateException ex = new InflateException(e.getMessage());  

  4.                ex.initCause(e);  

  5.                throw ex;  

  6.            } catch (IOException e) {  

  7.                InflateException ex = new InflateException(  

  8.                        parser.getPositionDescription()  

  9.                        + ": " + e.getMessage());  

  10.                ex.initCause(e);  

  11.                throw ex;  

  12.            }  

  13.            return result;  

  14.        }  

  15.    }  

  16. /** 

  17.     *  

  18.  * 有上至下递归的初始化所有子view和子view的子view。在此方法被调用完成后 

  19.  * 会调用此view的parent view的onFinishInflate方法。表明其子view全部加载完毕 

  20.     */  

  21.    private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)  

  22.            throws XmlPullParserException, IOException {  

  23.        final int depth = parser.getDepth();  

  24.        int type;  

  25.        while (((type = parser.next()) != XmlPullParser.END_TAG ||  

  26.                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {  

  27.            if (type != XmlPullParser.START_TAG) {  

  28.                continue;  

  29.            }  

  30.            final String name = parser.getName();  

  31.            if (TAG_REQUEST_FOCUS.equals(name)) {  

  32.                parseRequestFocus(parser, parent);  

  33.            } else if (TAG_INCLUDE.equals(name)) {  

  34.                if (parser.getDepth() == 0) {  

  35.                    throw new InflateException("<include /> cannot be the root element");  

  36.                }  

  37.                parseInclude(parser, parent, attrs);  

  38.            } else if (TAG_MERGE.equals(name)) {  

  39.                throw new InflateException("<merge /> must be the root element");  

  40.            } else {         //看这里,创建view的方法。而且这里已经重新获得了它的  

  41.                final View view = createViewFromTag(name, attrs);  

  42.                final ViewGroup viewGroup = (ViewGroup) parent;  

  43.                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);  

  44.                rInflate(parser, view, attrs);  

  45.                viewGroup.addView(view, params);  

  46.            }  

  47.        }  

  48.        parent.onFinishInflate();  

  49.    }  

  50. View createViewFromTag(String name, AttributeSet attrs) {  

  51.        if (name.equals("view")) {  

  52.            name = attrs.getAttributeValue(null, "class");  

  53.        }  

  54.        if (DEBUG) System.out.println("******** Creating view: " + name);  

  55.        try {  

  56.            View view = (mFactory == null) ? null : mFactory.onCreateView(name,  

  57.                    mContext, attrs);  

  58.            if (view == null) {  

  59.                if (-1 == name.indexOf('.')) {       //这里只是为了判断xml文件中tag的属性是否加了包名  

  60.                    view = onCreateView(name, attrs);  

  61.                } else {  

  62.                    view = createView(name, null, attrs);  

  63.                }  

  64.            }  

  65.            if (DEBUG) System.out.println("Created view is: " + view);  

  66.            return view;  

  67.        } catch (InflateException e) {  

  68.            throw e;  

  69.        } catch (ClassNotFoundException e) {  

  70.            InflateException ie = new InflateException(attrs.getPositionDescription()  

  71.                    + ": Error inflating class " + name);  

  72.            ie.initCause(e);  

  73.            throw ie;  

  74.        } catch (Exception e) {  

  75.            InflateException ie = new InflateException(attrs.getPositionDescription()  

  76.                    + ": Error inflating class " + name);  

  77.            ie.initCause(e);  

  78.            throw ie;  

  79.        }  

  80.    }  

  81. /** 

  82. * 真正创建一个view的方法, 

  83. * 此方法是用反射获取构造器来实例对象而不是直接new出来这是为了处于性能优化考虑, 

  84. * 同一个类名的不同对象,可以直接得到缓存的构造器直接获取一个构造器对象实例。而不需要 

  85. * 重复进行new操作。 

  86.    * 

  87. * @param name 此View的全名 

  88. * @param prefix 前缀,值为 "android.view."其实就是是否包含包名 

  89. * @param attrs 此view的属性值,传递给此view的构造函数 

  90. */  

  91.  public final View createView(String name, String prefix, AttributeSet attrs)  

  92.            throws ClassNotFoundException, InflateException {  

  93.        Constructor constructor = sConstructorMap.get(name); //缓存中是否已经有了一个构造函数  

  94.        Class clazz = null;  

  95.        try {  

  96.            if (constructor == null) {  

  97.                //通过类名获得一个class对象  

  98.                clazz = mContext.getClassLoader().loadClass(  

  99.                        prefix != null ? (prefix + name) : name);  

  100.                if (mFilter != null && clazz != null) {  

  101.                    boolean allowed = mFilter.onLoadClass(clazz);  

  102.                    if (!allowed) {  

  103.                        failNotAllowed(name, prefix, attrs);  

  104.                    }  

  105.                }  

  106.             //通过参数类型获得一个构造器,参数列表为context,attrs  

  107.                constructor = clazz.getConstructor(mConstructorSignature);  

  108.                sConstructorMap.put(name, constructor);      //把此构造器缓存起来  

  109.            } else {  

  110.                // If we have a filter, apply it to cached constructor  

  111.                if (mFilter != null) {  

  112.                    // Have we seen this name before?  

  113.                    Boolean allowedState = mFilterMap.get(name);  

  114.                    if (allowedState == null) {  

  115.                        // New class -- remember whether it is allowed  

  116.                        clazz = mContext.getClassLoader().loadClass(  

  117.                                prefix != null ? (prefix + name) : name);  

  118.                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);  

  119.                        mFilterMap.put(name, allowed);  

  120.                        if (!allowed) {  

  121.                            failNotAllowed(name, prefix, attrs);  

  122.                        }  

  123.                    } else if (allowedState.equals(Boolean.FALSE)) {  

  124.                        failNotAllowed(name, prefix, attrs);  

  125.                    }  

  126.                }  

  127.            }  

  128.            Object[] args = mConstructorArgs;  

  129.            args[1] = attrs;     //args[0]已经在前面初始好了。这里只要初始化args[1]  

  130.            return (View) constructor.newInstance(args);     //通过反射new出一个对象。。大功告成  

  131.        } catch (NoSuchMethodException e) {  

  132.            InflateException ie = new InflateException(attrs.getPositionDescription()  

  133.                    + ": Error inflating class "  

  134.                    + (prefix != null ? (prefix + name) : name));  

  135.            ie.initCause(e);  

  136.            throw ie;  

  137.        } catch (ClassNotFoundException e) {  

  138.            // If loadClass fails, we should propagate the exception.  

  139.            throw e;  

  140.        } catch (Exception e) {  

  141.            InflateException ie = new InflateException(attrs.getPositionDescription()  

  142.                    + ": Error inflating class "  

  143.                    + (clazz == null ? "" : clazz.getName()));  

  144.            ie.initCause(e);  

  145.            throw ie;  

  146.        }  

  147.    }  

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

Java代码   收藏代码

  1.  public XmlResourceParser getLayout(int id) throws NotFoundException {  

  2.      return loadXmlResourceParser(id, "layout");  

  3.  }  

  4. ackage*/ XmlResourceParser loadXmlResourceParser(int id, String type)  

  5.          throws NotFoundException {  

  6.      synchronized (mTmpValue) {  

  7. /*TypedValue对象保存了一些有关resource 的数据值,比如说,对于一个view来说,在xml 

  8.     文件中可以定义许多属性,TypedValue保存了其中一个属性的相关信息,包括此属性的值的类型 

  9.     type,是boolean还是color还是reference还是String,这些在attr.xml文件下都有定义。 

  10.     它的值的字符串名称;一个属性有多个值时,它从xml文件中获取的值它的顺序data;如果此属性的值 

  11.     的类型是一个reference则保存它的resource id的等等。 

  12. */  

  13.          TypedValue value = mTmpValue;  

  14.          getValue(id, value, true);  

  15.          if (value.type == TypedValue.TYPE_STRING) {  

  16.              return loadXmlResourceParser(value.string.toString(), id,  

  17.                      value.assetCookie, type);  

  18.          }  

  19.          throw new NotFoundException(  

  20.                  "Resource ID #0x" + Integer.toHexString(id) + " type #0x"  

  21.                  + Integer.toHexString(value.type) + " is not valid");  

  22.      }  

  23.  }  

  24.  /** 

  25.     *  getValue方法,id表示要查找的控件的 id,outValue是一个对象,用于保存一些属性相关信息 

  26.  *  resolveRefs为true表明,当通过属性id找到xml文件中的标签时,比如是一个<Button  android:id="@+id/button"/> 

  27.  * 它的值是一个引用reference,则继续解析获得这个id的值。这里看AssetManager类的实现*/  

  28.  public void getValue(int id, TypedValue outValue, boolean resolveRefs)  

  29.          throws NotFoundException {  

  30.      boolean found = mAssets.getResourceValue(id, outValue, resolveRefs);  

  31.      if (found) {  

  32.          return;  

  33.      }  

  34.      throw new NotFoundException("Resource ID #0x"  

  35.                                  + Integer.toHexString(id));  

  36.  }  

  37.  /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,  

  38.          int assetCookie, String type) throws NotFoundException {  

  39.      if (id != 0) {  

  40.          try {  

  41.     //取缓存  

  42.              synchronized (mCachedXmlBlockIds) {  

  43.                  // First see if this block is in our cache.  

  44.                  final int num = mCachedXmlBlockIds.length;  

  45.                  for (int i=0; i<num; i++) {  

  46.                      if (mCachedXmlBlockIds[i] == id) {  

  47.                          //System.out.println("**** REUSING XML BLOCK!  id="  

  48.                          //                   + id + ", index=" + i);  

  49.                          return mCachedXmlBlocks[i].newParser();  

  50.                      }  

  51.                  }  

  52.                 //第一次加载时,会打开这个文件获取一个xml数据块对象。  

  53.        // 这里先看AssetManager类的实现  

  54.                  XmlBlock block = mAssets.openXmlBlockAsset(  

  55.                          assetCookie, file);  

  56.         //下面会把此xmlBlock对象缓存起来,保存id和block,  

  57.         //以后如果是同样的id,直接在缓存中取XmlBlock。  

  58.         //这样就不用再在本地方法中打开文件创建解析树了。  

  59.                  if (block != null) {    

  60.                      int pos = mLastCachedXmlBlockIndex+1;  

  61.                      if (pos >= num) pos = 0;  

  62.                      mLastCachedXmlBlockIndex = pos;  

  63.                      XmlBlock oldBlock = mCachedXmlBlocks[pos];  

  64.                      if (oldBlock != null) {  

  65.                          oldBlock.close();  

  66.                      }  

  67.                      mCachedXmlBlockIds[pos] = id;  

  68.                      mCachedXmlBlocks[pos] = block;  

  69.                      //返回的内部类继承了XmlResourceParser,在APi中此类是隐藏的  

  70.                      return block.newParser();  

  71.                  }  

  72.              }  

  73.          } catch (Exception e) {  

  74.              NotFoundException rnf = new NotFoundException(  

  75.                      "File " + file + " from xml type " + type + " resource ID #0x"  

  76.                      + Integer.toHexString(id));  

  77.              rnf.initCause(e);  

  78.              throw rnf;  

  79.          }  

  80.      }  

  81.      throw new NotFoundException(  

  82.              "File " + file + " from xml type " + type + " resource ID #0x"  

  83.              + Integer.toHexString(id));  

  84.  }  

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.    //这是一个本地方法,是在本地方法中获取这个控件信息,返回通过此控件的id找到的文件名  

  17. //的位置,由于个人对c++不是很了解,只初略的解释本地方法的一些功能。  

  18. //对于的JNI文件位于:\frameworks\base\core\jni\android_util_AssetManager.cpp  

  19. private native final int loadResourceValue(int ident, TypedValue outValue,  

  20.                                              boolean resolve);  

  21. /** 

  22.  * 通过文件名,在本地方法中找到这个xml文件,并且在本地方法中生成一个xml解析对象。 

  23.  * 返回一个id,这个id对应java中的xmlBlock对象。这样xml文件就被load进了内存。 

  24.   * 也就是android所说的预编译,以后再访问只要直接去取数据即可 

  25.  */  

  26.    /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)  

  27.       throws IOException {  

  28.       synchronized (this) {  

  29.           if (!mOpen) {  

  30.               throw new RuntimeException("Assetmanager has been closed");  

  31.           }  

  32.           int xmlBlock = openXmlAssetNative(cookie, fileName);  

  33.           if (xmlBlock != 0) {  

  34.         /* 

  35.         * 在XmlBlock对象中,终于到找了实现XmlResourceParser接口的类 

  36.         * Parser,它是XmlBlock的一个内部类。这里面可以获取所有xml文件中的内容。 

  37.         * 不管是属性还是Tag标签。这里xmlBlock是用来与本地类中的解析树对象对应的。 

  38.         * 所有的解析方法,其实都是调用的本地xml解析树中的方法。所以此类中有大量的 

  39.         * 本地方法。 

  40.         */  

  41.               XmlBlock res = new XmlBlock(this, xmlBlock);  

  42.               incRefsLocked(res.hashCode());  

  43.               return res;  

  44.           }  

  45.       }  

  46.       throw new FileNotFoundException("Asset XML file: " + fileName);  

  47.   }  

三 。通过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. //不是吧,这不是坑爹吗?猜想肯定是被viewgroup重写了  

  9. protected View findViewTraversal(int id) {  

  10.        if (id == mID) {  

  11.            return this;  

  12.        }  

  13.        return null;  

  14.    }  

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.         final View[] where = mChildren;  

  12.         final int len = mChildrenCount;  

  13.         for (int i = 0; i < len; i++) {  

  14.             View v = where[i];  

  15.             if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {  

  16.                 v = v.findViewById(id);  

  17.                 if (v != null) {  

  18.                     return v;  

  19.                 }  

  20.             }  

  21.         }  

  22.         return null;  

  23.     }  

四。通过activity的setContentView方法和findViewById获取一个view的实例。

它是通过 

getWindow().setContentView(layoutResID);设置window对象的view 

再来看看window对象是在哪里获得到的,在类Activity中找到 

mWindow = PolicyManager.makeNewWindow(this); 

它是由PolicyManager生成的。 

找到com.android.internal.policy.PolicyManager,找到方法 

Java代码   收藏代码

  1. //window是由sPolicy对象创建的  

  2.    public static Window makeNewWindow(Context context) {  

  3.        return sPolicy.makeNewWindow(context);  

  4.    }  

  5.    //sPolicy对象是通过反射,获取的一个实例  

  6. //此类的实现在com.android.internal.policy.impl.Policy中  

  7.    private static final String POLICY_IMPL_CLASS_NAME =  

  8.        "com.android.internal.policy.impl.Policy";  

  9.    private static final IPolicy sPolicy;  

  10.    static {  

  11.        // Pull in the actual implementation of the policy at run-time  

  12.        try {  

  13.            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);  

  14.            sPolicy = (IPolicy)policyClass.newInstance();  

  15.        } catch (ClassNotFoundException ex) {  

  16.            throw new RuntimeException(  

  17.                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);  

  18.        } catch (InstantiationException ex) {  

  19.            throw new RuntimeException(  

  20.                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);  

  21.        } catch (IllegalAccessException ex) {  

  22.            throw new RuntimeException(  

  23.                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);  

  24.        }  

  25.    }  

找到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.    //这里的layoutResID也是由mLayoutInflater进行加载的,加载的方式与第二种方法一样。  

  5. //只不过这里把的到的view变成了mContentParent的子view  

  6.   @Override  

  7.    public void setContentView(int layoutResID) {  

  8.        if (mContentParent == null) {  

  9.            installDecor();  

  10.        } else {  

  11.            mContentParent.removeAllViews();  

  12.        }  

  13.        mLayoutInflater.inflate(layoutResID, mContentParent);  

  14.        final Callback cb = getCallback();  

  15.        if (cb != null) {        //这是回调方法,表明mContentParent的子view已经发生改变  

  16.            cb.onContentChanged();  

  17.        }  

  18.    }  

  19.    //再来看看mContentParent究竟是何物,它肯定是一个viewGroup  

  20. private void installDecor() {  

  21.        if (mDecor == null) {  

  22.            mDecor = generateDecor();  

  23.            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  

  24.            mDecor.setIsRootNamespace(true);  

  25.        }  

  26.        if (mContentParent == null) {  

  27.            mContentParent = generateLayout(mDecor);  

  28.            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  

  29.            if (mTitleView != null) {        //这里设置的是是否隐藏titleContainer,即头部titlebar  

  30.                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  

  31.                    View titleContainer = findViewById(com.android.internal.R.id.title_container);  

  32.                    if (titleContainer != null) {  

  33.                        titleContainer.setVisibility(View.GONE);  

  34.                    } else {  

  35.                        mTitleView.setVisibility(View.GONE);  

  36.                    }  

  37.                    if (mContentParent instanceof FrameLayout) {  

  38.                        ((FrameLayout)mContentParent).setForeground(null);  

  39.                    }  

  40.                } else {  

  41.                    mTitleView.setText(mTitle);  

  42.                }  

  43.            }  

  44.        }  

  45.    }  

  46.  //当顶层view为null是,new了一个DecorView  

  47.  protected DecorView generateDecor() {  

  48.        return new DecorView(getContext(), -1);  

  49.    }  

  50.    //这里生成了mContentParent。  

  51. protected ViewGroup generateLayout(DecorView decor) {  

  52.        mDecor.startChanging();  

  53.        //根据window的不同参数选择layoutResource  

  54.        View in = mLayoutInflater.inflate(layoutResource, null);  

  55.        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  

  56.        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  

  57.        if (contentParent == null) {  

  58.            throw new RuntimeException("Window couldn't find content container view");  

  59.        }  

  60.        return contentParent;  

  61.    }  

  62.    //顶层view是一个framelayout  

  63. private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {  

  64.          public DecorView(Context context, int featureId) {  

  65.             super(context);