LayoutInflater
Однажды я пытался раскрыть вам тайное значение названия метода inflate() — слово inflate происходит от словосочетания in flat — в квартиру. Существует старинная традиция запускать в квартиру первым кота, который исследует все закоулки дома и заявляет о своём согласии жить в нём. Однако, мы сделаем вид, что поверили официальной версии, что по-английски inflate переводится как надувать, т.е. мы как бы надуваем данными из XML-файла объекты View.
Мы рассмотрим надувание компонентов применимо к LayoutInflater. Существует также класс MenuInflater, который использует тот же принцип.
Фрагменты тоже часто используют механизм надувания. Обратите внимание на сигнатуру метода onCreateView():
Сам механизм надувания прост и не требует особых объяснений, изучения достоин один аспект.
Вероятно, вам не раз приходилось писать/копировать такой код:
Даже у меня на сайте есть такие примеры. Недавно я заметил, что среда разработки ругается на последний параметр null.
Оказывается, это неправильно. Давайте изучать этот код.
Существует две версии метода inflate() для стандартных приложений.
В первом параметре указывается идентификатор ресурса разметки, который мы собираемся надуть. Во втором параметре указывается корневой компонент, к которому нужно присоединить надутые объекты. В третьем параметре (если он используется) указывается, нужно ли присоединять надутые объекты к корневому элементу.
LayoutInflater автоматически пытается присоединить надутые компоненты к корневому элементу. Иногда сама среда разработки может создать заготовку с использованием null, чтобы избежать возможного краха приложения. И разработчики используют предложенный вариант, хотя можно было использовать второй вариант метода с тремя параметрами.
Но давайте узнаем, чего мы лишаемся, когда используем null для корневого элемента.
Надувание компонентов часто применяется в адаптерах, когда разметка отдельного элемента списка задаётся в XML-файле, а затем динамически конвертируется в набор компонентов в методе getView():
Среда разработки заругалась. Исправим null на parent, который по названию вроде подходит под корневой элемент. Однако, запустив проект, мы получим ошибку. Йожкин кот, что мы наделали?!
Вспоминаем, что есть другая версия метода и исправляем на следующий вариант.
Пример работает, предупреждение не выводится. Но осадочек остался.
При использовании null среда разработки как-бы пытается сказать: «Я не знаю, какой элемент является родительским, поэтому прости, между нами всё кончено «, я буду использовать null.
Тогда зачем нам этот родительский элемент? На самом деле он может сыграть важную роль в различных ситуациях.
Проблемы могут возникнуть при использовании атрибутов android:layout_xxx в родительском элементе. В результате, не зная ничего о родителе, мы не можем использовать его LayoutParams и система будет игнорировать вашу разметку. А вы будете думать, что это баг.
Без LayoutParams родительский ViewGroup надует компоненты с настройками по умолчанию. И в большинстве случае это вполне работает.
Рассмотрим конкретный пример. Создадим разметку для отдельного элемента списка layout/list_item.xml.
Мы хотим использовать фиксированную высоту для каждого элемента и используем атрибут android:layout_height у родительского элемента.
Попробуем неправильный вариант.
В этом варианте игнорируется родитель и соответственно его настройки.
Результат на экране.
Теперь вам должна быть ясна разница между разными вариантами и правильный код для применения.
Но бывают случаи, когда использование null оправданно.
Одним из таких примеров является использование разметки в AlertDialog. Допустим, у нас есть код.
AlertDialog.Builder не нуждается в родительском элементе для построения диалогового окна.
Рассмотрим случаи, когда нужен true. Допустим, у нас есть отдельная разметка кнопки.
Мы можем программно присоединить кнопку к LinearLayout в активности или фрагменте.
Мы указали, что хотим надуть кнопку из его собственной разметки и присоединить полученную кнопку к LinearLayout (второй параметр). Это решение эквивалентно вызову метода с двумя параметрами.
Как вариант, можно не присоединять при надувании, а добавить программно через addView().
Другой случай использования true встречается при создании собственного компонента, когда применяется .
У файла разметки нет корневого ViewGroup и мы указываем свой собственный компонент на основе LinearLayout в качестве корневого. Если бы мы использовали в своей разметке вместо merge любой компонент, например, FrameLayout, то тогда был бы другой случай.
Теперь вы знаете, в каких ситуациях нужно использовать разные методы inflate(). Это как с котом — надутого кота можно обратить в null, когда вы не нуждаетесь в двойниках.
Источник
Полный список
— разбираем как можно использовать LayoutInflater
После изучения SQLite самое время приступить к изучению списков – List. Но перед этим полезно будет узнать про LayoutInflater. Это знание пригодится нам в создании расширенных списков. Также перед этим уроком рекомендую снова прочесть урок про LayoutParams, освежить знания.
LayoutInflater – это класс, который умеет из содержимого layout-файла создать View-элемент. Метод который это делает называется inflate. Есть несколько реализаций этого метода с различными параметрами. Но все они используют друг друга и результат их выполнения один – View.
Как видим, на вход метод принимает три параметра:
resource — ID layout-файла, который будет использован для создания View. Например — R.layout.main
root – родительский ViewGroup-элемент для создаваемого View. LayoutParams от этого ViewGroup присваиваются создаваемому View.
attachToRoot – присоединять ли создаваемый View к root. Если true, то root становится родителем создаваемого View. Т.е. это равносильно команде root.addView(View). Если false – то создаваемый View просто получает LayoutParams от root, но его дочерним элементом не становится.
Посмотрим на практике.
Project name: P0401_LayoutInflater
Build Target: Android 2.3.3
Application name: LayoutInflater
Package name: ru.startandroid.develop.p0401layoutinflater
Create Activity: MainActivity
Открываем main.xml и рисуем такой экран:
На экране две ViewGroup — linLayout и relLayout. В них по TextView с соответствующим текстом.
Создадим еще один layout-файл text.xml:
Тут просто TextView без всяких ViewGroup. На нем мы и будем испытывать LayoutInflater.
Открываем MainActivity.java и пишем код:
Мы получаем LayoutInflater методом getLayoutInflater, используем его для получения View-элемента из layout-файла text.xml и считываем LayoutParams у свежесозданного view.
Обратите внимание на параметры, которые мы использовали для метода inflate. Мы указали ID layout-ресурса, передали null в качестве родительского элемента и, соответственно, привязка к родителю — false.
Все сохраним и запустим.
На экране ничего не изменилось. Т.к. мы конвертнули layout в view, но никуда его не поместили. Он просто висит в памяти.
Class of view: class android.widget.TextView
LayoutParams of view is null: true
Text of view: Layout with TextView
Мы видим класс созданного элемента — TextView. Все верно — этот элемент и был в файле text.xml. Далее видим null вместо LayoutParams. Это произошло потому, что родителя в методе inflate мы указали null. А именно от родителя view и должен был получить LayoutParams. Третья строка лога показывает текст TextView. Он тот же, что и в layout-файле text.xml – все верно.
Давайте немного изменим программу. Будем добавлять наш созданный элемент в linLayout из main.xml. Делается это просто – командой addView.
(добавляете только выделенные строки)
Мы нашли linLayout с экрана и добавили в него созданный с помощью LayoutInflater элемент.
Сохраняем, запускаем. Видим, что элемент добавился на экран в linLayout.
Теперь давайте попробуем указать родителя (root) при вызове метода inflate. Перепишем метод onCreate:
Мы находим элементы linLayout и relLayout с экрана и с помощью LayoutInflater создаем два View-элемента из layout-файла text.xml. Для первого указываем root – linLayout, для второго – relLayout. Но третий параметр attachToRoot оставляем false. Это значит, что созданный View-элемент получит LayoutParams от root-элемента, но не добавится к нему.
Все сохраним, запустим. На экране ничего не поменялось. Т.к. мы ни к чему новые элементы не добавляли и attachToRoot = false.
Class of view1: class android.widget.TextView
Class of layoutParams of view1: class android.widget.LinearLayout$LayoutParams
Text of view1: Layout with TextView
Class of view2: class android.widget.TextView
Class of layoutParams of view2: class android.widget.RelativeLayout$LayoutParams
Text of view2: Layout with TextView
По логам видно, что класс созданных элементов – TextView. А класс LayoutParams различается. В первом случае – это LinearLayout$LayoutParams, т.к. в качестве root элемента в методе inflate мы указали linLayout, а это объект класса LinearLayout. Во втором случае класс LayoutParams у созданного элемента — RelativeLayout$LayoutParams. Потому, что в качестве root указали relLayout (класс RelativeLayout).
Теперь у нас два варианта, как добавить созданные view1 и view2 на экран.
1) Снова использовать методы addView
2) Передавать true в качестве третьего параметра метода inflate. Тогда созданный View-элемент будет добавлен к root.
Выберем второй вариант и внесем изменения в код:
Передаем true в качестве третьего параметра в методе inflate и убираем строки выведения в лог текстов из TextView. Сейчас будет понятно почему.
Все сохраним и запустим приложение.
Как видим, созданные TextView появились в своих родителях, которых мы указали в методе inflate. В RelativeLayout элементы наложились друг на друга, т.к. мы не настроили расположение. В данный момент это не существенно.
Class of view1: class android.widget.LinearLayout
Class of layoutParams of view1: class android.widget.LinearLayout$LayoutParams
Class of view2: class android.widget.RelativeLayout
Class of layoutParams of view2: class android.widget.LinearLayout$LayoutParams
Обратите внимание на класс элементов. В первом случае — это LinearLayout, а во втором — RelativeLayout. Т.е. метод inflate вернул нам не созданные из layout-файла View-элементы, а те, что мы указывали как root. А созданные из layout-файла View элементы он добавил в root как дочерние аналогично команде addView. Это произошло потому, что мы указали true в третьем параметре (attachToRoot) метода inflate.
Соответственно LayoutParams для view1 и view2 будет LinearLayout$LayoutParams, т.к. linLayout и relLayout имеют родителя LinearLayout. И LayoutParams берут от него.
Для закрепления темы на следующем уроке сделаем пример поинтереснее.
На следующем уроке:
— делаем свой вариант списка
getLayoutInflater
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Layout Inflater Class
Definition
Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
Instantiates a layout XML file into its corresponding android.view.View objects.
Remarks
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
Constructors
Create a new LayoutInflater instance associated with a particular Context.
A constructor used when creating managed representations of JNI objects; called by the runtime.
Create a new LayoutInflater instance associated with a particular Context.
Properties
Returns the runtime class of this Object .
(Inherited from Object)
Return the context we are running in, for access to resources, class loader, etc.
Return the current Factory (or null). -or- Attach a custom Factory interface for creating views while using this LayoutInflater.
Return the current Factory2 . -or- Like #setFactory , but allows you to set a Factory2 interface.
Sets the Filter to by this LayoutInflater.
The handle to the underlying Android instance.
(Inherited from Object)
This API supports the Mono for Android infrastructure and is not intended to be used directly from your code.
This API supports the Mono for Android infrastructure and is not intended to be used directly from your code.
Methods
Creates and returns a copy of this object.
(Inherited from Object)
Create a copy of the existing LayoutInflater object, with the copy pointing to a different Context than the original.
Low-level function for instantiating a view by name.
Low-level function for instantiating a view by name.
Indicates whether some other object is «equal to» this one.
(Inherited from Object)
Obtains the LayoutInflater from the given context.
Returns a hash code value for the object.
(Inherited from Object)
Inflate a new view hierarchy from the specified xml resource.
Inflate a new view hierarchy from the specified xml resource.
Inflate a new view hierarchy from the specified xml resource.
Inflate a new view hierarchy from the specified xml resource.
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
(Inherited from Object)
Wakes up a single thread that is waiting on this object’s monitor.
(Inherited from Object)
Wakes up all threads that are waiting on this object’s monitor.
(Inherited from Object)
Version of #onCreateView(View, String, AttributeSet) that also takes the inflation context.
Version of #onCreateView(View, String, AttributeSet) that also takes the inflation context.
Version of #onCreateView(View, String, AttributeSet) that also takes the inflation context.
Sets the Handle property.
(Inherited from Object)
Returns a string representation of the object.
(Inherited from Object)
Causes the current thread to wait until another thread invokes the java.lang.Object#notify() method or the java.lang.Object#notifyAll() method for this object.
(Inherited from Object)
Causes the current thread to wait until another thread invokes the java.lang.Object#notify() method or the java.lang.Object#notifyAll() method for this object.
(Inherited from Object)
Causes the current thread to wait until another thread invokes the java.lang.Object#notify() method or the java.lang.Object#notifyAll() method for this object.
(Inherited from Object)
Explicit Interface Implementations
IJavaPeerable.Disposed() | (Inherited from Object) |
IJavaPeerable.DisposeUnlessReferenced() | (Inherited from Object) |
IJavaPeerable.Finalized() | (Inherited from Object) |
IJavaPeerable.JniManagedPeerState | (Inherited from Object) |
IJavaPeerable.SetJniIdentityHashCode(Int32) | (Inherited from Object) |
IJavaPeerable.SetJniManagedPeerState(JniManagedPeerStates) | (Inherited from Object) |
IJavaPeerable.SetPeerReference(JniObjectReference) | (Inherited from Object) |
Extension Methods
Performs an Android runtime-checked type conversion.
Источник