Что означает параметр 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