Le LayoutInflater.inflate
La documentation ne précise pas vraiment le but du paramètre attachToRoot
.
attachToRoot : la hiérarchie gonflée doit-elle être attachée au paramètre racine? Si la valeur est false, root est uniquement utilisé pour créer la sous-classe correcte de LayoutParams pour la vue racine dans le fichier XML.
Quelqu'un pourrait-il, s'il vous plaît, expliquer plus en détail, en particulier ce qu'est la vue racine, et peut-être montrer un exemple de changement de comportement entre les valeurs true
et false
?
Si défini sur true, lorsque votre présentation est gonflée, elle est automatiquement ajoutée à la hiérarchie de vues du groupe de vues spécifié dans le deuxième paramètre en tant qu'enfant. Par exemple, si le paramètre racine était un LinearLayout
, votre vue gonflée sera automatiquement ajoutée en tant qu'enfant de cette vue.
Si la valeur est false, votre mise en page sera gonflée mais ne sera attachée à aucune autre mise en page (elle ne sera donc pas dessinée, elle ne recevra pas d'événements tactiles, etc.).
La principale différence entre le "troisième" paramètre attachToRoot étant true ou false est la suivante.
Quand vous mettez attachToRoot
true: ajoute la vue enfant au parent RIGHT NOW
false: ajoute la vue enfant au parent NOT NOW.
Ajoutez-le plus tard. `
Quand est-ce que plus tard?
C'est plus tard que vous utilisez par exemple pour parent.addView(childView)
ne idée fausse commune est, si le paramètre attachToRoot est false, la vue enfant ne sera pas ajoutée au parent. FAUX
Dans les deux cas, la vue enfant sera ajoutée à parentView. C'est juste la question de temps.
inflater.inflate(child,parent,false);
parent.addView(child);
équivaut à
inflater.inflate(child,parent,true);
A BIG NO-NO
Vous ne devez jamais attribuer la valeur true à attachToRoot lorsque vous n'êtes pas responsable de l'ajout de la vue enfant au parent.
Par exemple, lors de l'ajout de Fragment
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;
}
si vous transmettez le troisième paramètre à true, vous obtiendrez IllegalStateException à cause de ce type.
getSupportFragmentManager()
.beginTransaction()
.add(parent, childFragment)
.commit();
Puisque vous avez déjà ajouté le fragment enfant à onCreateView () par erreur. L'appel de add vous dira que la vue enfant est déjà ajoutée au parent. Par conséquent IllegalStateException.
Ici, vous n'êtes pas responsable de l'ajout de childView, c'est FragmentManager qui en est responsable. Donc toujours passer faux dans ce cas.
NOTE: J'ai aussi lu que parentView ne recevra pas childView touchEvents si attachToRoot est false. Mais je ne l'ai pas testé cependant.
On dirait qu'il y a beaucoup de texte dans les réponses mais pas de code, c'est pourquoi j'ai décidé de faire revivre cette vieille question avec un exemple de code. Dans plusieurs réponses, des personnes ont mentionné:
Si défini sur true, lorsque votre présentation est gonflée, elle est automatiquement ajoutée à la hiérarchie de vues du groupe de vues spécifié dans le deuxième paramètre en tant qu'enfant.
Ce que cela signifie réellement dans le code (ce que la plupart des programmeurs comprennent) est le suivant:
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);
}
}
Notez que le code précédent ajoute la disposition R.layout.child_view
en tant qu'enfant de MyCustomLayout
en raison de attachToRoot
, param est true
et affecte les paramètres de présentation du parent de la même manière que si j'utilisais addView
par programme, ou comme si je faisais ceci en xml:
<LinearLayout> <View.../> ... </LinearLayout>
Le code suivant explique le scénario lorsqu’on passe attachRoot
en tant que 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);
Dans le code précédent, vous indiquez que vous vouliez que myView
soit son propre objet racine et ne l'attachez à aucun parent. Nous l'avons ajouté ultérieurement dans le cadre du LinearLayout
, mais pour un instant, il était une vue autonome (pas de parent).
La même chose se produit avec les fragments, vous pouvez les ajouter à un groupe déjà existant et en faire partie, ou simplement passer les paramètres:
inflater.inflate (R.layout.fragment, null, false);
Pour spécifier que ce sera sa propre racine.
La documentation et les deux réponses précédentes devraient suffire, juste quelques réflexions de ma part.
La méthode inflate
est utilisée pour gonfler les fichiers de présentation. Avec ces présentations gonflées, vous avez la possibilité de les attacher directement à un parent ViewGroup
ou simplement de gonfler la hiérarchie de vues à partir de ce fichier de présentation et de les utiliser en dehors de la hiérarchie de vues normale.
Dans le premier cas, le paramètre attachToRoot
devra être défini sur true
(ou très simple, utilisez la méthode inflate
qui prend un fichier de mise en page et une racine parent ViewGroup
(non null
)). Dans ce cas, le View
renvoyé est simplement le ViewGroup
qui a été passé dans la méthode, le ViewGroup
auquel la hiérarchie de vues gonflée sera ajoutée.
Pour la deuxième option, le View
renvoyé est la racine ViewGroup
du fichier de présentation. Si vous vous souvenez de notre dernière discussion à partir de la question de la paire include-merge
c'est l'une des raisons de la limitation de merge
(lorsqu'un fichier de mise en page avec merge
lorsque la racine est gonflée, vous devez fournir un parent et attachedToRoot
doit être défini sur true
). Si vous avez un fichier de mise en page avec la racine, une balise merge
et attachedToRoot
est défini sur false
alors la méthode inflate
n'aura rien à renvoyer en tant que merge
n'a pas d'équivalent. De plus, comme le dit la documentation, la version inflate
avec attachToRoot
définie sur false
est importante car vous pouvez créer la hiérarchie de vues avec le correct LayoutParams
de la parent. Ceci est important dans certains cas, notamment avec les enfants de AdapterView
, une sous-classe de ViewGroup
, pour laquelle l'ensemble de méthodes addView()
n'est pas pris en charge. Je suis sûr que vous vous rappelez avoir utilisé cette ligne dans la méthode getView()
:
convertView = inflater.inflate(R.layout.row_layout, parent, false);
Cette ligne garantit que le fichier R.layout.row_layout
Gonflé contient le LayoutParams
correct de la sous-classe AdapterView
définie sur sa racine ViewGroup
. Si vous ne le faisiez pas, vous pourriez avoir des problèmes avec le fichier de disposition si la racine était un RelativeLayout
. Les TableLayout/TableRow
Ont aussi des LayoutParams
spéciaux et importants et vous devez vous assurer que les vues qu’ils contiennent ont le bon LayoutParams
.
J'étais moi-même également confus quant à l'objectif réel de attachToRoot
dans la méthode inflate
. Après un peu d'étude de l'interface utilisateur, j'ai finalement eu la réponse:
parent:
dans ce cas, il s'agit du widget/de la disposition entourant les objets de vue que vous souhaitez gonfler à l'aide de findViewById ().
attachToRoot:
attache les vues à leur parent (les inclut dans la hiérarchie parent), donc tout événement tactile que les vues reçoivent sera également transféré à la vue parent. À présent, le parent peut décider s'il souhaite traiter ces événements ou les ignorer . Si la valeur est false, ils ne sont pas ajoutés en tant qu'enfants directs du parent et le parent ne reçoit aucun événement tactile des vues.
J'espère que cela efface la confusion
Il y a beaucoup de confusion sur ce sujet en raison de la documentation de la méthode inflate ().
En règle générale, si attachToRoot est défini sur true, le fichier de disposition spécifié dans le premier paramètre est gonflé et lié au groupe ViewGroup spécifié dans le second paramètre à ce moment précis. Lorsque attachToRoot a la valeur false, le fichier de présentation du premier paramètre est gonflé et renvoyé sous forme de vue. Toute pièce jointe de vue se produit à un autre moment.
Cela ne veut probablement pas dire grand chose à moins que vous voyiez beaucoup d'exemples. Lorsque vous appelez LayoutInflater.inflate () à l'intérieur de la méthode onCreateView d'un fragment, vous souhaitez transmettre false pour attachToRoot car l'activité associée à ce fragment est en réalité responsable de l'ajout de la vue de ce fragment. Si vous gonflez et ajoutez manuellement une vue à une autre vue à un moment ultérieur, par exemple avec la méthode addView (), vous voudrez passer à false pour attachToRoot, car la pièce jointe est fournie à une date ultérieure.
Vous pouvez lire plusieurs exemples uniques concernant les dialogues et les vues personnalisées sur un article de blog que j'ai écrit à ce sujet.
https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
J'ai écrit cette réponse parce que, même après avoir consulté plusieurs pages StackOverflow, je n'ai pas pu comprendre clairement ce que attachToRoot voulait dire. Ci-dessous, la méthode inflate () de la classe LayoutInflater.
View inflate (int resource, ViewGroup root, boolean attachToRoot)
Jetez un coup d'œil au fichier activity_main.xml , button.xml et le fichier MainActivity.Java que j'ai créé.
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);
}
Lorsque nous exécutons le code, nous ne verrons pas le bouton dans la présentation. Cela est dû au fait que notre disposition de bouton n'est pas ajoutée à la disposition d'activité principale, car attachToRoot est défini sur false.
LinearLayout a une méthode addView (View View) qui peut être utilisée pour ajouter des vues à LinearLayout. Cela ajoutera la disposition du bouton dans la disposition de l'activité principale et le rend visible lorsque vous exécutez le code.
root.addView(view);
Supprimons la ligne précédente et voyons ce qui se passe lorsque nous affectons attachToRoot à true.
View view = inflater.inflate(R.layout.button, root, true);
Encore une fois, nous voyons que la disposition des boutons est visible. En effet, attachToRoot associe directement la présentation gonflée au parent spécifié. Qui dans ce cas est la racine LinearLayout. Ici, nous n’avons pas besoin d’ajouter manuellement les vues comme nous l’avions fait dans le cas précédent avec la méthode addView (View View).
Pourquoi les gens obtiennent-ils IllegalStateException lorsqu'ils définissent attachToRoot sur true pour un fragment?
En effet, pour un fragment, vous avez déjà indiqué où placer la présentation de ce fragment dans votre fichier d'activité.
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.root, fragment)
.commit();
Le add (int parent, fragment de fragment) ajoute le fragment qui a sa disposition à la disposition parente. Si nous définissons attachToRoot sur true, vous obtiendrez IllegalStateException: l'enfant spécifié a déjà un parent. Étant donné que la disposition des fragments est déjà ajoutée à la disposition parent dans la méthode add ().
Vous devez toujours indiquer false pour attachToRoot lorsque vous gonflez des fragments. C’est le travail de FragmentManager d’ajouter, de supprimer et de remplacer des fragments.
Revenons à mon exemple. Et si nous faisons les deux.
View view = inflater.inflate(R.layout.button, root, true);
root.addView(view);
Dans la première ligne, LayoutInflater associe la disposition du bouton à la disposition racine et renvoie un objet View contenant la même disposition. Dans la deuxième ligne, nous ajoutons le même objet View à la présentation de la racine parent. Cela se traduit par la même exception IllegalStateException que celle que nous avons vue avec Fragments (l'enfant spécifié a déjà un parent).
Gardez à l'esprit qu'il existe une autre méthode inflate () surchargée, qui définit attachToRoot sur true par défaut.
View inflate (int resource, ViewGroup root)
attachToRoot
défini sur true signifie que le inflatedView
sera ajouté à la hiérarchie de la vue parent. Ainsi, les utilisateurs peuvent éventuellement "voir" et détecter des événements tactiles (ou toute autre opération d'interface utilisateur). Sinon, il vient tout juste d'être créé, n'a été ajouté à aucune hiérarchie de vues et ne peut donc pas être vu ni gérer les événements tactiles.
Pour les développeurs iOS débutant sur Android, attachToRoot
défini sur true signifie que vous appelez cette méthode:
[parent addSubview:inflatedView];
Si vous voulez aller plus loin, vous pourriez vous demander: Pourquoi devrais-je passer de la vue parent si je règle attachToRoot
sur false
? C'est parce que l'élément racine de votre arborescence XML a besoin de la vue parent pour calculer certains LayoutParams (comme match parent).
attachToRoot Définissez sur true:
Si attachToRoot est défini sur true, le fichier de mise en forme spécifié dans le premier paramètre est gonflé et lié au groupe ViewGroup spécifié dans le second paramètre.
Imaginons que nous ayons spécifié un bouton dans un fichier de mise en page XML avec sa largeur et sa hauteur de mise définies pour 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>
Nous voulons maintenant ajouter par programmation ce bouton à un LinearLayout à l'intérieur d'un fragment ou d'une activité. Si notre LinearLayout est déjà une variable membre, mLinearLayout, nous pouvons simplement ajouter le bouton avec les éléments suivants:
inflater.inflate(R.layout.custom_button, mLinearLayout, true);
Nous avons spécifié que nous voulions gonfler le bouton à partir de son fichier de ressources de présentation; nous disons ensuite à LayoutInflater que nous voulons l’attacher à mLinearLayout. Nos paramètres de présentation sont respectés car nous savons que le bouton est ajouté à un LinearLayout. Le type de paramètres de disposition du bouton doit être LinearLayout.LayoutParams.
attachToRoot Défini sur false (il n'est pas obligatoire d'utiliser false)
Si attachToRoot est défini sur false, le fichier de présentation spécifié dans le premier paramètre est gonflé et n'est pas attaché au ViewGroup spécifié dans le second paramètre mais gonflé. view acquiert les LayoutParams du parent , ce qui lui permet de s’inscrire correctement dans le parent.
Examinons à quel moment vous souhaitez définir attachToRoot sur false. Dans ce scénario, la vue spécifiée dans le premier paramètre de inflate () n'est pas attachée au ViewGroup dans le deuxième paramètre à ce moment précis.
Rappelez-vous notre exemple de Button de précédemment, dans lequel nous souhaitons associer un bouton personnalisé d'un fichier de présentation à mLinearLayout. Nous pouvons toujours attacher notre Button à mLinearLayout en transmettant false pour attachToRoot - nous l'ajoutons manuellement nous-mêmes par la suite.
Button button = (Button) inflater.inflate(R.layout.custom_button, mLinearLayout, false);
mLinearLayout.addView(button);
Ces deux lignes de code sont équivalentes à ce que nous avons écrit précédemment dans une ligne de code lorsque nous avons passé true pour attachToRoot. En transmettant false, nous disons que nous ne voulons pas encore attacher notre View à la racine ViewGroup. Nous disons que cela se produira à un autre moment. Dans cet exemple, l'autre moment est simplement la méthode addView () utilisée immédiatement en dessous de l'inflation.
L'exemple false attachToRoot nécessite un peu plus de travail lorsque nous ajoutons manuellement la vue à un groupe de vues.
attachToRoot Défini sur false (false est requis)
Lorsque vous gonflez et renvoyez une vue de fragment dans onCreateView (), assurez-vous de transmettre false pour attachToRoot. Si vous transmettez true, vous obtiendrez une exception IllegalStateException car l'enfant spécifié a déjà un parent. Vous devriez avoir spécifié où la vue de votre fragment sera replacée dans votre activité. C’est le travail de FragmentManager d’ajouter, de supprimer et de remplacer des fragments.
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();
}
Le conteneur root_viewGroup qui contiendra votre fragment dans votre activité est le paramètre ViewGroup qui vous est attribué dans onCreateView () dans votre fragment. C'est aussi le ViewGroup que vous passez dans LayoutInflater.inflate (). FragmentManager gérera toutefois l’attachement de la vue de votre fragment à ce groupe de vues. Vous ne voulez pas l'attacher deux fois. Définissez attachToRoot sur false.
public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup, false);
…
return view;
}
Pourquoi obtenons-nous le ViewGroup parent de notre fragment en premier lieu si nous ne voulons pas le joindre à onCreateView ()? Pourquoi la méthode inflate () demande-t-elle un groupe de vues racine?
Il s’avère que même si nous n’ajoutons pas immédiatement notre View nouvellement gonflée à son ViewGroup parent, nous devrions quand même utiliser les LayoutParams du parent afin que la nouvelle View détermine sa taille et sa position chaque fois qu’elle est attachée.
Lorsque vous définissez le parent, attachToRoot détermine si vous souhaitez que le système de gonflage l'attache réellement au parent ou non. Dans certains cas, cela pose des problèmes, comme dans un ListAdapter, cela provoquera une exception car la liste tente d'ajouter la vue à la liste mais indique qu'elle est déjà attachée. Dans les autres cas où vous ne faites que gonfler la vue vous-même pour l'ajouter à une activité, cela pourrait être pratique et vous faire économiser une ligne de code.
Par exemple, nous avons un ImageView
, un LinearLayout
et un RelativeLayout
. LinearLayout est l'enfant de RelativeLayout. la hiérarchie de vues sera.
RelativeLayout
------->LinearLayout
et nous avons un fichier de mise en page séparé pour ImageView
image_view_layout.xml
Attacher à la racine:
//here container is the LinearLayout
View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
setImageResource(R.drawable.np);
de ImageView, vous devrez le trouver à l'aide de la référence du parent i.e view.findById()
ne pas attacher à la racine:
//here container is the LinearLayout
View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
view.setImageResource(R.drawable.np);
sans faire référence à findViewById
. Mais conteneur est spécifié pour que ImageView obtienne les LayoutParams du conteneur afin que vous puissiez dire que la référence du conteneur est juste pour LayoutParams, rien d'autre.