Wie kann RelativeLayout mit Merge and Include arbeiten?

114

Ich habe seit einigen Tagen versucht, meine Layouts effizienter zu gestalten, indem ich mehrere verschachtelte Ebenen LinearLayoutsin eine umwandelte , RelativeLayoutund bin auf einige Probleme gestoßen, für die ich keine Problemumgehung gefunden habe ...

Ich habe die Android-Anfängergruppe und diese Website durchsucht und konnte nichts finden, was mir bei der Lösung des Problems helfen könnte.

Ich habe in einem der Blogs gelesen, dass Sie Layouts mit Zusammenführen kombinieren und Tags einschließen können. Ich habe also eine Hauptlayoutdatei mit einem RelativeLayoutStammelement. Darin befinden sich 5 Include-Tags, die auf 5 verschiedene XML-Layoutdateien verweisen, die jeweils ein Zusammenführungselement für das Stammverzeichnis enthalten (alle meine Zusammenführungsdateien sind bis auf die darin enthaltenen IDs identisch).

Ich habe zwei Probleme, die ich nach der Veröffentlichung einer vereinfachten Version meines Layout-Codes erläutern werde:

Beispiel für eine Hauptlayoutdatei:

<?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>

Beispiel enthaltene Zusammenführungsdatei:

<?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>

Ich habe zwei Probleme:

1) Die android:layout_*Attribute scheinen ignoriert zu werden, wenn sie im Include-Tag verwendet werden, und alle zusammengeführten Layouts werden übereinander angezeigt. Laut diesem Beitrag ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) "kann jedes android:layout_*Attribut mit dem <include />Tag verwendet werden"

2) Da dies nicht funktionieren konnte, habe ich beschlossen, android:layout_belowdem ersten TextViewElement in jeder Zusammenführungslayoutdatei ein Attribut hinzuzufügen , was bedeutet, dass jede Zusammenführungsdatei auf eine ID aus einer anderen Zusammenführungslayoutdatei verweist ... Zum größten Teil hat tatsächlich funktioniert und mein Layout sieht gut aus. Bei einem der android:layout_belowAttribute wird jedoch eine Fehlermeldung angezeigt, dass die von mir angegebene ID nicht gefunden werden kann. Ich habe die IDs doppelt und dreifach überprüft, um sicherzustellen, dass sie korrekt sind. Das Seltsamste ist, dass ich die AutoFillFunktion verwendet habe, um die ID an erster Stelle in das Attribut einzufügen .

Wenn jemand Vorschläge oder Problemumgehungen hat, probiere ich diese gerne aus. Wenn mir jemand eine Möglichkeit einfallen lässt, nur eine XML-Layoutdatei zum Zusammenführen anstelle von 5 zu erstellen, wäre dies sehr dankbar. Ich konnte keinen Weg finden, dies zu tun, da ich zur Laufzeit Zugriff auf jedes Element in den Zusammenführungslayoutdateien haben muss ...

Justin
quelle

Antworten:

214

Es liegt ein Problem mit dem Include-Tag vor. Überprüfen Sie: https://issuetracker.google.com/issues/36908001

Um dies zu beheben, stellen Sie sicher, dass Sie BEIDE überschreiben layout_widthund layout_heightwenn Sie diese einschließen, wird sonst alles ignoriert.

Macarse
quelle
15
Dies ist eine bessere Lösung als die akzeptierte, da dadurch vermieden wird, dass ein ansonsten überflüssiges Layoutobjekt erstellt wird. Außerdem ist es scheiße, wie laut den Android-Entwicklern dies a-OK ist.
Mikołak
4
Dies ist eine wirklich einfachere, weniger fest codierte und optimierte Lösung als das Packen von <include /> in ein anderes Layout. Überlegen Sie, was Sie tun würden, wenn Sie mit Listen arbeiten würden.
TeoREtik
2
Dies funktioniert viel besser als akzeptierte Antwort. Danke vielmals! Und ... komm schon Google, behebe dieses Problem bereits, das ist BS! :)
Felipe Caldas
2
@ JeffAxelrod, der LayoutInflater-Quellcode zeigt, dass ID-, Sichtbarkeits- und Layout_ * -Tags-Überschreibungen leider nicht angewendet werden, wenn das Root-Element ein Merge-Tag ist. Da Sie keine Ansicht als XML-Root haben können, müssen wir dort eine zusätzliche ViewGroup haben ...
Rafael Nobre
13
Bei mir hat es einfach nicht funktioniert. Ich habe beides layout_widthund habe layout_heightmich auf mein festgelegt <include>. Ich habe auch versucht, auf layout_widthund layout_heightauf meine zu setzen, <merge>aber ohne Erfolg. Was vermisse ich ?
dum4ll3
33

Siehe die höher bewertete Antwort unten. Meins ist absolut veraltet


Ich kann ein Problem ansprechen, das Justin angesprochen hat: Unfähigkeit von RelativeLayout, die Positionierung eines Includes zu verwalten (zumindest in diesem einfachen Fall auf einem 1.6-Emulator)

CommonsWare schlägt vor, die Includes in einen eindeutigen übergeordneten Container zu packen , dies jedoch, um das Adressieren und Scoping von Ansichten mit identischem Namen in Justins Includes zu unterstützen

Jeder müsste einen eindeutigen übergeordneten Container haben, und Sie würden findViewById () für diesen Container (ViewGroup) und nicht für die Aktivität aufrufen.

Tatsächlich müssen Sie dies auch tun , damit sich RelativeLayout wie erwartet verhält:

Dies funktioniert ( Fußzeile ist gut positioniert):

<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>

Dies ist nicht der Fall (die Fußzeile schwebt oben auf dem Bildschirm):

<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>

Das nackte Fußzeilen- Include wird ohne das umgebende LinearLayout nicht am unteren Rand des übergeordneten Elements ausgerichtet. Ich würde dieses erwartete Verhalten nicht nennen.

Darüber hinaus scheint sich das WebView per ID gut an den Header anzuhängen , aber ich halte dies für eine Illusion, da es einfach vertikal unter dem Header fließt. Ich habe auch versucht, eine Schaltfläche direkt über dem Fußzeilen-Include zu setzen, aber es wurde auch alles schwebend und falsch

RelativeLayout hatte mehr Probleme in 1.5, aber ich mag es immer noch :)

alienjazzcat
quelle
2
Ich habe es um 1 erhöht und dann wieder verringert, als ich den Kommentar von @Macarse gesehen habe. Das ist der richtige Weg, dies zu tun.
Jayshil Dave
Bitte entfernen Sie diese irreführende Antwort :(
Daniel Smith
8

Mann, das ist alt, aber es scheint ganz oben auf der Suche zu stehen, also werde ich einen Kommentar abgeben.

Ich denke, der Trick hier ist, dass das <merge>Tag in Kombination mit dem <include>Tag im Wesentlichen jede Art von "übergeordneter" Ansichtsgruppe auf dieser Ebene entfernt. Also, wen genau fragst du dann, um jemand anderen "layout_below" zu machen? Niemand. Auf dieser Ebene gibt es keine Sicht.

Das <merge>Tag nimmt die untergeordneten Ansichten und fügt sie direkt in das übergeordnete Element des <include>Tags ein. Sie müssen daher die Kinder in dem Layout, das Sie einschließen, bitten, sich entsprechend zu verankern.

Plantage
quelle
4

Damit die Positionierung in RelativeLayout funktioniert, müssen Sie die Parameter layout_ * in der Include-Datei und nicht in der Hauptlayoutdatei festlegen. Dieser Weg

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>

Dies ist offensichtlich nicht das, was wir Entwickler wollen, aber es ist die einzige Lösung, die ich gefunden habe, um das Duplizieren von XML zu vermeiden

Maragues
quelle
1
Warum keine Upvotes? Das hat bei mir funktioniert. Ich bin nicht verliebt in das Einfügen von Parametern in ein Objekt, das möglicherweise in einem Layout enthalten ist, das sie nicht benötigt, aber wenn es ein LinLay ist, scheint es, dass RelLays layout_ * einfach ignoriert wird. Vermisse ich etwas
QED
1

Die Attribute android: layout_ * scheinen ignoriert zu werden, wenn sie im Include-Tag verwendet werden, und alle zusammengeführten Layouts werden übereinander angezeigt.

Ich vermute, dass Sie anhand von Layoutregeln nicht auf android:idAttribute verweisen können, die für <include>Elemente definiert sind, sondern nur auf Attribute, die sich auf "echten" Widgets und Containern befinden.

Wenn mir jemand eine Möglichkeit einfallen lässt, nur eine XML-Layoutdatei zum Zusammenführen anstelle von 5 zu erstellen, wäre dies sehr dankbar.

Einfach: Legen Sie sie alle in eine Datei.

Ich konnte keinen Weg finden, dies zu tun, da ich zur Laufzeit Zugriff auf jedes Element in den Zusammenführungslayoutdateien haben muss

Unabhängig davon, ob Sie ein <include>Element oder 1.000 haben, sollte der gesamte Inhalt zur Laufzeit verfügbar sein. Eine Ausnahme ist, wenn Sie android:idAttribute dupliziert haben. Sie müssten Ihre findViewById()Aufrufe ordnungsgemäß ausführen, um die richtigen zu erhalten, genau wie beim Abrufen von Widgets aus einer ListView-Zeile.

Wenn Sie ein Beispielprojekt erstellen können, das 2+ Zusammenführungsdateien verwendet, in denen Sie nachweisen können, dass der Inhalt zur Laufzeit nicht verfügbar ist, lassen Sie es mich wissen.

CommonsWare
quelle
1
Ich möchte nicht alle in eine massive Layoutdatei einfügen, da dies auf lange Sicht schwieriger zu verwalten ist. Deshalb habe ich nach der Möglichkeit gefragt, eine Haupt-XML mit einer Zusammenführungsdatei zu haben Ich habe 5 Dateien mit einem Zusammenführungselement als Stamm, die genau das gleiche Layout haben, außer dass die IDs unterschiedlich sind. Ich mache es so, damit ich zur Laufzeit darauf zugreifen kann. Es scheint, als würde ich meinen findViewById () -Aufruf festlegen. Wie können Sie diesen Aufruf so gestalten, dass Sie dieselbe Layoutdatei mehrmals einschließen und zur Laufzeit trotzdem auf alle Komponenten zugreifen können?
Justin
1
Danke für die Antwort. Ich werde das untersuchen. In der Zwischenzeit (und vielleicht enthülle ich hier meine Unwissenheit) würde nicht das Einschließen jedes Include-Tags in einen übergeordneten Container den Zweck der Verwendung von RelativeLayout zunichte machen? Der ganze Hype von RelativeLayout besteht darin, verschachtelte Layouts zu vermeiden ...
Justin
3
Der einzige Grund, warum ich danach gefragt habe, war, Platz zu sparen und ein gutes Design zu entwickeln ... Ich habe hier über die Wiederverwendbarkeit von Layouts gelesen: developer.android.com/resources/articles/… und fand, dass es gut klang. Als ich versuchte, es zu implementieren, stieß ich auf einige Probleme. Derzeit habe ich 5 Layouts, die bis auf die darin enthaltenen IDs im Wesentlichen dupliziert sind. Daher dachte ich, dass die Wiederverwendbarkeit von Layouts ein guter Kandidat wäre. Vielleicht fehlt mir hier etwas, aber es scheint, dass RelativeLayout nicht alles ist, was angepriesen wird ...
Justin
1
Wow ... danke, dass du so unglaublich hilfreich bist. Im Allgemeinen sind Ihre Antworten ziemlich hilfreich, sodass ich nicht weiß, ob Sie gerade einen schlechten Tag haben oder was, aber ich habe lediglich versucht, die Konzepte hinter RelativeLayout, dem Include-Tag und dem Merge-Tag basierend auf besser zu verstehen Artikel, die ich gelesen habe und die versuchen, eine geeignete Lösung für das Layout zu finden, das ich erreichen möchte.
Justin
1
"Und für eine echte Wiederverwendung umfasst das Erstellen von benutzerdefinierten Trümpfen für View-Klassen Folgendes:" Einverstanden. Ich wollte das einfach nicht tun, wenn es eine relativ einfache Möglichkeit gab, grundlegende Layouts zu verwenden ... "Sie haben einen Fehler gemacht - bitte wundern Sie sich nicht, wenn die Leute darauf reagieren. Und während mein jüngster Kommentar lautet Hoch auf Snark, die Punkte sind immer noch gültig. "Ich habe einen Fehler gemacht, weil ich das Gefühl hatte, dass Sie dies in Ihren Antworten getan haben. "Das Wechseln zu Zeilen in einer ListView ist möglicherweise besser." Listview funktioniert nicht mit dem Aussehen meiner App. Meine App auf dem Markt ist AppSwipe! wenn Sie wissen wollen, was ich tue ...
Justin
1

Versuchen :

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

In meinem Fall beginnt das Layout, das ich einschließen wollte, mit dem <mergeTag. Wenn ich es in ein Layout geändert habe, sagen wir, <RelativeLayoutes hat funktioniert. Unten ist die Abbildung.

ARBEITEN

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

FUNKTIONIERT NICHT

<merge xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Rohit Mandiwal
quelle
Dadurch wird eine weitere verschachtelte Ebene erstellt, die nicht die optimale Lösung darstellt
Silvia H,
0

Ich hatte das gleiche Problem und sogar das Definieren layout_widthund layout_heightes hat nicht funktioniert. Das Problem war, dass das Layout, das ich einfügte, Tags hatte und nach dem Entfernen alles wie ein Zauber funktionierte. Ich nehme an, dass das Zusammenführen kein Layout-Tag ist und daher keine Positionierungs- und Größenparameter empfangen kann. Da alles, was Sie definieren, in das innere übergeordnete Layout übertragen wird, wurden die Einstellungen einfach weggeworfen.

TL: DR: Entfernen Sie einfach die Tags, verschieben Sie die XMLNS-Definitionen in einen echten Layout-Ansichtshalter, und Sie sollten gut sein.

Vor:

<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>

Arbeiten:

<...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
quelle