Что означает параметр attachToRoot LayoutInflater?

LayoutInflater.inflate не совсем ясна для меня в отношении назначения параметра attachToRoot.

attachToRoot: следует ли прикреплять раздутую иерархию к корневому параметру? Если false, корень используется только для создания правильного подкласса LayoutParams для корневого представления в XML.

Может ли кто-нибудь объяснить более подробно, в частности, что такое корневое представление, и, возможно, показать пример изменения поведения между значениями true и false?


person Jeff Axelrod    schedule 24.09.2012    source источник
comment
Связано: Понимание LayoutInflater   -  person blahdiblah    schedule 10.10.2014
comment
Дубликат: stackoverflow.com/questions/22326314/   -  person mikebabcock    schedule 13.01.2018


Ответы (12)


СЕЙЧАС ИЛИ НЕ СЕЙЧАС

Основное различие между «третьим» параметром attachToRoot, являющимся истинным или ложным, заключается в следующем.

Когда вы ставите attachToRoot

true : добавить дочернее представление к родительскому ПРЯМО СЕЙЧАС
false: добавить дочернее представление к родительскому НЕ СЕЙЧАС.
Добавьте позже. `

Когда это позже?

Это позже, когда вы используете, например, 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 не получит дочерние события touchEvents, если для attachToRoot установлено значение false. Но я не проверял это, хотя.

person Rohit Singh    schedule 22.08.2017
comment
Очень полезно, особенно часть, касающаяся FragmentManager, спасибо! - person CybeX; 25.12.2018
comment
очень хорошо объяснил. это должно быть частью документов Android. мне было непонятно, когда я читал документы Android - person Shayan_Aryan; 17.03.2021

Если установлено значение true, то, когда ваш макет будет раздут, он будет автоматически добавлен в иерархию представлений ViewGroup, указанную во втором параметре как дочерний элемент. Например, если корневым параметром было LinearLayout, то ваше расширенное представление будет автоматически добавлено в качестве дочернего элемента этого представления.

Если для него установлено значение false, тогда ваш макет будет увеличен, но не будет прикреплен к какому-либо другому макету (поэтому он не будет отображаться, получать события касания и т. д.).

person Joseph Earl    schedule 24.09.2012
comment
Я смущен. Я получал указанный дочерний элемент, у которого уже есть родительская ошибка», пока я не прочитал этот ответ, который указал мне использовать false для attachToRoot во время моего Фрагмента onCreateView. Это решило проблему, и все же макет фрагмента виден и активен, несмотря на ваш ответ. Что тут происходит? - person Jeff Axelrod; 28.09.2012
comment
Поскольку фрагмент автоматически прикрепляет макет, возвращенный из onCreateView. Поэтому, если вы прикрепите его вручную в onCreateView, ваше представление будет привязано к 2 родителям (что приводит к упомянутой вами ошибке). - person Joseph Earl; 28.09.2012
comment
Я немного запутался здесь, @JosephEarl, вы сказали, что если установлено значение true, представление прикрепляется ко второму параметру, который является container, но затем вы говорите, что фрагмент автоматически прикрепляется из onCreateView(), поэтому, насколько я понимаю, третий параметр бесполезен и должен быть установлен false всегда? - person unmultimedio; 17.07.2014
comment
Вы возвращаете представление в oncreateview, после чего оно автоматически прикрепляется. Если вы установите для подключения значение true, будет выдана ошибка. Однако, когда вы расширяете представление в автономной ситуации, вы можете выбрать автоматическое присоединение представления к его контейнеру, установив значение true. Я почти никогда не устанавливал значение true, поскольку я всегда добавляю вид сам. - person frostymarvelous; 22.07.2014
comment
@unmultimedio бесполезен только для корневого представления, возвращаемого onCreateView. Если вы расширяете дополнительные макеты в этом корневом представлении или расширяете в другом контексте (например, в действии), тогда это полезно. - person Joseph Earl; 03.02.2015
comment
Каковы другие способы установить для третьего параметра значение true? - person Neon Warge; 30.11.2015
comment
Не нарисовано? Я думаю, что передача нулевого родителя по-прежнему будет отображать представление, хотя он не будет устанавливать параметр attachToRoot, поскольку не будет родителя для присоединения. Я также могу сказать, что он также будет получать события касания, но они не распространяются на родителя (поскольку у них его нет). Что-то упустил? - person stdout; 11.05.2016

Похоже, что в ответах много текста, но нет кода, поэтому я решил возродить этот старый вопрос с примером кода, в нескольких ответах люди упомянули:

Если установлено значение 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 равен 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);

Чтобы указать, что это будет его собственный корень.

person Martin Cazares    schedule 07.01.2014
comment
Из всех, это было самым полезным. - person Wahib Ul Haq; 20.06.2017

Документации и двух предыдущих ответов должно быть достаточно, просто некоторые мысли от меня.

Метод 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.

person user    schedule 24.09.2012

Я сам тоже был озадачен тем, какова была реальная цель метода attachToRoot в inflate. После небольшого изучения пользовательского интерфейса я наконец получил ответ:

родитель:

в данном случае это виджет/макет, окружающий объекты представления, которые вы хотите раздуть с помощью findViewById().

прикрепить к корневому каталогу:

прикрепляет представления к их родителю (включает их в родительскую иерархию), поэтому любое событие касания, полученное представлениями, также будет передано родительскому представлению. Теперь это зависит от родителя, хочет ли он развлекать эти события или игнорировать их. если установлено значение false, они не добавляются как прямые дочерние элементы родителя, и родитель не получает никаких событий касания из представлений.

Надеюсь, это устранит путаницу

person Umer Farooq    schedule 24.03.2014
comment
Ваш ответ уже предоставлен здесь: stackoverflow.com/questions/22326314/ - person Neon Warge; 30.11.2015

Я написал этот ответ, потому что даже после просмотра нескольких страниц 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>

кнопка.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), который можно использовать для добавления представлений в LinearLayout. Это добавит макет кнопки в макет основного действия и сделает кнопку видимой при запуске кода.

root.addView(view);

Давайте удалим предыдущую строку и посмотрим, что произойдет, если установить для attachToRoot значение true.

View view = inflater.inflate(R.layout.button, root, true);

Мы снова видим, что макет кнопки виден. Это связано с тем, что attachToRoot напрямую прикрепляет раздутый макет к указанному родителю. Что в данном случае является корневым LinearLayout. Здесь нам не нужно добавлять представления вручную, как в предыдущем случае с методом addView(View view).

Почему люди получают исключение IllegalStateException, если для фрагмента установлено значение true для attachToRoot.

Это связано с тем, что для фрагмента вы уже указали, где разместить макет фрагмента в файле активности.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .add(R.id.root, fragment)
    .commit();

Команда add(int parent, Fragment fragment) добавляет фрагмент с его макетом в родительский макет. Если мы установим для attachToRoot значение true, вы получите исключение IllegalStateException: указанный дочерний элемент уже имеет родителя. Так как макет фрагмента уже добавлен в родительский макет в методе add().

Вы должны всегда передавать значение false для attachToRoot при расширении фрагментов. Задача FragmentManager — добавлять, удалять и заменять фрагменты.

Вернемся к моему примеру. Что, если мы сделаем и то, и другое.

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)
person capt.swag    schedule 10.10.2017
comment
Простое и понятное объяснение, как раз то, что я искал! - person flyingAssistant; 07.04.2020
comment
Это ЕДИНСТВЕННЫЙ ответ здесь, который объясняет, что attachToRoot по умолчанию устанавливается на true, если он опущен (что противоречит интуиции) - person El Sushiboi; 30.10.2020
comment
понятное объяснение!! устраняет путаницу по этому поводу - person Asutosh Panda; 25.02.2021

В этой теме много путаницы из-за документации по методу inflate().

В общем, если для attachToRoot установлено значение true, то файл макета, указанный в первом параметре, раздувается и прикрепляется к ViewGroup, указанному во втором параметре, в этот момент времени. Если для attachToRoot установлено значение false, файл макета из первого параметра увеличивается и возвращается как представление, а любое вложение представления происходит в другое время.

Это, вероятно, не имеет большого значения, если вы не видите много примеров. При вызове LayoutInflater.inflate() внутри метода onCreateView фрагмента вы захотите передать значение false для attachToRoot, поскольку действие, связанное с этим фрагментом, фактически отвечает за добавление представления этого фрагмента. Если вы вручную расширяете представление и добавляете его в другое представление в какой-то более поздний момент времени, например, с помощью метода addView(), вы захотите передать false для attachToRoot, потому что вложение появляется в более поздний момент времени.

Вы можете прочитать о нескольких других уникальных примерах, касающихся диалогов и пользовательских представлений, в сообщении блога, которое я написал на эту тему.

https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/

person seanjfarrell    schedule 23.02.2016

Значение attachToRoot равно true означает, что inflatedView будет добавлено в иерархию родительского представления. Таким образом, пользователи могут «видеть» и воспринимать сенсорные события (или любые другие операции пользовательского интерфейса). В противном случае он только что был создан, не был добавлен ни в какую иерархию представлений и, следовательно, не может быть виден или обрабатывать события касания.

Для разработчиков iOS, плохо знакомых с Android, attachToRoot, установленное в true, означает, что вы вызываете этот метод:

[parent addSubview:inflatedView];

Если пойти дальше, вы можете спросить: почему я должен передавать родительское представление, если я установил attachToRoot в false? Это связано с тем, что корневой элемент в вашем XML-дереве нуждается в родительском представлении для вычисления некоторых параметров LayoutParams (например, сопоставления родителя).

person Alston    schedule 21.12.2015

Когда вы определяете родителя, attachToRoot определяет, хотите ли вы, чтобы инфлятор действительно прикреплял его к родителю или нет. В некоторых случаях это вызывает проблемы, например, в ListAdapter это вызовет исключение, потому что список пытается добавить представление в список, но говорит, что оно уже прикреплено. В другом случае, когда вы просто расширяете представление, чтобы добавить его в действие, это может быть удобно и сэкономит вам строку кода.

person CaseyB    schedule 24.09.2012
comment
не дает четкой картины, которую должен дать хороший ответ. - person Prakhar1001; 13.03.2018

Например, у нас есть ImageView , LinearLayout и 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);
  1. Здесь v содержит ссылку на макет контейнера, то есть LinearLayout. И если вы хотите установить такие параметры, как setImageResource(R.drawable.np); для ImageView, вам нужно будет найти его по ссылке родителя, то есть view.findById()
  2. Родителем v будет FrameLayout.
  3. LayoutParams будет иметь значение FrameLayout.

Не подключаться к корневому каталогу:

//here container is the LinearLayout
    View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
  1. Здесь v содержит не ссылочный макет контейнера, а прямую ссылку на ImageView, который раздут, поэтому вы можете установить его параметры, такие как view.setImageResource(R.drawable.np);, не ссылаясь на findViewById. Но контейнер указан так, что ImageView получает LayoutParams контейнера, поэтому вы можете сказать, что ссылка на контейнер предназначена только для LayoutParams и ничего больше.
  2. поэтому в конкретном случае Parent будет нулевым.
  3. LayoutParams будет LinearLayout.
person Faisal Naseer    schedule 28.02.2017

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);

Мы указали, что хотим надуть кнопку из ее файла ресурсов макета; затем мы сообщаем LayoutInflater, что хотим присоединить его к mLinearLayout. Наши параметры макета учитываются, потому что мы знаем, что Button добавляется в LinearLayout. Тип параметров макета кнопки должен быть LinearLayout.LayoutParams.

attachToRoot Установите значение false (не обязательно использовать false)

Если для параметра attachToRoot задано значение false, то файл макета, указанный в первом параметре, расширяется и не присоединяется к ViewGroup, указанному во втором параметре, но это расширенное представление получает родительские параметры LayoutParams. что позволяет этому представлению правильно вписаться в родителя.


Давайте посмотрим, когда вы захотите установить для attachToRoot значение false. В этом сценарии View, указанный в первом параметре inflate(), не присоединен к ViewGroup во втором параметре в данный момент времени.

Вспомните наш предыдущий пример с кнопкой, где мы хотим прикрепить пользовательскую кнопку из файла макета к mLinearLayout. Мы по-прежнему можем прикрепить нашу кнопку к mLinearLayout, передав false для attachToRoot — мы просто вручную добавим ее позже.

Button button = (Button) inflater.inflate(R.layout.custom_button,    mLinearLayout, false);
mLinearLayout.addView(button);

Эти две строки кода эквивалентны тому, что мы написали ранее в одной строке кода, когда передавали значение true для attachToRoot. Передавая false, мы говорим, что пока не хотим присоединять наш View к корневой ViewGroup. Мы говорим, что это произойдет в какой-то другой момент времени. В этом примере другой момент времени — это просто метод addView(), используемый сразу после инфляции.

Ложный пример attachToRoot требует немного больше работы, когда мы вручную добавляем View в ViewGroup.

attachToRoot Установите значение false (false требуется)

При раздувании и возврате представления фрагмента в onCreateView() обязательно передайте false для attachToRoot. Если вы передадите значение true, вы получите исключение IllegalStateException, поскольку у указанного дочернего элемента уже есть родитель. Вы должны были указать, где будет находиться представление вашего фрагмента в вашей деятельности. Задача FragmentManager — добавлять, удалять и заменять фрагменты.

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 нашего Фрагмента, если мы не хотим присоединять ее в onCreateView()? Почему метод inflate() запрашивает корневую группу ViewGroup?

Оказывается, даже если мы не сразу добавляем наше новое расширенное представление в его родительскую группу ViewGroup, мы все равно должны использовать LayoutParams родителя, чтобы новый Просмотр, чтобы определить его размер и положение, когда он в конечном итоге будет присоединен.

Ссылка: https://youtu.be/1Y0LlmTCOkM?t=409

person Utshaw    schedule 26.06.2017

Просто делюсь некоторыми моментами, с которыми я столкнулся при работе над этой темой,

В дополнение к принятому ответу я хочу указать некоторые моменты, которые могут быть полезны.

Итак, когда я использовал attachToRoot как true, возвращаемое представление имело тип ViewGroup, т. е. родительскую корневую ViewGroup. который был передан в качестве параметра для метода inflate(layoutResource,ViewGroup,attachToRoot) не типа макета, который был передан, а на attachToRoot как false, мы получаем тип возвращаемого функцией корня ViewGroup этого layoutResource.

Поясню на примере:

Если у нас есть LinearLayout в качестве корневого макета, а затем мы хотим добавить TextView в него с помощью функции inflate.

затем при использовании attachToRoot в качестве true функция наполнения возвращает представление< /strong> типа LinearLayout

в то время как при использовании attachToRoot в качестве false функция наполнения возвращает представление< /strong> типа TextView

Надеюсь, что эта находка будет чем-то полезна...

person HARIS UMAID    schedule 04.06.2020