何时将子视图从XML添加到Layout/ViewGroup [英] When are child views added to Layout/ViewGroup from XML

查看:48
本文介绍了何时将子视图从XML添加到Layout/ViewGroup的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是: 我想知道xLayout(或通常的ViewGroup)何时从XML添加子视图?所谓何时",是指在代码的什么点上,UI工具包的遍历"是什么? 我应该重写哪种xLayout或ViewGroup方法?

My question is : I want to know when does a xLayout (or ViewGroup in general) add a child view from XML ? And by "when" I mean at what point of code, in what "pass" of the "traversal" of the UI toolkit ? Which method of xLayout or ViewGroup should I override ?

我已经完成作业:我已经看过为Android编写自定义视图" 在上次Google I/O中由Adam Powell和Romain Guy提出,并且我已阅读Adam Powell在此Google+上的评论帖子.

I have done my homework : I have watched the "Writing Custom Views For Android" presented (by Adam Powell and Romain Guy) in the last Google I/O and I have read Adam Powell comments on this Google+ post.

推荐答案

在Android源代码中查找添加子级的确切点.

Looking for the exact point in Android's source code where children are added.

我们可以看看setContentView(R.layout.some_id)的作用.

We can look at what setContentView(R.layout.some_id) is doing under the hood.

setContentView(int)调用PhoneWindow#setContentView(int)-PhoneWindow

setContentView(int) calls PhoneWindow#setContentView(int) - PhoneWindowLink is a concrete inplementation of 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).在此之间,子视图

The method LayoutInflater#inflate(layoutResID, mContentParent) eventually calls ViewGroup#addView(View, LayoutParams) on mContentParent. In between, child views

我想知道将内容视图设置为包含自定义视图的XML文件后究竟会发生什么.在构造函数之后,代码中必须包含一部分,其中自定义视图解析/读取/充气/转换" XML声明的子视图为实际视图! (JohnTube的评论)

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)

无处不在:从JohnTube的评论看来,他似乎更想知道自定义视图是如何被夸大的.要知道这一点,我们将不得不研究LayoutInflater

Ambiquity: From JohnTube's comment, it seems he is more interested in knowing how a custom view is inflated. To know this, we will have to look at the workings of LayoutInflaterLink.

因此,对Which method of xLayout or ViewGroup should I override ?的答案是ViewGroup#addView(View, LayoutParams).请注意,此时,所有常规/自定义视图的膨胀已经发生.

So, the answer to Which method of xLayout or ViewGroup should I override ? is ViewGroup#addView(View, LayoutParams). Note that, at this point, the inflation of all regular/custom Views has already taken place.

自定义视图的通货膨胀:

Inflation of custom views:

LayoutInflater中的以下方法是在父级/根级上调用addView(View, LayoutParams)的位置:

The following method in LayoutInflater is where the addView(View, LayoutParams) is called on the parent/root:

注意:PhoneWindow#setContentView(int)中的呼叫mLayoutInflater.inflate(layoutResID, mContentParent);与此链接. mContentParentDecorView:可通过getWindow().getDecorView()访问的视图.

Note: The call mLayoutInflater.inflate(layoutResID, mContentParent); in PhoneWindow#setContentView(int) chains to this. Here mContentParent is the DecorView: the view that's accessible through 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))中感兴趣的调用是:

The call of interest in this method(and in the recursive rInflate(XmlPullParser, View, AttributeSet, boolean)) is:

temp = createViewFromTag(root, name, attrs);

让我们看看createViewFromTag(...)在做什么:

View createViewFromTag(View parent, String name, AttributeSet attrs) {
    ....
    ....
    if (view == null) {
        if (-1 == name.indexOf('.')) {
            view = onCreateView(parent, name, attrs);
        } else {
            view = createView(name, null, attrs);
        }
    }
    ....
}

period(.)决定调用onCreateView(...)还是createView(...).

The period(.) decides whether onCreateView(...) or createView(...) is called.

为什么要检查?因为在android.viewandroid.widgetandroid.webkit程序包中定义的View是通过其类名访问的.例如:

Why this check? Because a View defined in android.view, android.widget or android.webkit package is accessed through its class name. For example:

android.widget: Button, TextView etc.

android.view: ViewStub. SurfaceView, TextureView etc.

android.webkit: WebView

遇到这些视图时,将调用onCreateView(parent, name, attrs).该方法实际上链接到createView(...):

When these views are encountered, onCreateView(parent, name, attrs) is called. This method actually chains to createView(...):

protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
    return createView(name, "android.view.", attrs);
}

这将处理SurfaceViewTextureViewandroid.view程序包中定义的其他视图.如果您想知道如何处理TextView, Button etc.,请查看PhoneLayoutInflater

This would deal with SurfaceView, TextureView and other views defined in android.view package. If you are interested in knowing how TextView, Button etc. are dealt with, look at PhoneLayoutInflaterLink - it extends LayoutInflater and overrides onCreateView(...) to check if android.widget and android.webkit are the intended package names. In fact, the call getLayoutInflater() gets you an instance of PhoneLayoutInflater. This is why if you were to subclass LayoutInflater, you couldn't even inflate the simplest of layouts - because LayoutInflater can only deal with views from android.view package.

反正我离题了.对于常规视图-这些定义中没有period(.)的情况,会产生额外的影响.自定义视图 do 的名称中带有句点-com.my.package.CustomView. 这是LayoutInflater区分两者的方式.

Anyway, I digress. This extra bit happens for regular Views - which don't have a period(.) in their definition. Custom views do have a period in their names - com.my.package.CustomView. This is how the LayoutInflater distinguishes between the two.

因此,在常规视图(例如Button)的情况下,诸如android.widget之类的prefix将作为第二个参数传递-对于自定义视图,这将是null.然后,将prefixname一起使用,以获取该特定视图的类的构造函数.自定义视图不需要此视图,因为它们的name已经完全限定.我想这样做是为了方便.否则,您可能会以这种方式定义布局:

So, in case of a regular view(say, Button), a prefix such as android.widget will be passed as the second argument - for custom views, this will be null. The prefix is then used along with the name to obtain the constructor for that particular view's class. Custom views don't need this because their name is already fully qualified. I guess this has been done for convenience. Else, you would have been defining your layouts in this way:

<android.widget.LinearLayout
    ...
    ... />  

(虽然合法...)

这也是为什么来自支持库(例如< android.support.v4.widget.DrawerLayout .../>)的视图必须使用标准名称的原因.

Also, this is why views coming from a support library (eg. <android.support.v4.widget.DrawerLayout.../>) have to use fully qualified names.

顺便说一句,如果您确实希望将布局编写为:

By the way, if you did want to write your layouts as:

<MyCustomView ../>

您要做的就是扩展LayoutInflater并将包名称com.my.package.添加到充气期间检查的字符串列表中.检查PhoneLayoutInflater以获得帮助.

all you have to do is to extend LayoutInflater and add your package name com.my.package. to the list of strings that are checked during inflation. Check PhoneLayoutInflater for help with this.

让我们看看自定义视图和常规视图在最后阶段会发生什么-createView(...):

Let's see what happens in the final stage for both custom and regular views - 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`.
}

... View诞生了.

这篇关于何时将子视图从XML添加到Layout/ViewGroup的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆