web-dev-qa-db-fra.com

Utilisation de <include> avec <merge> dans ConstraintLayout

Je ne parviens pas à utiliser les balises <include> et <merge> à l'intérieur d'un objet ConstraintLayout. 

Je souhaite créer une hiérarchie de vues à plat (donc des contraintes), tout en conservant des éléments réutilisables. J'utilise donc <include> dans ma mise en page et <merge> dans les mises en page incluses pour éviter d'avoir des mises en page imbriquées (en particulier pour éviter l'imbrication de ConstraintLayouts)

J'ai donc écrit ceci:

<Android.support.constraint.ConstraintLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <include
        Android:id="@+id/review_1"
        layout="@layout/view_movie_note"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/review_2"/>

    <include
        layout="@layout/view_movie_note"
        Android:id="@+id/review_2"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        Android:layout_marginLeft="7dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/review_1"
        app:layout_constraintRight_toRightOf="parent"
        />

</Android.support.constraint.ConstraintLayout>

et cette view_movie_note: 

<merge>

    <TextView
        Android:id="@+id/note_Origin"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginBottom="15dp"
        Android:layout_marginStart="5dp"
        app:layout_constraintStart_toStartOf="@+id/cardView2"
        app:layout_constraintTop_toTopOf="parent"
        Android:layout_marginLeft="5dp" />


    <Android.support.v7.widget.CardView
        Android:id="@+id/five_star_view_container"
        Android:layout_width="0dp"
        Android:layout_height="52dp"
        Android:layout_marginBottom="8dp"
        Android:layout_marginTop="10dp"
        Android:elevation="3dp"
        app:cardUseCompatPadding="true"
        app:contentPaddingTop="22dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_min="52dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/note_Origin">

        <FiveStarsView
            Android:id="@+id/five_star_view"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_gravity="center_horizontal" />

    </Android.support.v7.widget.CardView>

    <Android.support.v7.widget.CardView
        Android:id="@+id/cardView2"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginTop="20dp"
        app:cardBackgroundColor="@color/colorPrimary"
        app:contentPaddingLeft="15dp"
        app:contentPaddingRight="15dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/note_Origin">

        <TextView
            Android:id="@+id/grade"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:textSize="12sp" />

    </Android.support.v7.widget.CardView>


</merge>

Je m'attends à cela

 I am expecting this

Au lieu de cela, j'ai ce

 Instead I get this

Il est clair que les contraintes que je mets dans la balise <include> sont remplacées par les contraintes de la disposition incluse. 

Est-ce le comportement attendu? Si oui, comment devons-nous conserver une mise en page plate en utilisant <include> et ConstraintLayout?

22
JDenais

Réponse courte

_ {Le meilleur choix sera de remplacer le bloc <merge> par une (variable) ConstraintLayout plutôt que d'utiliser une structure de présentation redondante.




ConstraintLayout est génial mais il ne fonctionne pas bien avec la composition et séparation des responsabilités de chaque pièce

C'est faux. ConstraintLayout fonctionne bien avec la réutilisation de présentations. Toute disposition dans laquelle toutes les vues enfants sont disposées en fonction des relations entre les vues frères et la disposition parente se comporte exactement comme ceci. Cela est vrai même pour RelativeLayout.


Alors, où est le problème?

Regardons de plus près ce que <merge> est.

Le doc dit 

La balise <merge/> permet d’éliminer les groupes de vues redondants dans votre vue hiérarchie lors de l’inclusion d’une mise en page dans une autre.

Cela aura le même effet que de remplacer l'élément <include> par le contenu du bloc <merge>. En d'autres termes, les vues du bloc <merge/> sont directement placées dans la disposition parente sans groupe de vues intermédiaire. Par conséquent, les contraintes de l'élément <include> sont complètement ignorées.

Dans cet exemple particulier, les vues de la disposition y compris sont ajoutées deux fois au parent en tant que deuxième au-dessus d'une autre.


Conclusion

Les fichiers de ressources de mise en page sont destinés à être utilisés indépendamment. Pour qualifier le terme réutilisable, il ne devrait pas dépendre de son parent (groupe de vues dans lequel il sera ajouté à l'avenir). Si vous ne deviez inclure la mise en page qu’une seule fois, cela vous irait. Mais </merge> ne sera pas une bonne idée dans ce cas aussi, car vous ne pouvez pas le placer dans une mise en page différente à une position différente.

De toute évidence, les hiérarchies de mise en page à plat offrent de meilleures performances. Cependant, nous pouvons parfois devoir le sacrifier.

9
Anees

Documentation Android dit

La balise <merge /> permet d’éliminer les groupes de vues redondants dans votre vue hiérarchie lors de l'inclusion d'une mise en page dans une autre

et a aussi un exemple

Si votre mise en page principale est une verticale LinearLayout dans laquelle Deux vues consécutives peuvent être réutilisées dans plusieurs mises en page, puis le La mise en page réutilisable dans laquelle vous placez les deux vues nécessite sa propre vue racine. Cependant, utilisez un autre LinearLayout comme racine du fichier une disposition réutilisable entraînerait un LinearLayout vertical dans un vertical LinearLayout. LinearLayout imbriqué ne sert à rien autre que de ralentir les performances de votre interface utilisateur.

Voir aussi cette réponse , qui vous permettra de mieux comprendre la balise de fusion. 

Problème dans votre mise en page

Pour la mise en page enfant

Vous mettez des contraintes sur les éléments enfants à l'intérieur de la balise <merge. Ça ne va pas. Parce que ces contraintes sont détruites au moment de l'exécution lorsque les deux modèles enfants sont fusionnés dans votre modèle parent. (Vous me dites si vous pouvez le faire sans inclure la balise include, vos contraintes fonctionneront-elles?)

Pour la mise en page parent

Pareil pour la balise <include, vous attribuez des contraintes/attributs personnalisés à la balise <include, qui sera perdue, car la balise <merge est jointe à la vue racine. Vous ne pouvez donc pas appliquer d'attributs personnalisés à la balise <include avec <merge . C'est pourquoi Bahman réponse fonctionnera.

Les attributs sur la balise <include fonctionnent lorsque vous avez un élément racine dans la présentation enfant et la balise no <merge

Conclusion

Comme cela est clair, vous n'utilisez pas <merge et <include, comme il se doit. Vous avez compris ce que font les balises <include et <merge. Alors utilisez-les à bon escient.

Si vous demandez une solution

ConstraintLayout a été introduit pour résoudre une mise en page complexe. Ne pas augmenter la complexité. Alors, quand vous pouvez le faire facilement avec LinearLayout, pourquoi choisir Constraints.

Disposition des parents

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    >

    <include
        Android:id="@+id/review_1"
        layout="@layout/view_movie_note"
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        Android:layout_weight="1"
        />

    <include
        Android:id="@+id/review_2"
        layout="@layout/view_movie_note"
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        Android:layout_marginLeft="7dp"
        Android:layout_weight="1"
        />

</LinearLayout>

view_movie_note.xml

<Android.support.constraint.ConstraintLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content">

    <TextView
     .../>

    <Android.support.v7.widget.CardView
    ...
    </Android.support.v7.widget.CardView>

    <Android.support.v7.widget.CardView
    ...
    </Android.support.v7.widget.CardView>
</Android.support.constraint.ConstraintLayout>

 output

J'espère que je pourrais vous faire bien comprendre.

3
Khemraj

Comme solution

Layout parent  enter image description here

<?xml version="1.0" encoding="utf-8"?>
<Android.support.constraint.ConstraintLayout 
 xmlns:Android="http://schemas.Android.com/apk/res/Android"
 xmlns:app="http://schemas.Android.com/apk/res-auto"
 Android:layout_width="match_parent"
 Android:layout_height="match_parent">


<Android.support.v7.widget.LinearLayoutCompat
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:weightSum="2">

    <include
        Android:id="@+id/review_1"
        layout="@layout/view_movie_note"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/review_2"
        app:layout_constraintTop_toTopOf="parent" />

    <include
        Android:id="@+id/review_2"
        layout="@layout/view_movie_note"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginLeft="7dp"
        Android:layout_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/review_1"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

 </Android.support.v7.widget.LinearLayoutCompat>


</Android.support.constraint.ConstraintLayout>

view_movie_note 2]

<?xml version="1.0" encoding="utf-8"?>


<Android.support.constraint.ConstraintLayout 
  xmlns:Android="http://schemas.Android.com/apk/res/Android"
  xmlns:app="http://schemas.Android.com/apk/res-auto"
  Android:layout_width="match_parent"
  Android:layout_height="wrap_content">

<TextView
    Android:id="@+id/note_Origin"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_marginBottom="15dp"
    Android:layout_marginLeft="5dp"
    Android:layout_marginStart="5dp"
    app:layout_constraintStart_toStartOf="@+id/cardView2"
    app:layout_constraintTop_toTopOf="parent" />


<Android.support.v7.widget.CardView
    Android:id="@+id/five_star_view_container"
    Android:layout_width="wrap_content"
    Android:layout_height="52dp"
    Android:layout_marginBottom="8dp"
    Android:layout_marginTop="10dp"
    Android:elevation="3dp"
    app:cardUseCompatPadding="true"
    app:contentPaddingTop="22dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHeight_min="52dp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/note_Origin">

    <!--<FiveStarsView-->
    <!--Android:id="@+id/five_star_view"-->
    <!--Android:layout_width="wrap_content"-->
    <!--Android:layout_height="wrap_content"-->
    <!--Android:layout_gravity="center_horizontal" />-->

    <RatingBar
        Android:id="@+id/ratingBar"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content" />
</Android.support.v7.widget.CardView>

<Android.support.v7.widget.CardView
    Android:id="@+id/cardView2"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_marginTop="20dp"
    app:cardBackgroundColor="@color/colorPrimary"
    app:contentPaddingLeft="15dp"
    app:contentPaddingRight="15dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@+id/note_Origin">

    <TextView
        Android:id="@+id/grade"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:textSize="12sp" />

  </Android.support.v7.widget.CardView>
</Android.support.constraint.ConstraintLayout>
2
Artem

Enveloppez les balises include avec les balises ConstraintLayout, puis déplacez les attributs des balises include vers ces nouvelles balises ConstraintLayout:

    <Android.support.constraint.ConstraintLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

    <Android.support.constraint.ConstraintLayout
            Android:id="@+id/review_1"
            Android:layout_width="0dp"
            Android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/review_2">

                    <include  layout="@layout/view_movie_note"  />

   </Android.support.constraint.ConstraintLayout>

    <Android.support.constraint.ConstraintLayout
            Android:id="@+id/review_2"
            Android:layout_width="0dp"
            Android:layout_height="0dp"
            Android:layout_marginLeft="7dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/review_1"
            app:layout_constraintRight_toRightOf="parent">

                       <include layout="@layout/view_movie_note" />

    </Android.support.constraint.ConstraintLayout>

  </Android.support.constraint.ConstraintLayout>
2
BAHMAN

la fusion est une balise et non un ViewGroup. Ainsi, tout le paramètre transmis à l'include sera ignoré ... Vous pouvez définir ce ViewGroup uniquement avec une présentation dupliquée. Si vous avez besoin de la gérer, vous pouvez créer un Group . ... Attributs XML de la disposition de fusion à RelativeLayout via inflate

2
Anis BEN NSIR

le fichier view_movie_note.xml doit avoir un seul nœud xml à la racine - sinon, il n'y aura pas deux nœuds de présentation pouvant être alignés sur des contraintes (la balise <merge> peut être inutile). Le fait d'avoir deux présentations identiques avec resId identique ajoute au problème, car les contraintes sont toujours définies par rapport à une resId, ce qui n'est pas unique ici.

des approches alternatives seraient, a) d'utiliser un LinearLayout afin de distribuer deux éléments horizontalement - ou b) au cas où il y aurait même quelques nœuds enfants (que je supposerais), il serait préférable d'utiliser CardViews dans un GridLayout avec deux colonnes, dans un GridView / RecyclerView .

un seul CardView en tant que nœud racine d'une telle disposition enfant serait le moins à aligner correctement.

selon le résultat attendu, il n'est pas nécessaire d'utiliser une ConstraintLayout pour aligner les nœuds de la même manière. Avec la variable GridLayoutManager, vous pouvez même ajuster le nombre de colonnes en fonction de la taille d'affichage. Une StaggeredGridLayoutManager serait également une option lorsque la hauteur des éléments varie.

en principe, il est toujours plus facile de travailler avec le cadre que de travailler contre le cadre.

0
Martin Zeitler

Quelques problèmes avec votre question:

  1. Selon la documentation Android link

Vous pouvez également remplacer tous les paramètres de présentation (tous les attributs Android: layout_ *) de la vue racine de la présentation incluse en les spécifiant dans la balise <include /> Ainsi, toute contrainte que vous avez mise dans la balise include sera supprimée.

  1. Tout Android:id dans l'inclusion NE sera PAS remplacé si la balise de fusion est utilisée dans votre mise en page incluse. 

  2. Le chaînage et l'ajout de contraintes fonctionnent sur des vues ayant différents identifiants. Donc, pour inclure la même vue plusieurs fois avec le même poids ne fonctionnera pas avec la balise include.

Cela étant dit, vous pouvez soit copier coller tout le 

Par conséquent, vous ne pouvez pas utiliser include de cette manière. 

Il ne vous reste que 3 options:

  1. Utiliser un autre ViewGroup (LinearLayout puis disposition par contrainte, par exemple)
  2. Copier coller le contenu de la disposition include avec différents identifiants de vues
  3. Modifiez le code ConstraintLayout pour prendre en charge les chaînes de répartition de sorte que la présentation incluse entière soit copiée horizontalement. 

IMO, la 1ère option est préférable si vous avez un petit nombre de ces mises en page, la 2ème option est la meilleure si vous n'avez qu'une seule mise en page (posée en question) et la 3ème option est la meilleure si vous avez un grand nombre de mises en page.

0
Rahul Kumar