Atributos com o mesmo nome em attrs.xml para visualização personalizada


180

Estou escrevendo algumas visualizações personalizadas que compartilham alguns atributos com o mesmo nome. Em sua respectiva <declare-styleable>seção, attrs.xmleu gostaria de usar os mesmos nomes para 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>

Estou recebendo um erro dizendo isso myattr1e myattr2já está definido. Descobri que deveria omitir o formatatributo for myattr1e myattr2in MyView2, mas se fizer isso, obtenho o seguinte erro no console:

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

Existe uma maneira de eu conseguir isso, talvez algum tipo de espaço para nome (apenas adivinhando)?

Respostas:


401

Solução: basta extrair atributos comuns das duas visualizações e adicioná-los diretamente como filhos do <resources>nó:

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

11
o que acontece quando myattr1está a string MyView1e o número inteiro MyView2?
— foxx1337

4
Acho que não, por exemplo, podemos ter o atributo 'orientação' e, para alguns, é 'horizontal' / 'vertical' e para outros 'paisagem' / 'retrato' / 'quadrado'. Do meu ponto de vista, é um bug (ou pelo menos um comportamento inconsistente) no Android (lembre-se de que: 1. atributos estilizados sempre começam com prefix = view name e 2. se você criar projetos de bibliotecas separados para essas visualizações, tudo funcionará bem )
— se.solovyev 04/04

4
Quando sigo esta resposta, recebo ERROR: In <declare-styleable> com_app_view_widget, unable to find attribute customAttr Por todas as visualizações que tento declarar. Alguma ideia?
— Dapp

45
@Google: Crappy design
— Glenn Bech

6
@ foxx1337 Basta usar <attr name="myattr1" format="string|integer" />. Funciona para mim.
— Mygod

58

Estou postando esta resposta, pois a solução postada acima não funcionou para mim no Android Studio. Preciso compartilhar meus atributos personalizados entre minhas visualizações personalizadas. Tentei a solução acima no Android Studio, mas não tive sorte. Então, experimento e faço uma maneira de fazê-lo. Espero que ajude alguém que esteja procurando pelo mesmo 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>

Isso funciona para mim completamente. Precisamos tornar um pai estiloso e, em seguida, herdar esse pai estilizado. Por exemplo, como eu fiz acima: Nome estiloso pai MyView e herdou isso para outros estilos como MyView1 e MyView2, respectivamente.


1
Isso funcionou para mim. Não consegui encontrar uma maneira de referenciar os atributos extraídos no código a partir da resposta aceita.
— Nemanja Kovacevic

Hmm ... isso é estranho .. a solução aceita parece estar funcionando bem para mim (targetSdkVersion 27). Talvez porque atributos como "texto" sejam comuns e possam ter existido em outros atributos. Xml ..?
— Aba

Tentei com um nome que provavelmente é incomum e a solução aceita ainda funcionou para mim.
— Aba

Eu concordo com o primeiro comentário! Não há como referenciar um atributo extraído de typedArray. Portanto, você precisa definir um pai
— estiloso

Para que isso funcione não se esqueça de abordar este atributo no pai e não na criança (para a classe MyView2direita: R.styleable.MyView2_myattr1, errado: R.styleable.MyView_myattr1)
— vigilancer

28

Como Priya Singhal respondeu, o Android Studio exige que os nomes de atributos comuns sejam definidos em seu próprio nome de estilo. Eles não podem mais estar na raiz.

No entanto, há algumas outras coisas a serem observadas (e é por isso que também estou adicionando uma resposta):

  • Os estilos comuns não precisam ter o mesmo nome que uma exibição. (Obrigado a esta resposta por apontar isso.)
  • Você não precisa usar herança com um pai.

Exemplo

Aqui está o que eu fiz em um projeto recente que possui duas visualizações personalizadas que compartilham os mesmos atributos. Enquanto as visualizações personalizadas ainda tiverem os nomes dos atributos e não incluírem a format, ainda posso acessá-las normalmente a partir do 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>

Exemplo simplificado

Na verdade, nem preciso colocar os atributos em um nome personalizado. Desde que eu os defina (dê a format) a pelo menos uma visualização personalizada, posso usá-los em qualquer lugar (sem a format). Portanto, isso também funciona (e parece mais limpo):

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

Para um projeto grande, porém, isso pode ficar confuso e defini-los na parte superior em um único local pode ser melhor (como recomendado aqui ).


O que há de errado com a herança? Eu tenho hierarquias de exibição personalizadas cujos estilos associados não refletem esse relacionamento, que só pode ser deduzido nas definições de estilo associadas por convenções de nomenclatura e itens específicos definidos nela (observando que alguns pertencem a um estilo e outros a outro). Prefiro torná-lo explícito com o parentatributo, mas não vi muitas postagens sugerindo seu uso.
— samis 15/01/19

@ samis, eu não trabalho nisso há um tempo, mas não sei nada de errado em usar parent. Eu acho que estava apenas dizendo que não era necessário.
— Suragch 15/01/19

Não é necessário, apenas criei outra subclasse envolvida em um atributo adicional que não queria colocar na classe base. Estou apenas usando comentários e convenções de nomenclatura para indicar a separação.
— samis 16/01/19

8

Graças a Lewis, tive o mesmo problema, e sua solução de herança me deu a dica para fazê-lo como abaixo e funciona bem. Acabei de declarar atributos comuns no item acima e reescrevê-lo no corpo da declaração de estilo novamente sem formatação. Eu espero que isso ajude alguém

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


1

Apenas no caso de alguém ainda estar com esse problema depois de tentar a solução disponível. Fiquei com adicionar subtitleatributo com stringformato.

Minha solução é remover o formato.

antes:

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

depois de:

<attr name="subtitle"/>

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.