我想知道xLayout(或一般来说是ViewGroup)什么时候从 XML添加子视图? “何时”我的意思是什么代码,什么“通过”UI工具包的“遍历”?
应该覆盖哪个xLayout或ViewGroup的方法?
我已经完成了我的功课:我看过“Writing Custom Views For Android”在最近的Google I / O中提交的(Adam Powell和Romain Guy),并且已经阅读了Adam Powell在Google post上的评论.
解决方法
Looking for the exact point in Android’s source code where children are added.
我们可以看看setContentView(R.layout.some_id)正在做什么.
setContentView(int)调用PhoneWindow#setContentView(int) – PhoneWindowLink是Window的具体实现:
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID,mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
方法LayoutInflater#inflate(layoutResID,mContentParent)最终在mContentParent上调用ViewGroup#addView(View,LayoutParams).之间,儿童视图
I want to know what happens exactly after I set content view to an XML file that contains a custom view. Afer the constructor there has to be a part in the code where the custom view “parse/read/inflate/convert” XML-declared child views to actual views ! (comment by JohnTube)
Ambiquity:从JohnTube的评论来看,他似乎更有兴趣了解自定义视图是如何膨胀的.要知道这一点,我们将看看LayoutInflaterLink的工作原理.
那么,我应该覆盖哪个xLayout或ViewGroup的方法的答案?是ViewGroup#addView(View,LayoutParams).请注意,在这一点上,所有常规/定制视图的通货膨胀已经发生.
自订视图通货膨胀:
LayoutInflater中的以下方法是在父/ root上调用addView(View,LayoutParams)的位置:
注意:调用mLayoutInflater.inflate(layoutResID,mContentParent);在PhoneWindow#setContentView(int)中链接到这个.这里的mContentParent是DecorView:可以通过getWindow()访问的视图getDecorView().
// Inflate a new view hierarchy from the specified XML node. public View inflate(XmlPullParser parser,ViewGroup root,boolean attachToRoot) // Recursive method used to descend down the xml hierarchy and instantiate views,// instantiate their children,and then call onFinishInflate(). void rInflate(XmlPullParser parser,View parent,final AttributeSet attrs,boolean finishInflate) throws XmlPullParserException,IOException
该方法的兴趣调用(和递归rInflate(XmlPullParser,View,AttributeSet,boolean))是:
temp = createViewFromTag(root,name,attrs);
看看createViewFromTag(…)正在做什么:
View createViewFromTag(View parent,String name,AttributeSet attrs) { .... .... if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(parent,attrs); } else { view = createView(name,null,attrs); } } .... }
句点(.)决定是否调用了onCreateView(…)或createView(…).
为什么这个检查?因为在android.view,android.widget或android.webkit包中定义的视图是通过其类名来访问的.例如:
android.widget: Button,TextView etc. android.view: ViewStub. SurfaceView,TextureView etc. android.webkit: WebView
遇到这些视图时,会调用onCreateView(parent,attrs).这个方法实际上链接到createView(…):
protected View onCreateView(String name,AttributeSet attrs) throws ClassNotFoundException { return createView(name,"android.view.",attrs); }
这将处理在Android.view包中定义的SurfaceView,TextureView和其他视图.如果您有兴趣了解TextView,Button等如何处理,请查看PhoneLayoutInflaterLink – 它扩展了LayoutInflater并覆盖onCreateView(…),以检查android.widget和android.webkit是否是预期的包名称.其实调用getLayoutInflater()可以得到一个PhoneLayoutInflater的实例.这就是为什么如果你要布局LayoutInflater,你甚至不能夸大最简单的布局 – 因为LayoutInflater只能处理来自android.view包的视图.
无论如何,我离题.这个额外的位发生在常规视图中 – 在它们的定义中没有句点(.).自定义视图在其名称中有一段时间 – com.my.package.CustomView.这是LayoutInflater如何区分两者.
所以,在常规视图(比如说Button)的情况下,前缀如android.widget将作为第二个参数传递 – 对于自定义视图,这将为null.然后将该前缀与名称一起使用以获取该特定视图的类的构造函数.自定义视图不需要此,因为它们的名称已经完全限定.我想这是为了方便而做的.否则,您将以这种方式定义您的布局:
<android.widget.LinearLayout ... ... />
(它的法律虽然…)
此外,这就是为什么来自支持库(例如< android.support.v4.widget.DrawerLayout ... />)的视图必须使用完全限定名称的原因.
顺便说一句,如果你确实想要把你的布局写成:
<MyCustomView ../>
所有你需要做的是扩展LayoutInflater并添加你的包名com.my.package.到通货膨胀期间检查的字符串列表.检查PhoneLayoutInflater以获得帮助.
让我们看看在最后阶段对于自定义和常规视图会发生什么 – createView(…):
public final View createView(String name,String prefix,AttributeSet attrs) throws ClassNotFoundException,InflateException { // Try looking for the constructor in cache Constructor<? extends View> constructor = sConstructorMap.get(name); Class<? extends View> clazz = null; try { if (constructor == null) { // Class not found in the cache,see if it's real,and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); .... // Get constructor constructor = clazz.getConstructor(mConstructorSignature); sConstructorMap.put(name,constructor); } else { .... } Object[] args = mConstructorArgs; args[1] = attrs; // Obtain an instance final View view = constructor.newInstance(args); .... // We finally have a view! return view; } // A bunch of catch blocks: - if the only constructor defined is `CustomView(Context)` - NoSuchMethodException - if `com.my.package.CustomView` doesn't extend View - ClassCastException - if `com.my.package.CustomView` is not found - ClassNotFoundException // All these catch blocks throw the often seen `InflateException`. }
一个视图诞生了.