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.