Joh*_*ube 12 android android-ui android-layout
我的问题是:我想知道xLayout(或一般的ViewGroup)何时从XML添加子视图?而"when"我的意思是代码的什么位置,在UI工具包的"遍历"的"传递"中?我应该覆盖xLayout或ViewGroup的哪种方法?
我已经完成了我的作业:我在上一次Google I/O中看过(由Adam Powell和Romain Guy 撰写的"编写Android自定义视图"),我在这篇Google+ 帖子中看到了Adam Powell的评论.
Vik*_*ram 12
寻找添加子项的Android源代码中的确切点.
我们可以看看幕后setContentView(R.layout.some_id)做了什么.
setContentView(int)调用PhoneWindow#setContentView(int)- PhoneWindow链接是一个具体的实现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();
}
}
Run Code Online (Sandbox Code Playgroud)
该方法LayoutInflater#inflate(layoutResID, mContentParent) 最终调用ViewGroup#addView(View, LayoutParams)上mContentParent.在两者之间,儿童观点
我想知道在将内容视图设置为包含自定义视图的XML文件后会发生什么.在构造函数之后,必须在代码中有一个部分,其中自定义视图"解析/读取/膨胀/转换"XML声明的子视图到实际视图!(JohnTube评论)
Ambiquity:从JohnTube的评论来看,他似乎更想知道自定义视图是如何夸大的.要知道这一点,我们将不得不看看LayoutInflaterLink的运作方式.
所以,答案Which method of xLayout or ViewGroup should I override ?是ViewGroup#addView(View, LayoutParams).请注意,此时,所有常规/自定义视图的通胀已经发生.
通货膨胀自定义视图:
以下方法LayoutInflater是在addView(View, LayoutParams)父/ root上调用的方法:
注意:对此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
Run Code Online (Sandbox Code Playgroud)
对此方法(以及递归rInflate(XmlPullParser, View, AttributeSet, boolean))的兴趣调用是:
temp = createViewFromTag(root, name, attrs);
Run Code Online (Sandbox Code Playgroud)
让我们看看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);
}
}
....
}
Run Code Online (Sandbox Code Playgroud)
在period(.)决定是否onCreateView(...)或createView(...)将被调用.
为什么要检查?因为一个View中所定义android.view,android.widget或android.webkit包被通过它的类名访问.例如:
android.widget: Button, TextView etc.
android.view: ViewStub. SurfaceView, TextureView etc.
android.webkit: WebView
Run Code Online (Sandbox Code Playgroud)
遇到这些视图时,onCreateView(parent, name, attrs)会调用它们.这种方法实际上链接到createView(...):
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
Run Code Online (Sandbox Code Playgroud)
这会处理SurfaceView,TextureView并在规定的其他意见android.view包.如果您有兴趣了解如何TextView, Button etc.被处理的,看看PhoneLayoutInflater链接 -它延伸LayoutInflater并覆盖onCreateView(...)检查android.widget,并android.webkit有预期的包名.实际上,该调用getLayoutInflater()会为您提供一个实例PhoneLayoutInflater.这就是为什么如果你是子类LayoutInflater,你甚至无法夸大最简单的布局 - 因为LayoutInflater只能处理来自android.view包的视图.
无论如何,我离题了.这个额外的位发生在常规视图中 - period(.)它们的定义中没有.自定义视图做有他们的名字一个时期- com.my.package.CustomView.这就是LayoutInflater两者之间的区别.
所以,在普通视图(比如按钮)的情况下,prefix如android.widget将作为第二个参数传递-自定义的看法,这将是null.该prefix则使用随着name以获得特定视图的类的构造函数.自定义视图不需要这样,因为它们name已经完全限定.我想这是为了方便起见.否则,您将以这种方式定义布局:
<android.widget.LinearLayout
...
... />
Run Code Online (Sandbox Code Playgroud)
(虽然合法......)
此外,这就是来自支持库(例如<android.support.v4.widget.DrawerLayout ... />)的视图必须使用完全限定名称的原因.
顺便说一下,如果你确实想把你的布局写成:
<MyCustomView ../>
Run Code Online (Sandbox Code Playgroud)
您所要做的就是扩展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`.
}
Run Code Online (Sandbox Code Playgroud)
...... a View诞生了.
如果您正在讨论在XML中定义的ViewGroup,则在视图膨胀时会添加子项.当您使用a LayoutInflater或您设置活动的内容视图时明确膨胀时,可以使用此选项.(可能还有其他一些时间,特别是如果您使用存根视图.)
如果你想自己将孩子添加到一个ViewGroup没有膨胀的孩子,你可以在视图的构造函数中做到这一点.
编辑:如果要查看视图膨胀时如何添加子项,则会在调用中发生LayoutInflater.inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot).源代码android.view.LayoutInflater包含在Android SDK发行版中; 在线版本可以在许多地方找到(例如,在GrepCode).这个方法结束时,例如,你叫被称为setContentView(int)一个Activity或当你明确地膨胀布局资源.
这些子项实际上是在调用rInflate(parser, root, attrs, false);("递归膨胀")中添加的,可以从方法中的几个不同位置调用inflate(),具体取决于inflater作为根标记找到的内容.您可以自己跟踪代码逻辑.一个有趣的观点是,在孩子被递归膨胀并添加到孩子之前,孩子不会被添加到其父母身上.
另一个有趣的方法,通过使用既inflate和rInflate,是createViewFromTag.这可能依赖于可安装LayoutInflater.Factory(或.Factory2对象)来创建视图,或者可能最终调用createView.在那里你可以看到如何调用视图的双参数构造函数((Context context, AttributeSet attrs)).
| 归档时间: |
|
| 查看次数: |
4527 次 |
| 最近记录: |