Como fazer o RelativeLayout funcionar com mesclar e incluir?

114

Estou tentando há alguns dias tornar meus layouts mais eficientes, convertendo o uso de vários níveis de aninhados LinearLayoutsem um RelativeLayoute encontrei alguns problemas para os quais não consegui encontrar uma solução alternativa ...

Eu pesquisei o grupo de iniciantes em Android e este site e não consegui encontrar nada que pudesse me ajudar a resolver o problema.

Eu li em um dos blogs que você pode combinar layouts com mesclar e incluir tags. Portanto, o que tenho é um arquivo de layout principal com um RelativeLayoutelemento raiz. Dentro disso, tenho 5 tags de inclusão que fazem referência a 5 arquivos de layout xml diferentes, cada um com um elemento de mesclagem para a raiz (todos os meus arquivos de mesclagem são iguais, exceto pelos ids neles).

Estou tendo dois problemas, que explicarei depois de postar uma versão simplificada do meu código de layout:

Arquivo de layout principal de amostra:

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

Arquivo de mesclagem incluído:

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

Estou tendo dois problemas:

1) Os android:layout_*atributos parecem ser ignorados quando usados ​​na tag de inclusão e todos os layouts mesclados são exibidos uns sobre os outros. De acordo com esta postagem ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) "qualquer android:layout_*atributo pode ser usado com a <include />tag"

2) Como não consegui fazer isso funcionar, decidi tentar adicionar um android:layout_belowatributo ao primeiro TextViewitem em cada arquivo de layout de mesclagem, o que significa que cada arquivo de mesclagem faria referência a um id de outro arquivo de layout de mesclagem ... Na maioria das vezes isso realmente funcionou e meu layout parece bom. No entanto, recebo um erro em um dos android:layout_belowatributos dizendo que ele não consegue encontrar o id que eu especifiquei ... Verifiquei duas ou três vezes os ids para ter certeza de que estavam corretos. A parte mais estranha é que usei o AutoFillrecurso para colocar o id no atributo em primeiro lugar.

Se alguém tiver alguma sugestão ou solução alternativa, terei todo o gosto em experimentá-los. Além disso, se alguém puder pensar em uma maneira de eu ter apenas um arquivo de layout xml mesclado em vez de 5, isso seria muito apreciado. Não consegui encontrar uma maneira de fazer isso porque preciso ter acesso a cada item nos arquivos de layout de mesclagem em tempo de execução ...

Justin
fonte

Respostas:

214

Há um problema com a tag de inclusão. Verifique: https://issuetracker.google.com/issues/36908001

Para corrigi-lo, certifique-se de substituir AMBOS layout_widthe layout_heightao incluir, caso contrário, tudo será ignorado.

Macarse
fonte
15
Esta é uma solução melhor do que a aceita, pois evita a criação de um objeto de layout supérfluo. Além disso, é uma pena como, de acordo com os desenvolvedores do Android, isso está OK.
mikołak
4
Isso é realmente mais fácil, menos codificado e uma solução mais otimizada do que empacotar <include /> em outro layout. Pense no que você faria se trabalhasse com listas.
teoREtik
2
Isso funciona muito melhor do que uma resposta aceita. Muito Obrigado! E ... vamos google, corrija esse problema já, isso é BS! :)
Felipe Caldas
2
@JeffAxelrod, o código-fonte do LayoutInflater mostra que id, visibilidade e substituição de tags layout_ * não são aplicadas quando o elemento raiz é uma tag de mesclagem, infelizmente. Como você não pode ter um View como raiz xml, devemos ter um ViewGroup extra lá ...
Rafael Nobre
13
Simplesmente não funcionou para mim. Eu tenho ambos layout_widthe layout_heightestabeleci meu <include>. Eu tentei também definir layout_widthe layout_heightno meu <merge>mas sem sucesso. O que estou perdendo ?
dum4ll3
33

Veja a resposta mais votada abaixo. O meu está terrivelmente desatualizado


Posso resolver um problema que Justin levantou: incapacidade do RelativeLayout de gerenciar o posicionamento de um include (pelo menos neste caso simples, em um emulador 1.6)

CommonsWare sugere envolver os includes em um único container pai, mas faz isso para ajudar a endereçar e definir o escopo de Views com nomes idênticos dentro dos includes de Justin

Cada um teria que ter um contêiner pai exclusivo e você chamaria findViewById () nesse contêiner (ViewGroup) em vez de na Activity.

Na verdade, você também deve fazer isso para que o RelativeLayout se comporte conforme o esperado:

Isso funciona (o rodapé está bem posicionado):

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

Isso não acontece (o rodapé está flutuando no topo da tela):

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

O include de rodapé descalço não se alinhará à parte inferior do pai, sem o LinearLayout circundante. Eu não chamaria esse comportamento esperado.

Além disso, o WebView parece se anexar bem ao cabeçalho por ID, mas acredito que isso seja uma ilusão, porque simplesmente flui abaixo do cabeçalho verticalmente. Eu também tentei definir um botão logo acima do footer include, mas ficou todo flutuante e errado também

RelativeLayout teve mais problemas no 1.5, mas eu ainda gosto :)

alienjazzcat
fonte
2
Aumentei em 1 e diminuí de volta vendo o comentário do @Macarse, essa é a maneira correta de fazer.
Jayshil Dave
Remova esta resposta enganosa :(
Daniel Smith
8

Cara, isso é antigo, mas parece que aparece no topo das pesquisas, então vou comentar.

Acho que o truque aqui é que a <merge>tag combinada com a <include>tag remove essencialmente qualquer tipo de grupo de visualização "pai" nesse nível. Então, quem exatamente você está pedindo para "layout_below" outra pessoa? Ninguém. Não há visão nesse nível.

A <merge>tag pega as visualizações filhas e as coloca diretamente no pai da <include>tag. Você deve, portanto, pedir aos filhos no layout que está incluindo que se ancorem de acordo.

Plantage
fonte
4

Para que o posicionamento funcione no RelativeLayout, você precisa definir os parâmetros layout_ * no arquivo de inclusão, não no arquivo de layout principal. Dessa maneira

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>

Obviamente, isso não é o que nós, desenvolvedores, queremos, mas é a única solução que encontrei para evitar a duplicação de xml

Maragues
fonte
1
Por que não houve votos positivos? Isso funcionou para mim. Não gosto de inserir parâmetros em um objeto que pode ser incluído em um layout que não precisa deles, mas se for um LinLay, parece que o layout_ * do RelLay está sendo ignorado. Estou esquecendo de algo?
QED de
1

Os atributos android: layout_ * parecem ser ignorados quando usados ​​na tag de inclusão e todos os layouts mesclados são exibidos uns sobre os outros.

Meu palpite é que você não pode fazer referência, a partir de regras de layout, android:idatributos que são definidos em <include>elementos, apenas aqueles que estão em widgets e contêineres "reais".

Além disso, se alguém puder pensar em uma maneira de eu ter apenas um arquivo de layout xml mesclado em vez de 5, isso seria muito apreciado.

Simples: coloque todos em um arquivo.

Não consegui encontrar uma maneira de fazer isso porque preciso ter acesso a cada item nos arquivos de layout de mesclagem em tempo de execução

Quer você tenha um <include>elemento ou 1.000, todo o conteúdo deve estar acessível em tempo de execução. Uma exceção é se você tiver android:idatributos duplicados - você precisaria definir o escopo adequado de suas findViewById()chamadas para obter o correto, assim como faz ao obter widgets de uma linha ListView.

Se você pode criar um projeto de amostra que usa 2+ arquivos de mesclagem onde você pode demonstrar que o conteúdo não está acessível em tempo de execução, me avise.

CommonsWare
fonte
1
Não quero colocá-los todos em um arquivo de layout massivo porque é mais difícil de gerenciar no longo prazo ... É por isso que pedi a possibilidade de ter um xml principal com um arquivo de mesclagem ... porque agora Eu tenho 5 arquivos com um elemento de mesclagem como raiz que têm exatamente o mesmo layout, exceto que os ids são diferentes. Eu faço isso para poder acessá-los em tempo de execução. Parece que o escopo da minha chamada findViewById () é o que eu gostaria de fazer. Como você define o escopo dessa chamada para que possa incluir o mesmo arquivo de layout várias vezes e ainda ser capaz de acessar todos os componentes em tempo de execução?
Justin
1
Obrigado pela resposta. Vou dar uma olhada nisso. Nesse ínterim, (e talvez eu esteja expondo minha ignorância aqui), envolver cada tag include em um contêiner pai anularia o propósito de usar RelativeLayout? Todo o hype do RelativeLayout é para evitar layouts aninhados ...
Justin
3
A única razão pela qual perguntei sobre isso foi para economizar espaço e chegar a um bom design ... Eu li sobre reutilização de layout aqui: developer.android.com/resources/articles/… e achei que parecia bom. Quando tentei implementá-lo, tive alguns problemas. Atualmente tenho 5 layouts que são essencialmente duplicados, exceto pelos ids neles ... então pensei que a reutilização de layout seria um bom candidato. Talvez eu esteja faltando alguma coisa aqui, mas parece que RelativeLayout não é tudo o que é anunciado ...
Justin
1
Uau ... obrigado por ser tão incrivelmente útil. Geralmente suas respostas são muito úteis, então não sei se você está apenas tendo um dia ruim ou o quê, mas eu estava simplesmente tentando obter uma melhor compreensão dos conceitos por trás do RelativeLayout, a tag de inclusão e a tag de mesclagem, com base em artigos que li e tentando encontrar uma solução adequada para o layout que desejo alcançar.
Justin
1
"E, para uma verdadeira reutilização, a criação de uma classe de visualização personalizada trunfos incluem" Concordo. Eu só não queria fazer isso se houvesse uma maneira relativamente fácil de usar layouts básicos ... "Você pegou uma 'tude - por favor, não se surpreenda quando as pessoas reagirem a isso. E embora meu comentário mais recente seja no alto da snark, os pontos ainda são válidos. "mudar para linhas em um ListView pode ser melhor." Listview não funcionará com a aparência do meu aplicativo. Meu aplicativo no mercado é o AppSwipe! se você quiser saber o que estou fazendo ...
Justin
1

experimentar :

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

No meu caso, o layout que estava tentando incluir começa com <mergetag. Quando mudei para um layout, diga <RelativeLayoutque funcionou. Abaixo está a ilustração.

TRABALHANDO

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

NÃO ESTÁ FUNCIONANDO

<merge xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Rohit Mandiwal
fonte
isso criará outra camada aninhada que não é a solução ideal
Silvia H
0

Tive o mesmo problema e até defini layout_widthe layout_heightnão funcionou. O problema era que o layout que eu estava incluindo tinha tags e depois de removê-las, tudo funcionou perfeitamente. Suponho que merge não seja uma tag de layout e por isso não pode receber parâmetros de posicionamento e tamanho. Como tudo que você define é transferido para o layout pai interno, as configurações foram descartadas.

TL: DR: Apenas remova as tags, mova as definições xmlns para um visualizador de layout real e você deve estar bem.

Antes:

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

Trabalhando:

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