Que signifie le paramètre LayoutInflater attachToRoot?
la documentation LayoutInflater.inflate
n'est pas très claire pour moi sur le but du paramètre attachToRoot
.
attachToRoot : la hiérarchie gonflée doit-elle être attachée au paramètre racine? Si false, root n'est utilisé que pour créer la bonne sous-classe de LayoutParams pour la vue racine en XML.
quelqu'un Pourrait-il expliquer en détail, en particulier ce que la vue racine est, et peut-être montrer un exemple d'un changement de comportement entre les valeurs true
et false
?
11 réponses
si défini à true, alors lorsque votre mise en page est gonflée, il sera automatiquement ajouté à la hiérarchie de vue du groupe de vue spécifié dans le 2e paramètre en tant qu'enfant. Par exemple, si le paramètre root était un LinearLayout
, alors votre vue gonflée sera automatiquement ajoutée en tant qu'enfant de cette vue.
si elle est définie à false, alors votre mise en page sera gonflée mais ne sera pas attachée à une autre mise en page (donc elle ne sera pas dessinée, ne recevra pas d'événements touch, etc.).
semble comme 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 les gens ont mentionné:
si défini à true alors quand votre disposition est gonflée il sera automatiquement ajouté à la hiérarchie de vue du groupe de vue spécifié dans le 2e paramètre comme un enfant.
ce que cela signifie réellement dans le code(ce que la plupart des programmeurs comprennent) est:
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 le layout R.layout.child_view
comme enfant de MyCustomLayout
à cause de attachToRoot
param est true
et attribue les paramètres de la mise en page du parent exactement de la même manière que si j'utilisais addView
programmatiquement, ou comme si je l'avais fait en xml:
<LinearLayout> <View.../> ... </LinearLayout>
le code suivant explique le scénario en passant attachRoot
par 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 spécifiez que vous vouliez que myView
soit son propre objet racine et ne l'attachez à aucun parent, plus tard nous l'avons ajouté dans le cadre du LinearLayout
mais pour un moment c'é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 tout simplement passer les paramètres:
inflater.inflate(R. layout.fragment, nul, faux);
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 mise en page. Avec ces mises en page gonflées vous avez la possibilité de les attacher directement à un parent ViewGroup
ou tout simplement gonfler la hiérarchie de vue à partir de ce fichier de mise en page et de travailler avec elle en dehors de la hiérarchie de vue normale.
dans le premier cas le paramètre attachToRoot
aura pour être défini à true
(ou très simple utiliser la méthode inflate
qui prend un fichier de mise en page et une racine parent ViewGroup
(non null
)). Dans ce cas le View
retourné est simplement le ViewGroup
qui a été passé dans la méthode, le ViewGroup
auquel la hiérarchie de vue gonflée sera ajoutée.
pour la deuxième option, View
est la racine ViewGroup
du fichier de mise en page. Si vous vous souvenez de notre dernière discussion de la include-merge
question de paire c'est l'une des raisons de la limitation de merge
(quand un fichier de mise en page avec merge
comme root est gonflé, vous devez fournir un parent et attachedToRoot
doit être réglé à true
). Si vous aviez un fichier de mise en page avec la racine, une étiquette merge
et attachedToRoot
a été définie à false
, alors la méthode inflate
n'aura rien à retourner car merge
n'a pas d'équivalent.
Aussi, comme le dit la documentation, la version inflate
avec attachToRoot
définie à false
est importante parce que vous pouvez créer la hiérarchie de vue avec le LayoutParams
correct du 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 supporté. Je suis sûr que vous vous rappelez avoir utilisé cette ligne dans la getView()
méthode:
convertView = inflater.inflate(R.layout.row_layout, parent, false);
cette ligne garantit que le gonflé Le fichier R.layout.row_layout
contient le fichier correct LayoutParams
de la sous-classe AdapterView
placée sur sa racine ViewGroup
. Si vous ne le faisiez pas, vous pourriez avoir des problèmes avec le fichier de mise en page si la racine était un RelativeLayout
. Le TableLayout/TableRow
ont également quelques spécial et important LayoutParams
et vous devez vous assurer que les vues en eux ont le correct LayoutParams
.
MAINTENANT OU PAS MAINTENANT
la principale différence entre le" troisième " paramètre attachToRoot étant vrai ou faux est ceci.
quand vous mettez attachToRoot
vrai : ajouter à la vue enfant à parent DROIT MAINTENANT
faux: ajouter à la vue enfant à parent PAS MAINTENANT .
Ajouter par la suite.
quand est-ce que plus tard ?
qui plus tard est quand vous utilisez par exemple parent.addView(childView)
une idée fausse courante est, si le paramètre attachToRoot est faux, alors la vue enfant ne sera pas ajoutée à parent. WRONG
Dans les deux cas, l'enfant sera ajouté à parentView. Il s'agit simplement de time .
inflater.inflate(child,parent,false);
parent.addView(child);
est équivalent à
inflater.inflate(child,parent,true);
A BIG NO-NO
Vous ne devez jamais passer attachToRoot comme vrai lorsque vous n'êtes pas responsable de l'ajout de la vue enfant à parent.
Eg lors de l'ajout de Fragment
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
}
si vous passez troisième paramètre comme true vous obtiendrez IllegalStateException à cause de ce type.
getSupportFragmentManager()
.beginTransaction()
.add(parent, childFragment)
.commit();
puisque vous avez déjà ajouté le fragment enfant dans onCreateView() par erreur. Appeler add vous dira que la vue enfant est déjà ajoutée à parent D'où IllegalStateException .
Ici, vous n'êtes pas responsable de l'ajout de childView, FragmentManager est responsable. Donc toujours passer faux dans ce cas.
NOTE: j'ai aussi lu que parentView n'obtiendra pas de touchEvents childView si attachToRoot est faux. Mais je ne l'ai pas testé.
j'étais moi-même confus au sujet de ce qui était le but réel de attachToRoot
dans inflate
méthode. Après un peu d'étude D'UI, j'ai finalement obtenu la réponse:
parent:
dans ce cas est le widget/layout qui entoure les objets de vue que vous voulez gonfler en utilisant findViewById().
attachToRoot:
attache les vues à leur parent (inclut dans la hiérarchie parent), donc tout événement tactile que le point de vue de recevoir seront également transférés à la vue parent. Maintenant, c'est jusqu'à la mère si elle veut divertir les événements ou les ignorer. si défini à false, ils ne sont pas ajoutés en tant qu'enfants directs du parent et le parent ne reçoit pas d'événements de toucher des vues.
Espère que cela efface la confusion
il y a beaucoup de confusion sur ce sujet en raison de la documentation pour la méthode inflate ().
en général, si attachToRoot est défini à true, alors le fichier de mise en page spécifié dans le premier paramètre est gonflé et attaché au groupe de vue spécifié dans le deuxième paramètre à ce moment dans le temps. Lorsque attachToRoot est false, le fichier de mise en page du premier paramètre est gonflé et retourné comme une vue et toute fixation de vue se produit à un autre moment.
cela ne signifie probablement pas grand chose à moins que vous voyez beaucoup d'exemples. En appelant LayoutInflater.inflate() à l'intérieur de la méthode onCreateView d'un Fragment, vous voudrez passer dans false for attachToRoot parce que l'activité associée à ce Fragment est en fait responsable de l'ajout de la vue de ce Fragment. Si vous gonflez manuellement et ajoutez une vue à une autre vue à un moment ultérieur, comme avec la méthode addView (), vous voudrez passer dans false for attachToRoot parce que l'attache vient à un moment plus tard dans le temps.
vous pouvez lire sur plusieurs autres exemples uniques concernant les dialogues et les vues personnalisées sur un billet de blog que j'ai écrit sur ce même sujet.
https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
attachToRoot
défini à true signifie que le inflatedView
sera ajouté à la hiérarchie de la vue mère. Ainsi, il est possible que les utilisateurs puissent "voir" et détecter des événements tactiles (ou toute autre opération D'interface utilisateur). Dans le cas contraire, il est simplement créé, il n'a pas été ajouté à une hiérarchie de vues et ne peut donc pas être vu ou gérer des événements tactiles.
pour les développeurs iOS nouveaux à Android, attachToRoot
mis à true signifie que vous appelez cette méthode:
[parent addSubview:inflatedView];
si on va plus loin vous pourriez vous demander: Pourquoi devrais-je passer la vue parent si je mets attachToRoot
à false
? C'est parce que l'élément root dans votre arborescence XML a besoin de la vue parent pour calculer quelques LayoutParams (comme match parent).
j'ai écrit cette réponse parce que même après avoir passé en revue plusieurs pages empilées, Je n'ai pas pu saisir clairement ce que attachToRoot signifiait. Ci-dessous est la méthode inflate() dans la classe LayoutInflater.
View inflate (int resource, ViewGroup root, boolean attachToRoot)
regardez activity_main.xml fichier", 1519120920" bouton.xml et la MainActivity .java fichier 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>
Bouton .xml
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
activité principale.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);
}
quand on exécute le code, on ne voit pas le bouton dans la mise en page. Cela est dû au fait que notre disposition de bouton n'est pas ajoutée dans la disposition principale d'activité puisque attachToRoot est défini à false.
LinearLayout a une méthode addView(View view) qui peut être utilisée pour ajouter des vues à LinearLayout. Cela ajoutera le bouton layout dans la mise en page de l'activité principale, et rendre le bouton visible lorsque vous exécutez le code.
root.addView(view);
supprimons la ligne précédente, et voyons ce qui se passe lorsque nous définissons attachToRoot comme true.
View view = inflater.inflate(R.layout.button, root, true);
de nouveau, nous voyons que la disposition du bouton est visible. C'est parce que attachToRoot attache directement la disposition gonflée au parent spécifié. Ce qui dans ce cas est la racine de LinearLayout. Ici, nous n'avons pas à ajouter les vues manuellement comme nous l'avons fait dans le cas précédent avec la méthode addView(View view).
pourquoi les gens obtiennent-ils IllegalStateException en mettant attachToRoot comme vrai pour un Fragment.
c'est parce que pour un fragment vous avez déjà spécifié où placer votre disposition de fragment dans votre fichier d'activité.
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.root, fragment)
.commit();
le ajouter(parent int, fragment de Fragment) ajoute le fragment qui a sa disposition à la mère de mise en page. Si nous définissons attachToRoot comme true, vous obtiendrez IllegalStateException: l'enfant spécifié a déjà un parent. Depuis fragment layout est déjà ajouté au layout parent dans la méthode add ().
vous devez toujours passer false pour attachToRoot lorsque vous gonflez des Fragments. C'est le travail du FragmentManager d'ajouter, d'enlever et de remplacer des Fragments.
retour à mon exemple. Que faire si nous ne les deux.
View view = inflater.inflate(R.layout.button, root, true);
root.addView(view);
dans la première ligne, LayoutInflater attache le bouton layout au layout racine et renvoie un objet View qui contient le même bouton layout. Dans la deuxième ligne, nous ajoutons le même objet View à la disposition racine mère. Il en résulte la même Exception illégale que nous avons vue avec des 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 comme true par défaut.
View inflate (int resource, ViewGroup root)
quand vous définissez le parent l'attachToRoot détermine si vous voulez que le gonfleur l'attache effectivement au parent ou non. Dans certains cas, cela cause des problèmes, comme dans un ListAdapter, cela causera une exception parce que la liste essaie d'ajouter la vue à la liste mais elle dit qu'elle est déjà jointe. Dans d'autres cas où vous gonflez la vue vous-même pour ajouter à une activité, il pourrait être pratique et vous sauver une ligne de code.
Par exemple, nous avons un ImageView
, un LinearLayout
et un RelativeLayout
. LinearLayout est l'enfant de RelativeLayout.
le point de Vue de la Hiérarchie sera.
RelativeLayout
------->LinearLayout
et nous avons un fichier de mise en page séparé pour ImageView
image_view_layout.xml
attache à la racine:
//here container is the LinearLayout
View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
- ici v contient le référence de la disposition du conteneur I. le
LinearLayout.et si vous voulez définir les paramètres comme
setImageResource(R.drawable.np);
D'ImageView vous devrez le trouver par la référence de parent I. eview.findById()
- Parent de v sera le FrameLayout.
- LayoutParams sera de FrameLayout.
ne pas attacher à la racine:
//here container is the LinearLayout
View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
- disposition du conteneur de référence, mais directe
référence à L'ImageView qui est gonflé de sorte que vous pouvez définir son
paramètres comme
view.setImageResource(R.drawable.np);
sans arbitrage commefindViewById
. Mais conteneur est spécifié de sorte que ImageView reçoit les paramètres de mise en page du conteneur pour que vous puissiez dire que la référence de conteneur est juste pour LayoutParams rien autre. - donc dans un cas particulier Parent sera nul.
- LayoutParams sera de LinearLayout.
attachToRoot true:
si attachToRoot est défini à true, alors le fichier de mise en page spécifié dans le premier paramètre est gonflé et attaché au groupe de vue spécifié dans le second paramètre.
Imaginez que nous ayons spécifié un bouton dans un fichier de mise en page XML avec sa largeur et sa hauteur de mise en page réglées à 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 ce Bouton pour 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 Suivant:
inflater.inflate(R.layout.custom_button, mLinearLayout, true);
nous avons spécifié que nous voulons gonfler le bouton à partir de son fichier de ressources layout; nous disons alors à LayoutInflater que nous voulons le fixer à mLinearLayout. Nos paramètres de mise en page sont honorés parce que nous savons que le bouton est ajouté à une sortie en ligne. Le Le type de Params de Button doit être LinearLayout.LayoutParams.
attachToRoot réglé à false (non requis d'utiliser false)
si attachToRoot est défini à false, alors le fichier de mise en page spécifié dans le premier paramètre est gonflé et pas attaché au groupe de vue spécifié dans le deuxième paramètre mais que gonflé view acquiert le LayoutParams de parent qui permet à cette vue de s'adapter correctement dans le parent.
regardons quand vous voulez mettre attachToRoot à false. Dans ce scénario, la vue spécifiée dans le premier paramètre de inflate() n'est pas attachée au groupe de vue dans le second paramètre à ce stade.
rappeler notre exemple de bouton de plus tôt, où nous voulons attacher un bouton personnalisé à partir d'un fichier de mise en page à mLinearLayout. Nous pouvons toujours attacher notre bouton à mLinearLayout en passant dans false pour attachToRoot-nous l'ajoutons manuellement nous-mêmes après.
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 plus tôt dans une ligne de code quand nous sommes passés dans true for attachToRoot. En passant dans false, nous disons que nous ne voulons pas attacher notre point de vue au groupe de vue racine pour le moment. Nous disons que c' se produira à un moment donné dans le temps. Dans cet exemple, l'autre point dans le temps est simplement la méthode addView() utilisée immédiatement au-dessous de l'inflation.
l'exemple false attachToRoot nécessite un peu plus de travail lorsque nous ajoutons manuellement la vue à un groupe de vue.
attachToRoot réglé sur false(false est requis)
Lors du gonflage et du retour de la vue D'un Fragment dans onCreateView(), be sûr de passer dans false pour attachToRoot. Si vous passez dans true, vous obtiendrez une exception IllegalStateException parce que l'enfant spécifié a déjà un parent. Vous auriez dû préciser où la vue de votre Fragment sera placée de nouveau dans votre activité. C'est le travail du FragmentManager d'ajouter, d'enlever 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 retiendra votre Fragment dans votre activité est le paramètre ViewGroup qui vous est donné dans onCreateView () dans votre Fragment. C'est aussi le groupe de vue que vous passez dans LayoutInflater.gonfler.)( Le FragmentManager se chargera de fixer la vue de votre Fragment à ce groupe de vue, cependant. Vous ne voulez pas l'attacher deux fois. Réglez 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 donne-t-on le groupe de vue parent de notre Fragment en premier lieu si nous ne voulons pas l'attacher dans onCreateView()? Pourquoi la méthode inflate () demande-t-elle un groupe de vue racine?
Il s'avère que même si nous n'ajoutons pas immédiatement notre vue nouvellement gonflée à son groupe de vue parent, nous devrions quand même utiliser les paramètres de mise en page du parent afin que la nouvelle vue détermine sa taille et sa position chaque fois qu'elle est éventuellement attachée.