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

LayoutInflater.inflateдокументация не совсем ясна для меня о целиattachToRootпараметр.

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

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

11 ответов

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

Основное различие между "третьим" параметром 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 v = inflater.inflate(R.layout.image_fragment,parent,false);
        .....
        return true
  }

если вы передадите третий параметр как true, вы получите IllegalStateException из-за этого парня.

getSupportFragmentManager()
      .beginTransaction()
      .add(parent, childFragment)
      .commit();

Поскольку вы уже добавили дочерний фрагмент в onCreateView() по ошибке. Вызов add скажет вам, что дочернее представление уже добавлено к родительскому элементу. Следовательно, IllegalStateException.
Здесь вы не несете ответственности за добавление childView, за это отвечает FragmentManager. Так что всегда передавайте false в этом случае.

ПРИМЕЧАНИЕ: я также прочитал, что parentView не получит childView touchEvents, если attachToRoot имеет значение false. Но я не проверял это все же.

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

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

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

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

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

Я написал этот ответ, потому что даже пройдя несколько страниц Stackru, я не смог четко понять, что означает 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 напрямую прикрепляет раздутый макет к указанному родительскому элементу. Который в данном случае является корневым LinearLayout. Здесь нам не нужно добавлять представления вручную, как мы делали в предыдущем случае с методом addView(View view).

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

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

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)

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

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,

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

родитель:

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

attachToRoot:

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

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

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

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

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

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

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

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

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

[parent addSubview:inflatedView];

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

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. В этом случае представление, указанное в первом параметре inflate(), не прикреплено к ViewGroup во втором параметре в данный момент времени.

Вспомните наш пример Button из предыдущего, где мы хотим присоединить пользовательскую кнопку Button из файла макета к 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?

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

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

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

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

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

Разрешите пояснить на примере:

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

затем с помощью attachToRoot в качестве истинной функции раздувать возвращает вид типа LinearLayout

в то время как на использование attachToRoot в качестве ложной функции раздувать возвращает вид типа TextView

Надеюсь, эта находка поможет...

Например, у нас есть 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.and, если вы хотите установить параметры, такие как 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.

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

Другие вопросы по тегам