Come far funzionare RelativeLayout con merge and include?


114

Ho provato per alcuni giorni a rendere i miei layout più efficienti convertendoli dall'utilizzo di diversi livelli di annidato LinearLayoutsa uno RelativeLayoute ho riscontrato alcuni problemi per i quali non sono riuscito a trovare una soluzione ...

Ho cercato nel gruppo principianti Android e in questo sito e non sono riuscito a trovare nulla che mi aiutasse a risolvere il problema.

Ho letto su uno dei blog che puoi combinare layout con merge e include tag. Quindi quello che ho è un file di layout principale con un RelativeLayoutelemento radice. Al suo interno ho 5 tag di inclusione che fanno riferimento a 5 diversi file di layout xml che hanno ciascuno un elemento di unione per la radice (tutti i miei file di unione sono gli stessi tranne che per gli ID in essi contenuti).

Sto riscontrando due problemi, che spiegherò dopo aver pubblicato una versione semplificata del mio codice di layout:

File di layout principale di esempio:

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

File di unione incluso di esempio:

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

Sto riscontrando due problemi:

1) Gli android:layout_*attributi sembrano essere ignorati quando vengono utilizzati nel tag include e tutti i layout uniti vengono visualizzati uno sopra l'altro. Secondo questo post ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) "qualsiasi android:layout_*attributo può essere utilizzato con il <include />tag"

2) Poiché non riuscivo a farlo funzionare, ho deciso di provare ad aggiungere un android:layout_belowattributo al primo TextViewelemento in ogni file di layout di unione, il che significa che ogni file di unione farebbe riferimento a un id da un altro file di layout di unione ... Per la maggior parte questo effettivamente ha funzionato e il mio layout sembra a posto. Tuttavia, ricevo un errore su uno degli android:layout_belowattributi che dice che non riesce a trovare l'id che ho specificato ... Ho controllato due volte e tre volte gli ID per assicurarmi che fossero corretti. La parte più strana è che ho usato la AutoFillfunzione per mettere l'id nell'attributo in primo luogo.

Se qualcuno ha suggerimenti o soluzioni alternative, sarò più che felice di provarli. Inoltre, se qualcuno può pensare a un modo per me di avere solo un file di layout xml di unione invece di 5, sarebbe molto apprezzato. Non sono riuscito a trovare un modo per farlo perché ho bisogno di avere accesso a ogni elemento nei file di layout di unione in fase di esecuzione ...

Risposte:


214

Si è verificato un problema con il tag include. Controlla: https://issuetracker.google.com/issues/36908001

Per risolverlo, assicurati di sovrascrivere ENTRAMBI layout_widthe layout_heightquando includi, altrimenti tutto verrà ignorato.


15
Questa è una soluzione migliore di quella accettata, poiché evita di creare un oggetto di layout altrimenti superfluo. Inoltre, fa schifo come secondo gli sviluppatori Android questo sia ok.
mikołak

4
Questa è una soluzione molto più semplice, meno codificata e più ottimizzata rispetto al pacchetto <include /> in un altro layout. Pensa cosa faresti se lavorassi con le liste.
teoREtik

2
Funziona molto meglio della risposta accettata. Grazie molto! E ... andiamo google, risolvi già questo problema, questo è BS! :)
Felipe Caldas

2
@JeffAxelrod, il codice sorgente LayoutInflater mostra che i tag id, visibilità e layout_ * non vengono applicati quando l'elemento radice è un tag merge, sfortunatamente. Dato che non puoi avere una vista come root xml, dobbiamo avere un ViewGroup extra lì ...
Rafael Nobre

13
Semplicemente non ha funzionato per me. Ho entrambi layout_widthe ho layout_heightscelto il mio <include>. Ho provato anche l'impostazione layout_widthe layout_heightsul mio <merge>ma senza successo. Cosa mi sto perdendo?
dum4ll3

33

Vedi la risposta più votata di seguito. Il mio è tristemente obsoleto


posso affrontare un problema sollevato da Justin : l'incapacità di RelativeLayout di gestire il posizionamento di un include (almeno in questo semplice caso, su un emulatore 1.6)

CommonsWare suggerisce di avvolgere gli include in un contenitore genitore univoco, ma lo fa per aiutare a indirizzare e definire le viste con lo stesso nome all'interno degli include di Justin

Ciascuno dovrebbe avere un contenitore padre univoco e dovresti chiamare findViewById () su quel contenitore (ViewGroup) invece che sull'attività.

In effetti, devi farlo anche per fare in modo che RelativeLayout si comporti come previsto:

Funziona (il piè di pagina è ben posizionato):

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

Questo non (il piè di pagina è mobile nella parte superiore dello schermo):

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

Il footer nudo include non si allineerà alla parte inferiore del genitore, senza il LinearLayout circostante .. Non chiamerei questo comportamento previsto.

Inoltre, la WebView sembra attaccarsi bene al colpo di testa da ID, ma credo che questo sia illusione, a causa di essa semplicemente che scorre sotto l'intestazione in verticale. Ho anche provato a impostare un pulsante proprio sopra l'inclusione del piè di pagina, ma è diventato tutto fluttuante e anche sbagliato

RelativeLayout ha avuto più problemi nella 1.5, ma mi piace ancora :)


2
L'ho aumentato di 1 e poi diminuito di nuovo vedendo il commento di @Macarse, questo è il modo corretto di farlo.
Jayshil Dave

Si prega di rimuovere questa risposta fuorviante :(
Daniel Smith

8

Amico, questo è vecchio, ma sembra essere in cima alle ricerche, quindi commenterò.

Penso che il trucco qui sia che il <merge>tag combinato con il <include>tag rimuove essenzialmente qualsiasi tipo di gruppo di visualizzazione "principale" a quel livello. Allora, chi stai chiedendo esattamente a "layout_below" qualcun altro? Nessuno. Non c'è vista a quel livello.

Il <merge>tag prende le visualizzazioni secondarie e le inserisce direttamente nel genitore del <include>tag. Devi quindi chiedere ai bambini nel layout che includi di ancorarsi di conseguenza.


4

Affinché il posizionamento funzioni su RelativeLayout è necessario impostare i parametri layout_ * nel file include, non nel file di layout principale. Quel modo

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>

Questo ovviamente non è quello che vogliamo noi sviluppatori, ma è l'unica soluzione che ho trovato per evitare di duplicare xml


1
Perché nessun voto positivo? Questo ha funzionato per me. Non mi piace inserire parametri in un oggetto che può essere incluso in un layout che non ne ha bisogno, ma se è un LinLay sembra che il layout_ * di RelLay venga semplicemente ignorato. Mi sto perdendo qualcosa?
QED

1

Gli attributi android: layout_ * sembrano essere ignorati quando utilizzati nel tag include e tutti i layout uniti vengono visualizzati uno sopra l'altro.

La mia ipotesi è che non si possa fare riferimento, dalle regole di layout, agli android:idattributi che sono definiti sugli <include>elementi, ma solo a quelli che sono su widget e contenitori "reali".

Inoltre, se qualcuno può pensare a un modo per me di avere solo un file di layout xml di unione invece di 5, sarebbe molto apprezzato.

Semplice: mettili tutti in un file.

Non sono riuscito a trovare un modo per farlo perché ho bisogno di avere accesso a ogni elemento nei file di layout di unione in fase di esecuzione

Che tu abbia uno <include>o 1.000 elementi, tutti i contenuti dovrebbero essere accessibili in fase di esecuzione. Un'eccezione è se si hanno android:idattributi duplicati : è necessario definire correttamente l'ambito delle findViewById()chiamate per ottenere quello giusto, proprio come quando si ottengono i widget da una riga ListView.

Se riesci a creare un progetto di esempio che utilizza 2+ file di unione in cui puoi dimostrare che i contenuti non sono accessibili in fase di esecuzione, fammelo sapere.


1
Non voglio metterli tutti in un enorme file di layout perché è più difficile da gestire a lungo termine ... Ecco perché ho chiesto la possibilità di avere un XML principale con un file di unione ... perché in questo momento Ho 5 file con un elemento merge come root che hanno lo stesso identico layout tranne che gli ID sono diversi. Lo faccio in questo modo in modo che possa accedervi in ​​fase di esecuzione. Sembra che l'ambito della mia chiamata findViewById () sia ciò che vorrei fare. Come si definisce l'ambito di tale chiamata in modo da poter includere lo stesso file di layout più volte e continuare ad accedere a tutti i componenti in fase di esecuzione?
Justin,

1
Grazie per la risposta. Lo esaminerò. Nel frattempo, (e forse sto esponendo la mia ignoranza qui) non sarebbe il wrapping di ogni tag di inclusione in un contenitore genitore sconfiggere lo scopo di utilizzare RelativeLayout? Tutto l'hype di RelativeLayout è quello di evitare layout annidati ...
Justin

3
L'unico motivo per cui l'ho chiesto era per risparmiare spazio e trovare un buon design ... Ho letto della riutilizzabilità del layout qui: developer.android.com/resources/articles/… e ho pensato che suonasse bene. Quando ho provato a implementarlo ho riscontrato alcuni problemi. Attualmente ho 5 layout che sono essenzialmente duplicati tranne che per gli ID in essi ... quindi ho pensato che la riusabilità del layout sarebbe stata un buon candidato. Forse mi manca qualcosa qui, ma sembra che RelativeLayout non sia tutto ciò che viene propagandato per essere ...
Justin

1
Wow ... grazie per essere così incredibilmente utile. In genere le tue risposte sono piuttosto utili, quindi non so se stai solo passando una brutta giornata o cosa, ma stavo semplicemente cercando di ottenere una migliore comprensione dei concetti alla base di RelativeLayout, il tag include e il tag di unione, in base a articoli che ho letto e sto cercando di trovare una soluzione adeguata al layout che voglio ottenere.
Justin

1
"E, per un vero riutilizzo, la creazione di una classe di visualizzazione personalizzata trionfa include" Concordato. Semplicemente non volevo farlo se ci fosse un modo relativamente semplice per usare i layout di base ... "Hai risposto bene, per favore non sorprenderti quando le persone reagiscono a questo. E mentre il mio commento più recente è alto su snark, i punti sono ancora validi "Ho preso un 'tude perché ho sentito che hai fatto nelle tue risposte. "il passaggio alle righe in un ListView potrebbe essere migliore." Listview non funzionerà con l'aspetto della mia app. La mia app sul mercato è AppSwipe! se vuoi sapere cosa sto facendo ...
Justin

1

provare :

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

0

Nel mio caso, il layout che stavo cercando di includere inizia con <mergetag. Quando l'ho modificato in un layout, dico <RelativeLayoutche ha funzionato. Di seguito è riportata l'illustrazione.

LAVORANDO

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

NON FUNZIONA

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

questo creerà un altro livello annidato che non è la soluzione ottimale
Silvia H

0

Ho avuto lo stesso problema e persino la definizione layout_widthe layout_heightnon ha funzionato. Il problema era che il layout che stavo includendo aveva dei tag e dopo averli rimossi, tutto ha funzionato a meraviglia. Suppongo che l'unione non sia un tag di layout e per questo motivo non può ricevere parametri di posizionamento e dimensione. Poiché tutto ciò che definisci viene trasferito al layout principale interno, le impostazioni sono state buttate via.

TL: DR: Basta rimuovere i tag, spostare le definizioni xmlns in un vero visualizzatore di layout e dovresti essere bravo.

Prima:

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

Lavorando:

<...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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.