¿Cómo hacer que RelativeLayout funcione con fusionar e incluir?

114

He estado intentando durante unos días hacer que mis diseños sean más eficientes al convertir el uso de varios niveles de anidados LinearLayoutsen uno RelativeLayouty me he encontrado con algunos problemas para los que no he podido encontrar una solución alternativa ...

He buscado en el grupo de principiantes de Android y en este sitio y no he podido encontrar nada que me ayude a resolver el problema.

Leí en uno de los blogs que puedes combinar diseños con combinar e incluir etiquetas. Entonces, lo que tengo es un archivo de diseño principal con un RelativeLayoutelemento raíz. Dentro de eso, tengo 5 etiquetas de inclusión que hacen referencia a 5 archivos de diseño xml diferentes, cada uno de los cuales tiene un elemento de combinación para la raíz (todos mis archivos de combinación son iguales, excepto por los ID que contienen).

Me encuentro con dos problemas, que explicaré después de publicar una versión simplificada de mi código de diseño:

Ejemplo de archivo de diseño 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>

Ejemplo de archivo de combinación incluido:

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

Me encuentro con dos problemas:

1) Los android:layout_*atributos parecen ignorarse cuando se usan en la etiqueta de inclusión y todos los diseños combinados se muestran uno encima del otro. Según esta publicación ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) " android:layout_*se puede usar cualquier atributo con la <include />etiqueta"

2) Como no pude hacer que esto funcionara, decidí intentar agregar un android:layout_belowatributo al primer TextViewelemento en cada archivo de diseño de combinación, lo que significa que cada archivo de combinación haría referencia a una identificación de otro archivo de diseño de combinación ... En su mayor parte, esto realmente funcionó y mi diseño se ve bien. Sin embargo, aparece un error en uno de los android:layout_belowatributos que dice que no puede encontrar la identificación que especifiqué ... He verificado dos y tres veces las identificaciones para asegurarme de que sean correctas. La parte más extraña es que utilicé la AutoFillfunción para poner la identificación en el atributo en primer lugar.

Si alguien tiene alguna sugerencia o solución alternativa, estaré más que feliz de probarlas. Además, si alguien puede pensar en una manera de tener un solo archivo de diseño xml combinado en lugar de 5, sería muy apreciado. No pude encontrar una manera de hacerlo porque necesito tener acceso a cada elemento en los archivos de diseño de combinación en tiempo de ejecución ...

Justin
fuente

Respuestas:

214

Hay un problema con la etiqueta de inclusión. Verificar: https://issuetracker.google.com/issues/36908001

Para solucionarlo, asegúrese de sobrescribir AMBOS layout_widthy layout_heightal incluirlos, de lo contrario, se ignorará todo.

Macarse
fuente
15
Esta es una solución mejor que la aceptada, ya que evita la creación de un objeto de diseño superfluo. Además, es una mierda cómo, según los desarrolladores de Android, esto está bien.
mikołak
4
Esta es una solución realmente más fácil, menos codificada y más optimizada que empaquetar <include /> en otro diseño. Piense en lo que haría si trabajara con listas.
teoREtik
2
Esto funciona mucho mejor que la respuesta aceptada. ¡Muchas gracias! Y ... vamos Google, soluciona este problema ya, ¡esto es BS! :)
Felipe Caldas
2
@JeffAxelrod, el código fuente de LayoutInflater muestra que las etiquetas id, visibilidad y layout_ * no se aplican cuando el elemento raíz es una etiqueta merge, desafortunadamente. Como no puede tener una vista como raíz xml, debemos tener un grupo de vistas adicional allí ...
Rafael Nobre
13
Simplemente no funcionó para mí. Tengo ambos layout_widthy me he layout_heightfijado en mi <include>. Traté también de montar layout_widthy layout_heighten mi <merge>pero sin éxito. Qué me estoy perdiendo ?
dum4ll3
33

Vea la respuesta más votada a continuación. El mío está lamentablemente desactualizado


Puedo abordar un problema que planteó Justin : incapacidad de RelativeLayout para administrar el posicionamiento de una inclusión (al menos en este caso simple, en un emulador 1.6)

CommonsWare sugiere envolver la incluye en un único contenedor principal, pero lo hace con el fin de ayudar a hacer frente a la determinación del alcance y con nombres idénticos puntos de vista dentro de Justin incluye

Cada uno tendría que tener un contenedor principal único, y llamaría a findViewById () en ese contenedor (ViewGroup) en lugar de en la Actividad.

De hecho, también debe hacerlo para que RelativeLayout se comporte como se espera:

Esto funciona (el pie de página está bien 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>

Esto no (el pie de página flota en la parte superior de la pantalla):

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

La inclusión de pie de página desnudo no se alineará con la parte inferior del padre, sin el LinearLayout circundante. No llamaría a este comportamiento esperado.

Además, WebView parece adjuntarse muy bien al encabezado por ID, pero creo que esto es una ilusión, debido a que simplemente fluye debajo del encabezado verticalmente. También intenté establecer un botón justo encima del pie de página, pero también se volvió flotante e incorrecto.

RelativeLayout tuvo más problemas en 1.5, pero todavía me gusta :)

alienjazzcat
fuente
2
Lo aumenté en 1 y luego disminuí al ver el comentario de @Macarse, esa es la forma correcta de hacerlo.
Jayshil Dave
Elimine esta respuesta engañosa :(
Daniel Smith
8

Hombre, esto es antiguo, pero parece aparecer en la parte superior de las búsquedas, así que voy a comentar.

Creo que el truco aquí es que la <merge>etiqueta combinada con la <include>etiqueta esencialmente elimina cualquier tipo de grupo de vista "principal" en ese nivel. Entonces, ¿a quién le estás preguntando exactamente para "layout_below" a otra persona? Ninguno. No hay vista a ese nivel.

La <merge>etiqueta toma las vistas secundarias y las coloca directamente en el padre de la <include>etiqueta. Por lo tanto, debe pedir a los niños del diseño que incluye que se anclen en consecuencia.

Plantage
fuente
4

Para que el posicionamiento funcione en RelativeLayout, debe establecer los parámetros layout_ * en el archivo de inclusión, no en el archivo de diseño principal. De esa manera

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, esto no es lo que queremos los desarrolladores, pero es la única solución que he encontrado para evitar duplicar xml

Maragues
fuente
1
¿Por qué no hay votos a favor? Esto funcionó para mí. No me encanta pegar parámetros en un objeto que puede incluirse en un diseño que no los necesita, pero si es un LinLay, parece que el diseño de RelLay_ * simplemente se está ignorando. ¿Me estoy perdiendo de algo?
QED
1

Los atributos android: layout_ * parecen ignorarse cuando se usan en la etiqueta de inclusión y todos los diseños combinados se muestran uno encima del otro.

Supongo que no se puede hacer referencia, desde las reglas de diseño, a android:idatributos que están definidos en <include>elementos, solo a los que están en widgets y contenedores "reales".

Además, si alguien puede pensar en una manera de tener un solo archivo de diseño xml combinado en lugar de 5, sería muy apreciado.

Simple: ponlos todos en un archivo.

No pude encontrar una manera de hacerlo porque necesito tener acceso a cada elemento en los archivos de diseño de combinación en tiempo de ejecución

Ya sea que tenga un <include>elemento o 1,000, todos los contenidos deben ser accesibles en tiempo de ejecución. Una excepción es si tiene android:idatributos duplicados : necesitaría ajustar correctamente el alcance de sus findViewById()llamadas para obtener la correcta, tal como debe hacerlo cuando obtiene widgets de una fila de ListView.

Si puede crear un proyecto de muestra que use más de 2 archivos de combinación en los que pueda demostrar que no se puede acceder al contenido en tiempo de ejecución, hágamelo saber.

CommonsWare
fuente
1
No quiero ponerlos todos en un archivo de diseño masivo porque es más difícil de administrar a largo plazo ... Es por eso que pedí la posibilidad de tener un xml principal con un archivo de combinación ... porque ahora mismo Tengo 5 archivos con un elemento de combinación como raíz que tienen exactamente el mismo diseño, excepto que los identificadores son diferentes. Lo hago de esa manera para poder acceder a ellos en tiempo de ejecución. Parece que el alcance de mi llamada findViewById () es lo que me gustaría hacer. ¿Cómo amplía esa llamada para que pueda incluir el mismo archivo de diseño varias veces y aún poder acceder a todos los componentes en tiempo de ejecución?
Justin
1
Gracias por la respuesta. Lo investigaré. Mientras tanto, (y tal vez estoy exponiendo mi ignorancia aquí), ¿envolver cada etiqueta de inclusión en un contenedor principal anularía el propósito de usar RelativeLayout? Todo el bombo de RelativeLayout es evitar diseños anidados ...
Justin
3
La única razón por la que pregunté al respecto fue para ahorrar espacio y crear un buen diseño ... Leí sobre la reutilización del diseño aquí: developer.android.com/resources/articles/… y pensé que sonaba bien. Cuando intenté implementarlo, encontré algunos problemas. Actualmente tengo 5 diseños que están esencialmente duplicados excepto por las identificaciones en ellos ... así que pensé que la reutilización del diseño sería un buen candidato. Tal vez me falte algo aquí, pero parece que RelativeLayout no es todo lo que se promociona ...
Justin
1
Wow ... gracias por ser tan increíblemente útil. En general, sus respuestas son bastante útiles, por lo que no sé si solo está teniendo un mal día o qué, pero simplemente estaba tratando de comprender mejor los conceptos detrás de RelativeLayout, la etiqueta include y la etiqueta merge, según artículos que he leído y trato de encontrar una solución adecuada a la maquetación que quiero conseguir.
Justin
1
"Y, para una verdadera reutilización, la creación de una clase de vista personalizada incluye" De acuerdo. Simplemente no quería hacer eso si hubiera una manera relativamente fácil de usar diseños básicos ... "Copiaste un 'tude - por favor no te sorprendas cuando la gente reaccione a eso. Y aunque mi comentario más reciente es alto en snark, los puntos siguen siendo válidos "Cobré una 'tude porque sentí que lo hiciste en tus respuestas. "cambiar a filas en un ListView podría ser mejor". Listview no funcionará con el aspecto de mi aplicación. ¡Mi aplicación en el mercado es AppSwipe! si quieres saber lo que estoy haciendo ...
Justin
1

tratar :

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

En mi caso, el diseño que intentaba incluir comienza con <mergeetiqueta. Cuando lo cambié a un diseño, digo <RelativeLayoutque funcionó. A continuación se muestra la ilustración.

TRABAJANDO

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

NO FUNCIONA

<merge xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Rohit Mandiwal
fuente
esto creará otra capa anidada que no es la solución óptima
Silvia H
0

Tuve el mismo problema e incluso definiendo layout_widthy layout_heightno funcionó. El problema era que el diseño que estaba incluyendo tenía etiquetas y, después de quitarlas, todo funcionó a la perfección. Supongo que la combinación no es una etiqueta de diseño y, por eso, no puede recibir parámetros de tamaño y posicionamiento. Dado que todo lo que define en se transfiere al diseño principal interno, la configuración simplemente se desechó.

TL: DR: Simplemente elimine las etiquetas, mueva las definiciones xmlns a un marcador de visualización de diseño real y debería estar bien.

Antes de:

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

Trabajando:

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