¿La etiqueta "incluye" de Android XML Layout realmente funciona?

84

No puedo anular los atributos cuando uso <include> en mis archivos de diseño de Android. Cuando busqué errores, encontré el problema rechazado 2863 :

"La etiqueta de inclusión está rota (la modificación de los parámetros de diseño nunca funciona)"

Dado que Romain indica que esto funciona en las suites de prueba y sus ejemplos, debo estar haciendo algo mal.

Mi proyecto está organizado así:

res/layout
  buttons.xml

res/layout-land
  receipt.xml

res/layout-port
  receipt.xml

El button.xml contiene algo como esto:

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

  <Button .../>

  <Button .../>
</LinearLayout>

Y los archivos Receiver.xml vertical y horizontal se parecen a lo siguiente:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

  ...

  <!-- Overridden attributes never work. Nor do attributes like
       the red background, which is specified here. -->
  <include
      android:id="@+id/buttons_override"
      android:background="#ff0000"
      android:layout_width="fill_parent"
      layout="@layout/buttons"/>

</LinearLayout>

¿Qué me estoy perdiendo?

Eric Burke
fuente
Las herramientas para desarrolladores de Android hacen referencia a esta pregunta cuando intentas utilizar la función de inclusión de una forma que no es compatible.
ThomasW

Respuestas:

132

Acabo de encontrar el problema. Primero, solo puede anular los atributos layout_ *, por lo que el fondo no funcionará. Ese es un comportamiento documentado y simplemente un descuido de mi parte.

El verdadero problema se encuentra en LayoutInflater.java:

// We try to load the layout params set in the <include /> tag. If
// they don't exist, we will rely on the layout params set in the
// included XML file.
// During a layoutparams generation, a runtime exception is thrown
// if either layout_width or layout_height is missing. We catch
// this exception and set localParams accordingly: true means we
// successfully loaded layout params from the <include /> tag,
// false means we need to rely on the included layout params.
ViewGroup.LayoutParams params = null;
try {
   params = group.generateLayoutParams(attrs);
} catch (RuntimeException e) {
   params = group.generateLayoutParams(childAttrs);
} finally {
   if (params != null) {
     view.setLayoutParams(params);
   }
}

Si la etiqueta <include> no incluye tanto layout_width como layout_height, se produce la RuntimeException y se maneja de forma silenciosa, incluso sin ninguna declaración de registro.

La solución es incluir siempre tanto layout_width como layout_height cuando se usa la etiqueta <include>, si desea anular cualquiera de los atributos de layout_ *.

Mi ejemplo debería cambiar a:

<include
      android:id="@+id/buttons_override"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      layout="@layout/buttons"/>
Eric Burke
fuente
33
Esto es ridículo. Nunca pude hacer que esto funcionara, e incluso vi que la documentación mencionaba la necesidad de tanto alto como ancho si intentaba anular las dimensiones, que asumí que eran alto y ancho. Sin embargo, todo lo que estaba tratando de anular es el margen, que en realidad no es una dimensión. ¿Por qué diablos necesito especificar ambos o incluso cualquiera de ellos cuando todo lo que quiero cambiar es layout_marginRight? Grrr, Android, a veces me frustras demasiado.
Artem Russakovskii
1
Para su información, Android Lint le dará un error (parámetro de diseño layout_height ignorado a menos que layout_width también se especifique en la etiqueta <include>) si no está anulando los atributos de alto y ancho
binario
10

Envié una solicitud de mejora para permitir que se anulen todos los atributos incluidos:

Supongamos que tengo dos diseños idénticos distintos de los valores de un TextViewcampo. Actualmente, he modificado el diseño en tiempo de ejecución o he duplicado el XML.

Por ejemplo, para pasar dos parámetros con los valores "hola" y "mundo" a layout1:

<include layout="@layout/layout1a" params="textView=hello|editText=world" />

layout1a.xml:

<merge><TextView text="@param/textView"><EditText hint="@param/editText"></merge>

Una implementación alternativa rompería la encapsulación y permitiría que la declaración de inclusión anule valores como:

<include layout="@layout/layout1b" overrides="@id/textView.text=hello|@id/editText.hint=world" />

layout1b.xml:

<merge><TextView id="@+id/textView"><EditText hint="@+id/editText"></merge>

Jeff Axelrod
fuente
1
Teniendo en cuenta el nuevo material de enlace de datos, <include>se usa aún más a menudo ahora, la anulación de attr es una característica realmente imprescindible
Dmitry Gryazin
1

Descubrí que a veces extraño incluir la etiqueta android: id cuando uso el constructor de GUI en Eclipse. Asegurándome (cuando me doy cuenta) de agregar en un TextView desde el constructor, la identificación que estoy usando en el diseño de ListView.

<TextView android:text="@+id/textView1"
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />
...

se convierte en

<TextView android:id="@+id/textView1"
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />
...

En lugar de obtener 'falso', 'falso', obtengo :) e incluye trabajar bien.

El Sheriff Solar
fuente