Что означает параметр LayoutInflater attachToRoot?
Документация LayoutInflater.inflate
не совсем ясна для цели параметра attachToRoot
.
attachToRoot: следует ли привязать завышенную иерархию к корневому параметру? Если false, root используется только для создания правильного подкласс LayoutParams для корневого представления в XML.
Может ли кто-нибудь объяснить более подробно, в частности, что такое корневой вид, и, возможно, показать пример изменения поведения между значениями true
и false
?
Ответы
Ответ 1
Если установлено значение "Истина", то, когда ваш макет завышен, он будет автоматически добавлен в иерархию представлений ViewGroup, указанную во втором параметре в качестве дочернего. Например, если корневой параметр был LinearLayout
, то ваше завышенное представление будет автоматически добавлено как дочерний элемент этого представления.
Если для него установлено значение false, ваш макет будет завышен, но не будет прикреплен к какому-либо другому макету (поэтому он не будет нарисован, получит события касания и т.д.).
Ответ 2
СЕЙЧАС ИЛИ НЕ СЕЙЧАС
Основное различие между "третьим" параметром attachToRoot, равным true или false, заключается в следующем.
Когда вы ставите attachToRoot
true: добавить дочерний вид к родителю ПРЯМО СЕЙЧАС
false: добавить дочерний вид к родительскому NOT NOW.
Добавьте это позже. '
Когда это позже?
Это позже, когда вы используете, например, для parent.addView(childView)
Распространенным заблуждением является то, что если параметр attachToRoot имеет значение false, то дочернее представление не будет добавлено к родительскому. НЕПРАВИЛЬНО
В обоих случаях дочерний вид будет добавлен в parentView. Это просто вопрос времени.
inflater.inflate(child,parent,false);
parent.addView(child);
эквивалентно
inflater.inflate(child,parent,true);
БОЛЬШОЕ НЕТ-НЕТ
Вы никогда не должны передавать attachToRoot как true, если вы не несете ответственности за добавление дочернего представления к родительскому.
Например, при добавлении фрагмента
public View onCreateView(LayoutInflater inflater,ViewGroup parent,Bundle bundle)
{
super.onCreateView(inflater,parent,bundle);
View view = inflater.inflate(R.layout.image_fragment,parent,false);
.....
return view;
}
если вы передадите третий параметр как true, вы получите IllegalStateException из-за этого парня.
getSupportFragmentManager()
.beginTransaction()
.add(parent, childFragment)
.commit();
Поскольку вы уже добавили дочерний фрагмент в onCreateView() по ошибке. Вызов add скажет вам, что дочернее представление уже добавлено к родительскому элементу. Следовательно, IllegalStateException.
Здесь вы не несете ответственности за добавление childView, за это отвечает FragmentManager. Так что всегда передавайте false в этом случае.
ПРИМЕЧАНИЕ: я также читал, что parentView не получит childView touchEvents, если attachToRoot имеет значение false. Но я не проверял это все же.
Ответ 3
Кажется, что в ответах много текста, но нет кода, поэтому я решил возродить этот старый вопрос с помощью примера кода, в нескольких отзывах упомянутых людей:
Если установлено значение true, то, когда ваш макет раздувается, он будет автоматически добавлен в иерархию представлений ViewGroup, указанную во втором параметре, как дочерний.
Что это значит в коде (что понимают большинство программистов):
public class MyCustomLayout extends LinearLayout {
public MyCustomLayout(Context context) {
super(context);
// Inflate the view from the layout resource and pass it as child of mine (Notice I'm a LinearLayout class).
LayoutInflater.from(context).inflate(R.layout.child_view, this, true);
}
}
Обратите внимание, что предыдущий код добавляет макет R.layout.child_view
как дочерний элемент MyCustomLayout
из-за attachToRoot
param is true
и назначает параметры макета родителя точно так же, как если бы я использовал addView
программно, или как если бы я сделал это в xml:
<LinearLayout>
<View.../>
...
</LinearLayout>
В следующем коде объясняется сценарий при передаче attachRoot
как false
:
LinearLayout linearLayout = new LinearLayout(context);
linearLayout.setLayoutParams(new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
// Create a stand-alone view
View myView = LayoutInflater.from(context)
.inflate(R.layout.ownRootView, null, false);
linearLayout.addView(myView);
В предыдущем коде вы указываете, что хотите, чтобы myView
был его собственным корневым объектом и не привязывал его к какому-либо родителю, позже мы добавили его как часть LinearLayout
, но на мгновение это был стенд - одиночный (без родительского) представления.
То же самое происходит с фрагментами, вы можете добавить их в уже существующую группу и быть частью этого, или просто передать параметры:
inflater.inflate(R.layout.fragment, null, false);
Чтобы указать, что это будет его собственный корень.
Ответ 4
Документации и двух предыдущих ответов должно быть достаточно, только некоторые мысли от меня.
Метод inflate
используется для раздувания файлов макета. С этими раздутыми макетами вы должны иметь возможность привязывать их непосредственно к родительскому ViewGroup
или просто раздувать иерархию представлений из этого файла макета и работать с ним вне обычной иерархии представлений.
В первом случае параметр attachToRoot
должен быть установлен в true
(или просто использовать метод inflate
, который принимает файл макета и родительский корень ViewGroup
(не null
)), В этом случае возвращаемый View
представляет собой просто ViewGroup
, который был передан в методе, ViewGroup
, к которому будет добавлена иерархия расширенного представления.
Для второго варианта возвращаемый View
является корневым ViewGroup
из файла макета. Если вы помните нашу последнюю дискуссию из include-merge
вопроса пары, это одна из причин ограничения merge
(когда файл макета с merge
как root надувается, вы должны указать родителя, а attachedToRoot
должен быть установлен на true
). Если у вас был файл макета с корнем, то тег merge
и attachedToRoot
был установлен в false
, тогда метод inflate
не сможет ничего вернуть, поскольку merge
не имеет эквивалента.
Кроме того, как указано в документации, версия inflate
с attachToRoot
, установленная на false
, важна, потому что вы можете создать иерархию представления с правильным LayoutParams
от родителя. Это важно в некоторых случаях, наиболее примечательным с дочерними элементами AdapterView
, подкласса ViewGroup
, для которого набор методов addView()
не поддерживается. Я уверен, что вы вспомнили использование этой строки в методе getView()
:
convertView = inflater.inflate(R.layout.row_layout, parent, false);
Эта строка гарантирует, что надутый R.layout.row_layout
файл имеет правильный LayoutParams
из подкласса AdapterView
, установленный в его корневом каталоге ViewGroup
. Если вы этого не сделаете, у вас могут возникнуть проблемы с файлом макета, если корень был RelativeLayout
. TableLayout/TableRow
также имеют некоторые специальные и важные LayoutParams
, и вы должны убедиться, что в них есть правильные LayoutParams
.
Ответ 5
Я тоже был смущен тем, какова была настоящая цель метода attachToRoot
в inflate
. После небольшого изучения пользовательского интерфейса я наконец получил ответ:
parent:
в этом случае является виджет/макет, который окружает объекты вида, которые вы хотите раздуть, используя findViewById().
attachToRoot:
прикрепляет представления к их родительскому объекту (включает их в родительскую иерархию), поэтому любое событие касания, получаемое просмотрами, также будет перенесено в родительское представление. Теперь до родителя, хочет ли он развлекать эти события или игнорировать их., если установлено значение false, они не добавляются как прямые дочерние элементы родителя, а родитель не получает никаких событий касания из представлений.
Надеюсь, это очистит путаницу
Ответ 6
В этом разделе есть много путаницы из-за документации для метода inflate().
В общем случае, если для attachToRoot установлено значение true, то файл макета, указанный в первом параметре, накачивается и привязывается к ViewGroup, указанному во втором параметре в данный момент времени. Когда attachToRoot является ложным, файл макета с первого параметра раздувается и возвращается как вид, а любое вложение вида происходит в другое время.
Это, вероятно, мало значит, если вы не увидите много примеров. При вызове метода LayoutInflater.inflate() внутри метода onCreateView фрагмента вы хотите передать false для attachToRoot, потому что действие, связанное с этим фрагментом, фактически отвечает за добавление этого фрагмента. Если вы вручную раздуваете и добавляете представление в другой вид в какой-то более поздний момент времени, например, с помощью метода addView(), вам нужно передать false для attachToRoot, потому что вложение приходит в более поздний момент времени.
Вы можете прочитать о нескольких других уникальных примерах, касающихся Dialogs и пользовательских представлений в блоге, который я написал об этой самой теме.
https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
Ответ 7
Я написал этот ответ, потому что даже после нескольких страниц StackOverflow я не смог четко понять, что означал attachToRoot. Ниже приведен метод inflate() в классе LayoutInflater.
View inflate (int resource, ViewGroup root, boolean attachToRoot)
Посмотрите файл activity_main.xml, макет button.xml и созданный файл MainActivity.java.
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
button.xml
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LayoutInflater inflater = getLayoutInflater();
LinearLayout root = (LinearLayout) findViewById(R.id.root);
View view = inflater.inflate(R.layout.button, root, false);
}
Когда мы запускаем код, мы не увидим кнопку в макете. Это связано с тем, что наш макет кнопки не добавляется в основной макет активности, так как для attachToRoot установлено значение false.
LinearLayout имеет метод addView (View view), который можно использовать для добавления Views в LinearLayout. Это добавит макет кнопки в макет основной активности и сделает кнопку видимой при запуске кода.
root.addView(view);
Удалите предыдущую строку и посмотрите, что произойдет, если мы установим attachToRoot как true.
View view = inflater.inflate(R.layout.button, root, true);
Снова мы видим, что макет кнопки виден. Это связано с тем, что attachToRoot напрямую прикрепляет раздутый макет к указанному родительскому элементу. В этом случае это root LinearLayout. Здесь нам не нужно добавлять представления вручную, как в предыдущем случае, с помощью метода addView (View view).
Почему люди получают IllegalStateException при установке attachToRoot как истинного для фрагмента.
Это потому, что для фрагмента вы уже указали, где разместить макет фрагмента в вашем файле активности.
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.root, fragment)
.commit();
Добавочный (int parent, фрагмент фрагмента) добавляет фрагмент, который имеет его макет для родительского макета. Если мы установим attachToRoot как true, вы получите IllegalStateException: указанный ребенок уже имеет родителя. Поскольку макет фрагмента уже добавлен в родительский макет в методе add().
Вы должны всегда передавать false для attachToRoot, когда вы раздуваете фрагменты. Задача FragmentManagers - добавлять, удалять и заменять фрагменты.
Вернуться к моему примеру. Что делать, если мы делаем оба.
View view = inflater.inflate(R.layout.button, root, true);
root.addView(view);
В первой строке LayoutInflater присоединяет макет кнопки к корневому макету и возвращает объект View, который содержит ту же макет кнопки. Во второй строке мы добавляем тот же объект View в родительский корневой макет. Это приводит к тому же исключению IllegalStateException, которое мы видели с помощью фрагментов (у указанного дочернего элемента уже есть родительский элемент).
Имейте в виду, что есть другой перегруженный метод inflate(), который по умолчанию устанавливает attachToRoot как true.
View inflate (int resource, ViewGroup root)
Ответ 8
attachToRoot
, установленное в true, означает, что inflatedView
будет добавлено в иерархию родительских представлений. Таким образом, пользователи могут "видеть" и ощущать сенсорные события (или любые другие операции пользовательского интерфейса) пользователями. В противном случае он просто создается, не добавляется ни в одну иерархию представлений и, следовательно, не может быть замечен или обрабатывать события касания.
Для разработчиков iOS, новых для Android, attachToRoot
установлено значение true, вы вызываете этот метод:
[parent addSubview:inflatedView];
Если продолжить, вы можете спросить: зачем мне передавать родительское представление, если я устанавливаю attachToRoot
в false
? Это связано с тем, что корневой элемент в вашем XML-дереве нуждается в родительском представлении для вычисления некоторых LayoutParams (например, родительский элемент соответствия).
Ответ 9
Когда вы определяете родителя, attachToRoot определяет, хотите ли вы, чтобы надуватель действительно привязывал его к родительскому объекту или нет. В некоторых случаях это вызывает проблемы, например, в ListAdapter, это вызовет исключение, потому что список пытается добавить представление в список, но он говорит, что он уже прикреплен. В другом корпусе, где вы просто раздуваете представление, чтобы добавить его в Activity, это может быть удобно и сохранить строку кода.
Ответ 10
Например, мы имеем ImageView
, a LinearLayout
и a RelativeLayout
. LinearLayout является дочерним элементом RelativeLayout.
Иерархия представления будет.
RelativeLayout
------->LinearLayout
и у нас есть отдельный файл макета для ImageView
image_view_layout.xml
Прикрепить к корню:
//here container is the LinearLayout
View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
- Здесь v содержит ссылку на макет контейнера, то есть
LinearLayout.and, если вы хотите установить такие параметры, как
setImageResource(R.drawable.np);
ImageView, вам придется найти его по ссылке parent i.e view.findById()
- Родитель v будет FrameLayout.
- LayoutParams будет из FrameLayout.
Не присоединяться к root:
//here container is the LinearLayout
View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
- Здесь v содержит макет ссылочного контейнера без ссылки
ссылку на ImageView, который завышен, поэтому вы можете установить его
параметры, такие как
view.setImageResource(R.drawable.np);
без
ссылаясь как findViewById
. Но контейнер указан так, что
ImageView получает LayoutParams контейнера, чтобы вы могли сказать
что ссылка на контейнер только для LayoutParams ничего
иначе.
- поэтому в конкретном случае родитель будет null.
- LayoutParams будет LinearLayout.
Ответ 11
attachToRoot Установите значение true:
Если для attachToRoot установлено значение true, то файл макета, указанный в первом параметре, накачивается и присоединяется к ViewGroup, указанному во втором параметре.
Предположим, мы указали кнопку в файле макета XML с его шириной макета и высотой макета, установленной в match_parent.
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/custom_button">
</Button>
Теперь мы хотим программно добавить эту кнопку в LinearLayout внутри фрагмента или действия. Если наш LinearLayout уже является переменной-членом, mlinearLayout, мы можем просто добавить кнопку со следующим:
inflater.inflate(R.layout.custom_button, mLinearLayout, true);
Мы указали, что хотим раздуть Button из его файла ресурсов макета; мы затем сообщим LayoutInflater, что мы хотим прикрепить его к mlinearLayout. Наши параметры компоновки выполнены, потому что мы знаем, что Button добавляется в LinearLayout. Тип параметров макета кнопок должен быть LinearLayout.LayoutParams.
attachToRoot Установите значение false (не требуется использовать false)
Если для attachToRoot установлено значение false, тогда файл макета, указанный в первом параметре, будет завышен и не, прикрепленный к ViewGroup, указанному во втором параметре, но это завышенное представление приобретает родительский LayoutParams, который позволяет этому представлению правильно входить в родительский элемент.
Давайте посмотрим, когда вы хотите установить attachToRoot в false. В этом случае представление, указанное в первом параметре inflate(), не привязано к ViewGroup во втором параметре в данный момент времени.
Вспомним наш пример кнопки из ранее, где мы хотим добавить пользовательскую кнопку из файла макета в mlinearLayout. Мы все еще можем привязать нашу Button к mlinearLayout, передав false для attachToRoot - мы просто вручную добавим ее сами после этого.
Button button = (Button) inflater.inflate(R.layout.custom_button, mLinearLayout, false);
mLinearLayout.addView(button);
Эти две строки кода эквивалентны тому, что мы написали ранее в одной строке кода, когда мы передали true для attachToRoot. Переходя в false, мы говорим, что мы еще не хотим прикреплять наш View к корневой ViewGroup. Мы говорим, что это произойдет в какой-то другой момент времени. В этом примере другой момент времени - это просто метод addView(), используемый непосредственно под инфляцией.
Пример false attachToRoot требует немного больше работы, когда мы вручную добавляем View в ViewGroup.
attachToRoot Установите значение false (требуется false)
При раздувании и возврате представления фрагментов в onCreateView() обязательно передайте false для attachToRoot. Если вы передадите true, вы получите исключение IllegalStateException, потому что указанный ребенок уже имеет родителя. Вы должны указать, где будет отображаться ваш фрагмент в вашей деятельности. Задача FragmentManagers - добавлять, удалять и заменять фрагменты.
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.root_viewGroup);
if (fragment == null) {
fragment = new MainFragment();
fragmentManager.beginTransaction()
.add(R.id.root_viewGroup, fragment)
.commit();
}
Контейнер root_viewGroup, который будет содержать ваш фрагмент в вашей деятельности, - это параметр ViewGroup, предоставленный вам в onCreateView() в вашем фрагменте. Его также ViewGroup вы переходите в LayoutInflater.inflate(). Однако FragmentManager будет обрабатывать ваш просмотр фрагментов в этой группе ViewGroup. Вы не хотите прикреплять его дважды. Установите attachToRoot в false.
public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup, false);
…
return view;
}
Почему мы предоставляем родительскую группу ViewGroup Fragments в первую очередь, если мы не хотим прикреплять ее в onCreateView()? Почему метод inflate() запрашивает корневую ViewGroup?
Оказывается, что даже если мы не сразу добавляем наш недавно завышенный вид в родительскую ViewGroup, мы все равно должны использовать родителей LayoutParams, чтобы новый вид определял его размер и положение всякий раз, когда он в конечном итоге привязан.
Ссылка: https://youtu.be/1Y0LlmTCOkM?t=409