Atributos con el mismo nombre en attrs.xml para vista personalizada

180

Estoy escribiendo algunas vistas personalizadas que comparten algunos atributos con el mismo nombre. En su <declare-styleable>sección respectiva , attrs.xmlme gustaría usar los mismos nombres para los atributos:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView1">
        <attr name="myattr1" format="string" />
        <attr name="myattr2" format="dimension" />
        ...
    </declare-styleable>

    <declare-styleable name="MyView2">
        <attr name="myattr1" format="string" />
        <attr name="myattr2" format="dimension" />
        ...
    </declare-styleable>
</resources>

Recibo un error que dice eso myattr1y myattr2ya estoy definido. Descubrí que debería omitir el formatatributo for myattr1and myattr2in MyView2, pero si lo hago, obtengo el siguiente error en la consola:

[2010-12-13 23:53:11 - MyProject] ERROR: In <declare-styleable> MyView2, unable to find attribute 

¿Hay alguna manera de lograr esto, tal vez algún tipo de espacio de nombres (solo adivinando)?

Venator85
fuente

Respuestas:

401

Solución: simplemente extraiga los atributos comunes de ambas vistas y agréguelos directamente como elementos secundarios del <resources>nodo:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="myattr1" format="string" />
    <attr name="myattr2" format="dimension" />

    <declare-styleable name="MyView1">
        <attr name="myattr1" />
        <attr name="myattr2" />
        ...
    </declare-styleable>

    <declare-styleable name="MyView2">
        <attr name="myattr1" />
        <attr name="myattr2" />
        ...
    </declare-styleable>
</resources>
Venator85
fuente
11
¿Qué sucede cuando myattr1se introducen una cadena MyView1y un entero MyView2?
foxx1337
44
No lo creo, por ejemplo, podemos tener el atributo 'orientación' y para algunas vistas es 'horizontal' / 'vertical' y para otro 'paisaje' / 'retrato' / 'cuadrado'. Desde mi punto de vista, es un error (o al menos un comportamiento inconsistente) en Android (tenga en cuenta que: 1. los atributos de estilo siempre comienzan con el prefijo = nombre de la vista y 2. si crea proyectos de biblioteca separados para tales vistas, todo funcionará bien )
se.solovyev
44
Cuando sigo esta respuesta obtengo ERROR: In <declare-styleable> com_app_view_widget, unable to find attribute customAttr Por toda la vista que intento declarar. ¿Algunas ideas?
Dapp
45
@Google: Diseño horrible
Glenn Bech
66
@ foxx1337 Solo usa <attr name="myattr1" format="string|integer" />. Funciona para mi.
Mygod
58

Estoy publicando esta respuesta ya que la solución publicada anteriormente no funcionó para mí en Android Studio. Necesito compartir mis atributos personalizados entre mis vistas personalizadas, así que probé la solución anterior en Android Studio pero no tuve suerte. Entonces experimento y hago un camino para hacerlo. Espero que pueda ayudar a alguien que busca el mismo problema.

  <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <!-- parent styleable -->
     <declare-styleable name="MyView">
         <attr name="myattr1" format="string" />
         <attr name="myattr2" format="dimension" />
     </declare-styleable>

     <!-- inheriting parent styleable -->
     <!-- also note "myBackgroundColor" belongs to child styleable"MyView1"-->
    <declare-styleable name="MyView1" parent="MyView">
        <attr name="myattr1" />
        <attr name="myattr2" />
        <attr name="myBackgroundColor" format="color"/>
    </declare-styleable>


    <!-- inheriting parent styleable -->
    <!-- same way here "myfonnt" belongs to child styelable "MyView2" -->
    <declare-styleable name="MyView2" parent="MyView">
        <attr name="myattr1" />
        <attr name="myattr2" />
        <attr name="myfont" format="string"/>
        ...
    </declare-styleable>
</resources>

Esto funciona para mí por completo. Necesitamos hacer que un padre tenga estilo y luego debemos heredar ese padre con estilo. Por ejemplo, como he hecho anteriormente: Nombre con estilo de los padres MyView y heredé esto a mi otro estilo como MyView1 y MyView2 respectivamente.

Priya Singhal
fuente
1
Esto funcionó para mí. No pude encontrar una manera de hacer referencia a los atributos extraídos en el código de la respuesta aceptada.
Nemanja Kovacevic
Hmm ... eso es raro ... la solución aceptada parece estar funcionando bien para mí (targetSdkVersion 27). ¿Quizás porque los atributos como "texto" son comunes y pueden haber existido en algunos otros atributos.
Aba
Intenté con un nombre que probablemente sea poco común y la solución aceptada todavía funcionó para mí.
Aba
¡Estoy de acuerdo con el primer comentario! No hay forma de hacer referencia a un atributo extraído de typedArray. Por lo tanto, debe definir un padre con estilo
reavcn
para que esto funcione, no olvide abordar este atributo en padre y no en hijo (para la clase MyView2correcta: R.styleable.MyView2_myattr1incorrecta R.styleable.MyView_myattr1)
vigilante
28

Como respondió Priya Singhal, Android Studio requiere que los nombres de atributos comunes se definan dentro de su propio nombre de estilo. Ya no pueden estar en la raíz.

Sin embargo, hay un par de otras cosas a tener en cuenta (por eso también estoy agregando una respuesta):

  • No es necesario que los estilos comunes tengan el mismo nombre que una vista. (Gracias a esta respuesta por señalar eso).
  • No necesita usar la herencia con un padre.

Ejemplo

Esto es lo que hice en un proyecto reciente que tiene dos vistas personalizadas que comparten los mismos atributos. Siempre que las vistas personalizadas sigan teniendo los nombres de los atributos y no incluyan un format, todavía puedo acceder a ellos de forma normal desde el código.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- common attributes to all custom text based views -->

    <declare-styleable name="TextAttributes">
        <attr name="text" format="string"/>
        <attr name="textSize" format="dimension"/>
        <attr name="textColor" format="color"/>
        <attr name="gravity">
            <flag name="top" value="48" />
            <flag name="center" value="17" />
            <flag name="bottom" value="80" />
        </attr>
    </declare-styleable>

    <!-- custom text views -->

    <declare-styleable name="View1">
        <attr name="text"/>
        <attr name="textSize"/>
        <attr name="textColor"/>
        <attr name="gravity"/>
    </declare-styleable>

    <declare-styleable name="View2">
        <attr name="text"/>
        <attr name="textSize"/>
        <attr name="textColor"/>
        <attr name="gravity"/>
    </declare-styleable>

</resources>

Ejemplo simplificado

De hecho, ni siquiera necesito poner los atributos bajo un nombre personalizado. Siempre que los defina (déles un format) para al menos una vista personalizada, puedo usarlos en cualquier lugar (sin el format). Entonces esto también funciona (y se ve más limpio):

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="View1">
        <attr name="text" format="string"/>
        <attr name="textSize" format="dimension"/>
        <attr name="textColor" format="color"/>
        <attr name="gravity">
            <flag name="top" value="48" />
            <flag name="center" value="17" />
            <flag name="bottom" value="80" />
        </attr>
    </declare-styleable>

    <declare-styleable name="View2">
        <attr name="text"/>
        <attr name="textSize"/>
        <attr name="textColor"/>
        <attr name="gravity"/>
    </declare-styleable>

</resources>

Sin embargo, para un proyecto grande, esto podría ser complicado y definirlos en la parte superior en una sola ubicación podría ser mejor (como se recomienda aquí ).

Suragch
fuente
¿Qué hay de malo con la herencia? Tengo jerarquías de vistas personalizadas cuyos estilos asociados no reflejan esta relación, que solo se puede deducir en las definiciones de estilo asociadas al nombrar las convenciones y los elementos específicos definidos en ellas (notando que algunos pertenecen a un estilo y algunos a otro). Prefiero hacerlo explícito con el parentatributo, pero no he visto muchas publicaciones que sugieran su uso.
samis
@samis, no he trabajado en esto por un tiempo, pero no sé nada malo con el uso parent. Creo que solo decía que no era necesario.
Suragch
No es obligatorio, solo hice otra subclase envuelta alrededor de un atributo adicional que no quería poner en la clase base. Solo estoy usando comentarios y convenciones de nombres para indicar la separación.
samis
8

Gracias Lewis, tuve el mismo problema, y ​​su solución de herencia me dio la pista para hacerlo como se muestra a continuación y funciona bien. Acabo de declarar atributos comunes en lo anterior y volver a escribirlo en el cuerpo de la declaración de estilo sin formato. Espero que esto ayude a alguien

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- common attributes -->
     <attr name="myattr1" format="string" />
     <attr name="myattr2" format="dimension" />

 <!-- also note "myBackgroundColor" belongs to child styleable"MyView1"-->
<declare-styleable name="MyView1" >
    <attr name="myattr1" />
    <attr name="myattr2" />
    <attr name="myBackgroundColor" format="color"/>
</declare-styleable>

<!-- same way here "myfonnt" belongs to child styelable "MyView2" -->
<declare-styleable name="MyView2" parent="MyView">
    <attr name="myattr1" />
    <attr name="myattr2" />
    <attr name="myfont" format="string"/>
    ...
</declare-styleable>

Hanieh Variani
fuente
1

En caso de que alguien aún se quede con este problema después de probar la solución disponible. Me quedé con agregar subtitleatributo con stringformato.

Mi solución es eliminar el formato.

antes de:

<attr name="subtitle" format="string"/>

después:

<attr name="subtitle"/>

Ahmad Muzakki
fuente