Ответ 1
Ищите точную точку в исходном коде Android, где добавляются дети.
Мы можем посмотреть, что делает setContentView(R.layout.some_id)
под капотом.
setContentView(int)
calls 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();
}
}
Метод LayoutInflater#inflate(layoutResID, mContentParent)
в конечном итоге вызывает ViewGroup#addView(View, LayoutParams)
на mContentParent
. В промежутках, представления ребенка
Я хочу знать, что происходит именно после того, как я установил представление контента в XML файл, содержащий пользовательский вид. Afer конструктор должен быть частью кода, в котором пользовательский вид "разбора/чтения/раздувания/преобразования" объявленных в виде XML дочерних представлений к реальным представлениям! (комментарий JohnTube)
Ambiquity: из комментария JohnTube кажется, что он больше заинтересован в понимании того, как пользовательское представление завышено. Чтобы это узнать, нам нужно будет посмотреть на работу LayoutInflater
Ссылка.
Итак, ответ на Which method of xLayout or ViewGroup should I override ?
равен ViewGroup#addView(View, LayoutParams)
. Обратите внимание, что на данный момент инфляция всех регулярных/пользовательских просмотров уже состоялась.
Инфляция пользовательских представлений:
Следующий метод в LayoutInflater
- это то, где addView(View, LayoutParams)
вызывается в parent/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
Вызов интереса к этому методу (и в рекурсивном 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(...)
.
Почему эта проверка? Поскольку View
, определенный в пакете android.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
Ссылка - он расширяет LayoutInflater
и переопределяет onCreateView(...)
чтобы проверить, являются ли android.widget
и android.webkit
именами предполагаемых пакетов. Фактически, вызов getLayoutInflater()
получает экземпляр PhoneLayoutInflater
. Вот почему, если вы были подклассом LayoutInflater
, вы даже не могли бы раздувать простейшие макеты, потому что LayoutInflater
может обрабатывать только представления из пакета android.view
.
В любом случае, я отвлекаюсь. Этот дополнительный бит происходит для обычных представлений, которые не имеют period(.)
в их определении. У пользовательских представлений есть период в их именах - com.my.package.CustomView
. Так различается LayoutInflater
.
Итак, в случае обычного вида (например, Button) в качестве второго аргумента будет передан prefix
, такой как android.widget
- для пользовательских представлений это будет null
. Затем prefix
используется вместе с name
, чтобы получить конструктор для этого конкретного класса представления. Пользовательские представления не нужны, потому что их name
уже полностью соответствует требованиям. Думаю, это было сделано для удобства. Иначе вы бы определили свои макеты таким образом:
<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 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`.
}
... a View
.