Ищите точную точку в исходном коде 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();
}
}
Метод LayoutInflater#inflate(layoutResID, mContentParent)
в итоге вызывает ViewGroup#addView(View, LayoutParams)
для mContentParent
. В промежутках дочерние просмотры
Я хочу знать, что происходит после того, как я задаю представление содержимого XML-файлу, содержащему настраиваемое представление. После конструктора в коде должна быть часть, где пользовательское представление «разбирает/читает/раздувает/преобразовывает» XML-декларированные дочерние представления в фактические представления! (комментарий JohnTube)
Неоднозначность: судя по комментарию JohnTube, его больше интересует, как раздувается пользовательское представление. Чтобы узнать это, нам нужно взглянуть на работу LayoutInflater
Ссылка.
Итак, ответ на Which method of xLayout or ViewGroup should I override ?
равен ViewGroup#addView(View, LayoutParams)
. Обратите внимание, что на данный момент инфляция всех обычных/пользовательских представлений уже произошла.
Инфляция пользовательских представлений:
Следующий метод в LayoutInflater
- это когда 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, 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
Link — расширяет 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
...
... />
(Хотя это законно...)
Кроме того, именно поэтому представления из библиотеки поддержки (например, ‹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`.
}
... родился View
.
person
Vikram
schedule
24.08.2014