子供が追加されたAndroidのソースコードの正確な場所を探してください。
setContentView(R.layout.some_id)
は何をしているのですか。
setContentView(int)
コールPhoneWindow#setContentView(int)
からPhoneWindow
LinkはWindow
の具体inplementationある:
@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();
}
}
結局mContentParent
にViewGroup#addView(View, LayoutParams)
を呼び出しLayoutInflater#inflate(layoutResID, mContentParent)
方法。その間に、子ビュー
私はコンテンツビューをカスタムビューを含むXMLファイルに設定した後に正確に何が起こるか知りたいと思います。コンストラクタを呼び出すには、XMLで宣言された子ビューを実際のビューに "解析/読み込み/膨張/変換"するカスタムビューのコードの一部が必要です。(JohnTubeによるコメント)
Ambiquity:JohnTubeさんのコメントから、彼がカスタムビューががを膨らませているかを知ることで、より興味を持っているようです。これを知るには、LayoutInflater
Linkの動作を見なければなりません。
したがって、Which method of xLayout or ViewGroup should I override ?
への回答はViewGroup#addView(View, LayoutParams)
です。この時点で、すべての標準/カスタムビューの膨張が既に行われていることに注意してください。カスタムビューの
インフレ:
addView(View, LayoutParams)
は親/ルートに呼び出された
LayoutInflater
で、次の方法がある:
注:これにPhoneWindow#setContentView(int)
チェーンでコールmLayoutInflater.inflate(layoutResID, mContentParent);
。ここで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, name, attrs);
} else {
view = createView(name, null, attrs);
}
}
....
}
period(.)
がいるかどうかを決定するonCreateView(...)
またはcreateView(...)
が呼び出されます。
このチェックはなぜですか? android.view
に定義されているView
、android.widget
またはandroid.webkit
パッケージはクラス名でアクセスされるためです。例:
android.widget: Button, TextView etc.
android.view: ViewStub. SurfaceView, TextureView etc.
android.webkit: WebView
これらのビューに遭遇すると、onCreateView(parent, name, attrs)
が呼び出されます。 createView(...)
この方法は、実際にチェーン:
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
これはSurfaceView
、TextureView
とandroid.view
パッケージで定義された他のビューを扱うでしょう。 TextView, Button etc.
の処理方法を知りたい場合は、PhoneLayoutInflater
Linkを参照してください。LayoutInflater
を拡張し、onCreateView(...)
を上書きして、android.widget
とandroid.webkit
が目的のパッケージ名であるかどうかを確認してください。実際にはgetLayoutInflater()
というコールはPhoneLayoutInflater
のインスタンスを取得します。このため、LayoutInflater
をサブクラス化すると、LayoutInflater
はandroid.view
パッケージからのビューしか扱えないため、最も単純なレイアウトを膨らませることはできません。
とにかく、私は逃げます。この余分なビットは通常のビューで発生します。そのビューにはperiod(.)
がありません。カスタムビューにはというピリオドが付きます。これは、LayoutInflater
が2つを区別する方法です。です。
ので、通常のビュー(たとえば、ボタン)の場合には、そのようなandroid.widget
などprefix
は、2番目の引数として渡されます - カスタムビューのために、これはnull
になります。 prefix
は、その特定のビューのクラスのコンストラクタを取得するためにname
とともに使用されます。カスタムビューでは、name
がすでに完全修飾されているため、このビューは必要ありません。私はこれが便宜のために行われたと思います。そうでなければ、あなたはこの方法であなたのレイアウトの定義されていたであろう:
<android.widget.LinearLayout
...
... />
(ただしその法的...)
ビューがサポートライブラリ(例えばから来る理由も、これはある< android.support。 .v4.widget.DrawerLayout ... />)は、完全修飾名を使用する必要があります。
<MyCustomView ../>
あなたがしなければならないすべてはLayoutInflaterを拡張し、膨張時にチェックされている文字列のリストにあなたのパッケージ名com.my.package.
を追加することです:あなたのようにあなたのレイアウトを書きたいなかった場合ところで
、 。これについては、PhoneLayoutInflater
を参照してください。
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
が生まれている:createView(...)
-
はのは、カスタムおよび定期的なビューの両方のための最終段階で何が起こるか見てみましょう。
素晴らしい回答!私は 'addView(View)'の使い方を探していましたが、関連するものは見つかりませんでした。 'LayoutInflater'の' rInflate'への参照は大きな助けになります!私が探していた正しい呼び出しは 'addView(View、int、LayoutParams)'でした。 – nhaarman
@NiekHaarmanありがとう、Niek。 'addView(View、int、LayoutParams)'は本当に正しい呼び出しです。他の全てのオーバーロードされた 'addView'メソッドはそれに連鎖します。 – Vikram