Comment faire fonctionner RelativeLayout avec la fusion et l'inclusion?

114

J'essaie depuis quelques jours maintenant de rendre mes mises en page plus efficaces en passant de l'utilisation de plusieurs niveaux d'imbrication LinearLayoutsà un seul RelativeLayoutet j'ai rencontré quelques problèmes pour lesquels je n'ai pas été en mesure de trouver une solution de contournement ...

J'ai recherché le groupe de débutants Android et ce site et je n'ai rien trouvé qui m'aiderait à résoudre le problème.

J'ai lu sur l'un des blogs que vous pouvez combiner des mises en page avec fusion et inclure des balises. Donc, ce que j'ai est un fichier de mise en page principal avec un RelativeLayoutélément racine. À l'intérieur de cela, j'ai 5 balises d'inclusion qui font référence à 5 fichiers de mise en page XML différents qui ont chacun un élément de fusion pour la racine (tous mes fichiers de fusion sont les mêmes à l'exception des identifiants qu'ils contiennent).

Je rencontre deux problèmes, que j'expliquerai après avoir publié une version simplifiée de mon code de mise en page:

Exemple de fichier de mise en page principal:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/translucent_gray" >

    <include 
        android:id="@+id/running_gallery_layout_id"
        layout="@layout/running_gallery_layout" />

    <include 
        android:id="@+id/recent_gallery_layout_id" 
        layout="@layout/recent_gallery_layout"
        android:layout_below="@id/running_gallery_layout_id" />

    <include
        android:id="@+id/service_gallery_layout_id"
        layout="@layout/service_gallery_layout"
        android:layout_below="@id/recent_gallery_layout_id" />

    <include
        android:id="@+id/process_gallery_layout_id"
        layout="@layout/process_gallery_layout"
        android:layout_below="@id/service_gallery_layout_id" />

</RelativeLayout>

Exemple de fichier de fusion inclus:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView 
        style="@style/TitleText"
        android:id="@+id/service_gallery_title_text_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:text="@string/service_title" />

    <Gallery
        android:id="@+id/service_gallery_id"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_below="@id/service_gallery_title_text_id" />

    <TextView 
        style="@style/SubTitleText"
        android:id="@+id/service_gallery_current_text_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/service_gallery_title_text_id"
        android:layout_above="@id/service_gallery_id" />
</merge>

Je rencontre deux problèmes:

1) Les android:layout_*attributs semblent être ignorés lorsqu'ils sont utilisés dans la balise include et toutes les mises en page fusionnées sont affichées les unes sur les autres. Selon ce post ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) "n'importe quel android:layout_*attribut peut être utilisé avec la <include />balise"

2) Comme je ne pouvais pas faire fonctionner cela, j'ai décidé d'essayer d'ajouter un android:layout_belowattribut au premier TextViewélément de chaque fichier de mise en page de fusion, ce qui signifie que chaque fichier de fusion ferait référence à un identifiant d'un autre fichier de mise en page de fusion ... Pour la plupart, ceci effectivement travaillé et ma mise en page semble bien. Cependant, j'obtiens une erreur sur l'un des android:layout_belowattributs indiquant qu'il ne trouve pas l'ID que j'ai spécifié ... J'ai vérifié deux fois et trois fois les identifiants pour m'assurer qu'ils étaient corrects. La partie la plus étrange est que j'ai utilisé la AutoFillfonctionnalité pour mettre l'identifiant dans l'attribut en premier lieu.

Si quelqu'un a des suggestions ou des solutions de contournement, je serai plus qu'heureux de les essayer. De plus, si quelqu'un peut penser à un moyen pour moi d'avoir juste un fichier de mise en page XML de fusion au lieu de 5, ce serait grandement apprécié. Je n'ai pas trouvé de moyen de le faire car je dois avoir accès à chaque élément des fichiers de mise en page de fusion au moment de l'exécution ...

Justin
la source

Réponses:

214

Il y a un problème avec la balise include. Vérifiez: https://issuetracker.google.com/issues/36908001

Pour résoudre ce problème, assurez-vous d'écraser LES DEUX layout_widthet layout_heightlors de l'inclusion, sinon tout sera ignoré.

Macarse
la source
15
C'est une meilleure solution que celle acceptée, car elle évite de créer un objet de mise en page par ailleurs superflu. En outre, cela craint que, selon les développeurs Android, ce soit OK.
mikołak
4
C'est vraiment une solution plus simple, moins codée en dur et plus optimisée que de regrouper <include /> dans une autre mise en page. Pensez à ce que vous feriez si vous travailliez avec des listes.
teoREtik
2
Cela fonctionne beaucoup mieux que la réponse acceptée. Merci beaucoup! Et ... allez google, résolvez déjà ce problème, c'est BS! :)
Felipe Caldas
2
@JeffAxelrod, le code source de LayoutInflater montre que le remplacement des balises id, visibilité et layout_ * n'est malheureusement pas appliqué lorsque l'élément racine est une balise de fusion. Comme vous ne pouvez pas avoir de vue en tant que racine xml, nous devons avoir un ViewGroup supplémentaire là-bas ...
Rafael Nobre
13
Cela n'a tout simplement pas fonctionné pour moi. J'ai les deux layout_widthet je me suis layout_heightinstallé sur mon <include>. J'ai essayé aussi le réglage layout_widthet layout_heightsur mon <merge>mais sans succès. Qu'est-ce que je rate ?
dum4ll3
33

Voir la réponse la plus votée ci-dessous. Le mien est terriblement dépassé


Je peux résoudre un problème soulevé par Justin : l'incapacité de RelativeLayout à gérer le positionnement d'un include (au moins dans ce cas simple, sur un émulateur 1.6)

CommonsWare suggère l'emballage comprend dans un conteneur parent unique, mais le fait afin d'aider les aborder et la portée vues au sein de même nom de Justin comprend

Chacun devrait avoir un conteneur parent unique, et vous appelleriez findViewById () sur ce conteneur (ViewGroup) plutôt que sur l'activité.

En fait, vous devez également le faire pour que RelativeLayout se comporte comme prévu:

Cela fonctionne (le pied de page est bien positionné):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <include android:id="@+id/header" layout="@layout/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <LinearLayout android:layout_alignParentBottom="true"
        android:layout_height="wrap_content" android:layout_width="fill_parent">
        <include android:id="@+id/footer" layout="@layout/footer" />
    </LinearLayout>
</RelativeLayout>

Ce n'est pas le cas (le pied de page flotte en haut de l'écran):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <include android:id="@+id/header" layout="@layout/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <include android:id="@+id/footer" layout="@layout/footer"
        android:layout_alignParentBottom="true" />
</RelativeLayout>

L' inclusion de pied de page nu ne s'alignera pas au bas du parent, sans le LinearLayout environnant. Je n'appellerais pas ce comportement attendu.

De plus, le WebView semble bien s'attacher à l'en- tête par ID, mais je pense que c'est une illusion, car il s'écoule simplement sous l'en-tête verticalement. J'ai également essayé de définir un bouton juste au-dessus du pied de page, mais tout était flottant et faux aussi

RelativeLayout a eu plus de problèmes dans la version 1.5, mais je l'aime toujours :)

alienjazzcat
la source
2
Je l'ai augmenté de 1, puis j'ai diminué en voyant le commentaire de @Macarse, c'est la bonne façon de le faire.
Jayshil Dave
Veuillez supprimer cette réponse trompeuse :(
Daniel Smith
8

Mec, c'est vieux, mais il semble arriver en tête des recherches, alors je vais commenter.

Je pense que l'astuce ici est que la <merge>balise combinée avec la <include>balise supprime essentiellement toute sorte de groupe de vues "parent" à ce niveau. Alors, à qui demandez-vous exactement à quelqu'un d'autre "layout_below"? Personne. Il n'y a pas de vue à ce niveau.

La <merge>balise prend les vues enfants et les insère directement dans le parent de la <include>balise. Vous devez donc demander aux enfants de la mise en page que vous incluez de s'ancrer en conséquence.

Plantage
la source
4

Pour que le positionnement fonctionne sur RelativeLayout, vous devez définir les paramètres layout_ * dans le fichier include, et non dans le fichier layout principal. De cette façon

main_layout.xml

<RelativeLayout
  android:id="@+id/header"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">
   ....
</RelativeLayout>

<RelativeLayout 
  android:id="@+id/footer"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_alignParentBottom="true">
    .....
</RelativeLayout>

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

content_layout.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_above="@id/footer"
    android:layout_below="@id/header" >

    ....
</RelativeLayout>
</merge>

Ce n'est évidemment pas ce que nous souhaitons, les développeurs, mais c'est la seule solution que j'ai trouvée pour éviter de dupliquer xml

Maragues
la source
1
Pourquoi pas de votes positifs? Cela a fonctionné pour moi. Je ne suis pas amoureux de coller des paramètres dans un objet qui peut être inclus dans une mise en page qui n'en a pas besoin, mais s'il s'agit d'un LinLay, il semble que le layout_ * de RelLay soit simplement ignoré. Est-ce que je manque quelque chose?
QED
1

Les attributs android: layout_ * semblent être ignorés lorsqu'ils sont utilisés dans la balise include et toutes les mises en page fusionnées sont affichées les unes sur les autres.

Je suppose que vous ne pouvez pas référencer, à partir des règles de mise en page, les android:idattributs qui sont définis sur les <include>éléments, seulement ceux qui sont sur des widgets et des conteneurs «réels».

De plus, si quelqu'un peut penser à un moyen pour moi d'avoir juste un fichier de mise en page XML de fusion au lieu de 5, ce serait grandement apprécié.

Simple: mettez-les tous dans un seul fichier.

Je n'ai pas trouvé de moyen de le faire car je dois avoir accès à chaque élément dans les fichiers de mise en page de fusion au moment de l'exécution

Que vous ayez un <include>élément ou 1 000, tout le contenu doit être accessible au moment de l'exécution. Une exception est si vous avez des android:idattributs dupliqués - vous devez définir correctement vos findViewById()appels pour obtenir le bon, tout comme vous devez le faire lorsque vous obtenez des widgets à partir d'une ligne ListView.

Si vous pouvez créer un exemple de projet utilisant plus de 2 fichiers de fusion dans lequel vous pouvez démontrer que le contenu n'est pas accessible au moment de l'exécution, faites-le moi savoir.

CommonsWare
la source
1
Je ne veux pas les mettre tous dans un fichier de mise en page massif car c'est plus difficile à gérer à long terme ... C'est pourquoi j'ai demandé la possibilité d'avoir un xml principal avec un fichier de fusion ... parce que pour le moment J'ai 5 fichiers avec un élément de fusion comme racine qui ont exactement la même disposition, sauf que les identifiants sont différents. Je le fais de cette façon afin de pouvoir y accéder au moment de l'exécution. Il semble que la portée de mon appel findViewById () soit ce que je voudrais faire. Comment définissez-vous cet appel afin que vous puissiez inclure le même fichier de mise en page plusieurs fois et pouvoir toujours accéder à tous les composants au moment de l'exécution?
Justin
1
Merci pour la réponse. J'examinerai cela. En attendant, (et peut-être que j'expose mon ignorance ici) n'emballer chaque balise include dans un conteneur parent ne va-t-il pas à l'encontre de l'objectif d'utiliser RelativeLayout? Tout le battage médiatique de RelativeLayout est d'éviter les mises en page imbriquées ...
Justin
3
La seule raison pour laquelle j'ai demandé à ce sujet était d'économiser de l'espace et de proposer un bon design ... J'ai lu sur la réutilisabilité de la mise en page ici: developer.android.com/resources/articles/ ... et j'ai trouvé que cela sonnait bien. Lorsque j'ai essayé de le mettre en œuvre, j'ai rencontré des problèmes. Actuellement, j'ai 5 mises en page qui sont essentiellement dupliquées, à l'exception des identifiants qu'ils contiennent ... donc j'ai pensé que la réutilisabilité de la mise en page serait un bon candidat. Peut-être que je manque quelque chose ici, mais il semble que RelativeLayout n'est pas tout ce qu'il est censé être ...
Justin
1
Wow ... merci d'avoir été si incroyablement utile. En général, vos réponses sont assez utiles, donc je ne sais pas si vous passez juste une mauvaise journée ou quoi, mais j'essayais simplement de mieux comprendre les concepts derrière RelativeLayout, la balise include et la balise merge, basée sur articles que j'ai lus et j'essaye de trouver une solution appropriée à la mise en page que je souhaite réaliser.
Justin
1
"Et, pour une vraie réutilisation, la création d'une classe View personnalisée a comme atouts" D'accord. Je ne voulais tout simplement pas faire ça s'il y avait un moyen relativement simple d'utiliser les mises en page de base ... "Vous avez cueilli une 'tude - ne soyez pas surpris quand les gens réagissent à cela. Et bien que mon commentaire le plus récent soit haut sur snark, les points sont toujours valables "J'ai pris une 'tude parce que j'ai senti que vous avez fait dans vos réponses. "passer aux lignes dans un ListView pourrait être mieux." Listview ne fonctionnera pas avec l'apparence de mon application. Mon application sur le marché est AppSwipe! si vous voulez savoir ce que je fais ...
Justin
1

essayez:

<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
zahra salmaninejad
la source
0

Dans mon cas, la mise en page que j'essayais d'inclure commence par une <mergebalise. Quand je l'ai changé en mise en page, disons que <RelativeLayoutcela a fonctionné. Ci-dessous l'illustration.

TRAVAIL

<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">

CA NE FONCTIONNE PAS

<merge xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Rohit Mandiwal
la source
cela créera une autre couche imbriquée qui n'est pas la solution optimale
Silvia H
0

J'ai eu le même problème et même définir layout_widthetlayout_height cela n'a pas fonctionné. Le problème était que la mise en page que j'incluais avait des balises et après les avoir supprimées, tout fonctionnait comme un charme. Je suppose que la fusion n'est pas une balise de mise en page et à cause de cela, elle ne peut pas recevoir de paramètres de positionnement et de taille. Puisque tout ce que vous définissez dans est transféré vers la mise en page parent interne, les paramètres ont simplement été jetés.

TL: DR: Supprimez simplement les balises, déplacez les définitions xmlns vers un véritable visualiseur de mise en page et vous devriez être bon.

Avant:

<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <...ui.component.BorderCardView
        android:layout_width="112dp"
        android:layout_height="32dp"
        app:cardCornerRadius="4dp"
        app:cardUseCompatPadding="true">

        <ImageView
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:src="@drawable/ic_logout"
            android:tint="@color/divider" />

    </...ui.component.BorderCardView>
</merge>

Travail:

<...ui.component.BorderCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="112dp"
    android:layout_height="32dp"
    app:cardCornerRadius="4dp"
    app:cardUseCompatPadding="true">

    <ImageView
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:src="@drawable/ic_logout"
        android:tint="@color/divider" />

</...ui.component.BorderCardView>
silvio.pereira
la source