A LayoutInflater.inflate
documentação não está exatamente clara para mim sobre o objetivo do attachToRoot
parâmetro.
attachToRoot : se a hierarquia inflada deve ser anexada ao parâmetro raiz? Se falso, a raiz será usada apenas para criar a subclasse correta de LayoutParams para a visualização raiz no XML.
Alguém poderia explicar com mais detalhes, especificamente qual é a visão raiz, e talvez mostrar um exemplo de mudança de comportamento entre valores true
e false
?
android
android-layout
android-view
layout-inflater
Jeff Axelrod
fonte
fonte
Respostas:
AGORA OU NÃO AGORA
A principal diferença entre o "terceiro" parâmetro attachToRoot ser verdadeiro ou falso é esse.
true: adicione a visão filho ao pai RIGHT NOW
false: adicione a visão filho ao pai NOT NOW .
Adicione depois. `
Mais tarde é quando você usa, por exemplo,
parent.addView(childView)
Um equívoco comum é que, se o parâmetro attachToRoot for false, a visualização filho não será adicionada ao pai. ERRADO
Nos dois casos, a exibição filho será adicionada ao parentView. É apenas uma questão de tempo .
é equivalente a
UM GRANDE NÃO-NÃO
Você nunca deve passar como attachToRoot como verdadeiro quando não for responsável por adicionar a exibição filho ao pai.
Por exemplo, ao adicionar fragmento
se você passar o terceiro parâmetro como verdadeiro, receberá IllegalStateException por causa desse cara.
Como você já adicionou o fragmento filho no onCreateView () por engano. Chamar add informará que a exibição filho já foi adicionada ao pai. Portanto, IllegalStateException .
Aqui você não é responsável por adicionar childView, o FragmentManager é responsável. Portanto, sempre passe falso neste caso.
NOTA: Também li que parentView não obterá childView touchEvents se o attachToRoot for falso. Mas eu ainda não testei.
fonte
FragmentManager
, obrigado!Se definido como true, quando o layout for inflado, ele será automaticamente adicionado à hierarquia de exibição do ViewGroup especificado no segundo parâmetro como filho. Por exemplo, se o parâmetro raiz era a
LinearLayout
, sua visualização inflada será automaticamente adicionada como filha dessa visualização.Se estiver definido como false, seu layout será inflado, mas não será anexado a nenhum outro layout (para que não seja desenhado, receba eventos de toque etc.).
fonte
false
paraattachToRoot
durante a minha Fragmento deonCreateView
. Isso resolveu o problema e ainda o layout do fragmento é visível e ativa, apesar de sua resposta. O que está acontecendo aqui?true
, a visualização é anexada ao segundo parâmetro, que é ocontainer
, mas você diz que o fragmento é automaticamente anexado a partir deonCreateView()
, de acordo com o meu entendimento, o terceiro parâmetro é inútil e deve ser definidofalse
sempre?onCreateView
. Se você inflar layouts adicionais nessa visualização raiz ou se estiver inflando em um contexto diferente (por exemplo, em uma Atividade), será útil.Parece muito texto nas respostas, mas nenhum código, por isso decidi reviver essa pergunta antiga com um exemplo de código, em várias respostas mencionadas pelas pessoas:
O que isso realmente significa no código (o que a maioria dos programadores entende) é:
Observe que o código anterior está adicionando o layout
R.layout.child_view
como filho porMyCustomLayout
causa deattachToRoot
param istrue
e atribui os parâmetros de layout do pai exatamente da mesma maneira como se eu estivesse usandoaddView
programaticamente ou como se fizesse isso em xml:O código a seguir explica o cenário ao passar
attachRoot
comofalse
:No código anterior, você especifica que deseja
myView
ser seu próprio objeto raiz e não o anexa a nenhum pai, posteriormente o adicionamos como parte daLinearLayout
mas, por um momento, foi uma exibição independente (sem pai).O mesmo acontece com os fragmentos, você pode adicioná-los a um grupo já existente e fazer parte dele ou apenas passar os parâmetros:
Para especificar que será sua própria raiz.
fonte
A documentação e as duas respostas anteriores devem ser suficientes, apenas alguns pensamentos meus.
O
inflate
método é usado para aumentar os arquivos de layout. Com esses layouts inflados, você tem a possibilidade de anexá-los diretamente a um paiViewGroup
ou simplesmente aumentar a hierarquia de exibição desse arquivo de layout e trabalhar com ele fora da hierarquia de exibição normal.No primeiro caso, o
attachToRoot
parâmetro deverá ser definido comotrue
(ou muito simples, use oinflate
método que utiliza um arquivo de layout e uma raiz paiViewGroup
(nãonull
)). Nesse caso, oView
retorno é simplesmente oViewGroup
que foi passado no método,ViewGroup
ao qual a hierarquia de exibição inflada será adicionada.Para a segunda opção, o retornado
View
é a raizViewGroup
do arquivo de layout. Se você se lembra da nossa última discussão dainclude-merge
questão do par, este é um dos motivos damerge
limitação (quando um arquivo de layout commerge
raiz é inflado, você deve fornecer um pai eattachedToRoot
deve estar definido comotrue
). Se você tinha um arquivo de layout com a raiz umamerge
tag eattachedToRoot
foi definido comofalse
, oinflate
método não terá nada para retornar, poismerge
não tem um equivalente. Além disso, como diz a documentação, ainflate
versãoattachToRoot
configurada comofalse
é importante porque você pode criar a hierarquia de visualizações com a corretaLayoutParams
dos pais. Isso é importante em alguns casos, principalmente com os filhos deAdapterView
, uma subclasse deViewGroup
, para os quais oaddView()
conjunto de métodos não é suportado. Tenho certeza de que você se lembra de usar esta linha nogetView()
método:Essa linha garante que o
R.layout.row_layout
arquivo inflado tenha o corretoLayoutParams
daAdapterView
subclasse definida em sua raizViewGroup
. Se você não estiver fazendo isso, poderá ter alguns problemas com o arquivo de layout se a raiz for aRelativeLayout
. ElesTableLayout/TableRow
também têm algumas coisas especiais e importantesLayoutParams
e você deve garantir que as visualizações estejam corretasLayoutParams
.fonte
Eu também estava confuso sobre o que era o verdadeiro propósito
attachToRoot
noinflate
método. Depois de um pouco de estudo da interface do usuário, finalmente recebi a resposta:pai:
nesse caso, é o widget / layout que envolve os objetos de exibição que você deseja aumentar usando findViewById ().
attachToRoot:
anexa as visualizações aos pais (inclui-as na hierarquia dos pais), para que qualquer evento de toque recebido pelas visualizações também seja transferido para a visualização pai. Agora cabe aos pais se eles querem entreter esses eventos ou ignorá-los. se definido como false, eles não serão adicionados como filhos diretos do pai e o pai não receberá nenhum evento de toque das visualizações.
Espero que isso limpe a confusão
fonte
Escrevi esta resposta porque, mesmo depois de passar por várias páginas do StackOverflow, não consegui entender claramente o significado de attachToRoot. Abaixo está o método inflate () na classe LayoutInflater.
Dê uma olhada no arquivo activity_main.xml , no layout button.xml e no arquivo MainActivity.java que criei.
activity_main.xml
button.xml
MainActivity.java
Quando executamos o código, não vemos o botão no layout. Isso ocorre porque o layout do nosso botão não é adicionado ao layout principal da atividade, pois o attachToRoot está definido como false.
O LinearLayout possui um método addView (exibição de exibição) que pode ser usado para adicionar Views ao LinearLayout. Isso adicionará o layout do botão ao layout principal da atividade e tornará o botão visível quando você executar o código.
Vamos remover a linha anterior e ver o que acontece quando configuramos attachToRoot como true.
Mais uma vez, vemos que o layout do botão é visível. Isso ocorre porque attachToRoot anexa diretamente o layout inflado ao pai especificado. Que neste caso é LinearLayout raiz. Aqui, não precisamos adicionar as visualizações manualmente, como fizemos no caso anterior, com o método addView (View view).
Por que as pessoas estão obtendo IllegalStateException ao definir attachToRoot como true para um Fragmento.
Isso ocorre porque, para um fragmento, você já especificou onde colocar seu layout de fragmento em seu arquivo de atividade.
O add (int parent, Fragment fragment) adiciona o fragmento que possui seu layout ao layout pai. Se definirmos attachToRoot como true, você receberá IllegalStateException: O filho especificado já tem um pai. Como o layout do fragmento já foi adicionado ao layout pai no método add ().
Você sempre deve passar false para attachToRoot ao inflar fragmentos. O trabalho do FragmentManager é adicionar, remover e substituir fragmentos.
De volta ao meu exemplo. E se fizermos as duas coisas?
Na primeira linha, LayoutInflater anexa o layout do botão ao layout raiz e retorna um objeto View que contém o mesmo layout do botão. Na segunda linha, adicionamos o mesmo objeto View ao layout raiz pai. Isso resulta na mesma IllegalStateException que vimos com fragmentos (o filho especificado já tem um pai).
Lembre-se de que existe outro método inflate () sobrecarregado, que define o padrão attachToRoot como true por padrão.
fonte
Há muita confusão sobre esse tópico devido à documentação do método inflate ().
Em geral, se attachToRoot estiver configurado como true, o arquivo de layout especificado no primeiro parâmetro será inflado e anexado ao ViewGroup especificado no segundo parâmetro naquele momento. Quando attachToRoot é false, o arquivo de layout do primeiro parâmetro é inflado e retornado como um View e qualquer anexo do View acontece em outro momento.
Provavelmente isso não significa muito, a menos que você veja muitos exemplos. Ao chamar LayoutInflater.inflate () dentro do método onCreateView de um fragmento, você desejará passar false para attachToRoot porque a atividade associada a esse fragmento é realmente responsável por adicionar a exibição desse fragmento. Se você estiver inflando e adicionando manualmente uma View a outra View em algum momento posterior, como com o método addView (), convém passar false para attachToRoot porque o anexo será posteriormente.
Você pode ler sobre vários outros exemplos exclusivos sobre caixas de diálogo e visualizações personalizadas em uma postagem de blog que escrevi sobre esse mesmo tópico.
https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
fonte
attachToRoot
definido como true significainflatedView
que será adicionado à hierarquia da visualização pai. Assim, pode ser "visto" e detectar eventos de toque (ou quaisquer outras operações da interface do usuário) pelos usuários. Caso contrário, ele será criado, não será adicionado a nenhuma hierarquia de exibição e, portanto, não poderá ser visto ou manipular eventos de toque.Para desenvolvedores iOS novos no Android,
attachToRoot
definir como true significa que você chama este método:Se for mais longe, você poderá perguntar: Por que devo passar na visualização dos pais se eu definir
attachToRoot
comofalse
? Isso ocorre porque o elemento raiz em sua árvore XML precisa da visualização pai para calcular alguns LayoutParams (como combinar pai).fonte
Quando você define o pai, o attachToRoot determina se você deseja que o inflador realmente o anexe ao pai ou não. Em alguns casos, isso causa problemas, como em um ListAdapter, causa uma exceção, porque a lista tenta adicionar a exibição à lista, mas diz que já está anexada. Em outros casos em que você está apenas inflando a visualização para adicionar a uma Atividade, pode ser útil e economizar uma linha de código.
fonte
Por exemplo, temos um
ImageView
, umLinearLayout
e umRelativeLayout
. LinearLayout é o filho de RelativeLayout. a hierarquia de exibição será.e temos um arquivo de layout separado para o ImageView
image_view_layout.xml
Anexar à raiz:
setImageResource(R.drawable.np);
ImageView, será necessário encontrá-lo pela referência de pai, ou sejaview.findById()
Não anexar à raiz:
view.setImageResource(R.drawable.np);
sem indicar comofindViewById
. Mas container é especificado para que o ImageView obtenha os LayoutParams do container, para que você possa dizer que a referência do container é apenas para LayoutParams.fonte
attachToRoot Defina como true:
Imagine que especificamos um botão em um arquivo de layout XML com a largura e a altura do layout definidas como match_parent.
Agora, queremos adicionar programaticamente esse botão a um LinearLayout dentro de um fragmento ou atividade. Se nosso LinearLayout já é uma variável de membro, mLinearLayout, podemos simplesmente adicionar o botão com o seguinte:
Especificamos que queremos aumentar o Button a partir de seu arquivo de recursos de layout; então dizemos ao LayoutInflater que queremos anexá-lo ao mLinearLayout. Nossos parâmetros de layout são respeitados porque sabemos que o botão é adicionado a um LinearLayout. O tipo de parâmetro de layout do Button deve ser LinearLayout.LayoutParams.
attachToRoot Defina como false (não é necessário usar false)
Vamos dar uma olhada em quando você deseja definir o attachToRoot como false. Nesse cenário, a exibição especificada no primeiro parâmetro de inflate () não é anexada ao ViewGroup no segundo parâmetro neste momento.
Lembre-se do nosso exemplo de botão anterior, onde queremos anexar um botão personalizado de um arquivo de layout ao mLinearLayout. Ainda podemos anexar nosso Button ao mLinearLayout, passando false para attachToRoot - apenas adicionamos manualmente depois.
Essas duas linhas de código são equivalentes ao que escrevemos anteriormente em uma linha de código quando passamos true para attachToRoot. Ao passar em false, dizemos que não queremos anexar nossa View ao ViewGroup raiz ainda. Estamos dizendo que isso acontecerá em algum outro momento. Neste exemplo, o outro momento é simplesmente o método addView () usado imediatamente abaixo da inflação.
O exemplo false attachToRoot requer um pouco mais de trabalho quando adicionamos manualmente a View a um ViewGroup.
attachToRoot Defina como false (false é obrigatório)
Ao inflar e retornar a Visualização de um fragmento em onCreateView (), certifique-se de passar false para attachToRoot. Se você for verdadeiro, você receberá uma IllegalStateException porque o filho especificado já possui um pai. Você deveria ter especificado onde a visão do seu Fragmento será colocada novamente em sua Atividade. O trabalho do FragmentManager é adicionar, remover e substituir fragmentos.
O contêiner root_viewGroup que manterá seu Fragmento em sua Atividade é o parâmetro ViewGroup fornecido a você em onCreateView () em seu Fragmento. É também o ViewGroup que você passa para LayoutInflater.inflate (). No entanto, o FragmentManager tratará de anexar a visualização do seu fragmento a este grupo de visualizações. Você não deseja anexá-lo duas vezes. Defina attachToRoot como false.
Por que recebemos o ViewGroup pai do nosso Fragment em primeiro lugar, se não queremos anexá-lo ao onCreateView ()? Por que o método inflate () solicita um ViewGroup raiz?
Acontece que, mesmo quando não estamos adicionando imediatamente nosso recém-inflado View ao seu ViewGroup pai, ainda devemos usar o LayoutParams do pai para que o novo View determine seu tamanho e posição sempre que for anexado.
Link: https://youtu.be/1Y0LlmTCOkM?t=409
fonte
Apenas compartilhando alguns pontos que encontrei ao trabalhar neste tópico,
Além da resposta aceita, quero alguns pontos que podem ser de alguma ajuda.
Portanto, quando usei o attachToRoot como true, a exibição retornada é do tipo ViewGroup, ou seja, o ViewGroup raiz dos pais, que foi passado como parâmetro para o método inflate (layoutResource, ViewGroup, attachToRoot) , não do tipo do layout que foi transmitido, mas no attachToRoot como false, obtemos o tipo de retorno de função do ViewGroup raiz desse layoutResource .
Deixe-me explicar com um exemplo:
Se temos um LinearLayout como o layout raiz e queremos adicionar o TextView nele através da função inflar .
em seguida, usando attachToRoot como verdadeira função inflar retorna uma Visualização do tipo LinearLayout
enquanto estiver usando o attachToRoot como função de inflamento falso , retorna uma Visualização do tipo TextView
Espero que este achado seja de alguma ajuda ...
fonte