Atrybuty o tej samej nazwie w attrs.xml dla widoku niestandardowego

179

Piszę kilka niestandardowych widoków, które mają takie same atrybuty. W odpowiedniej <declare-styleable>sekcji attrs.xmlchciałbym użyć tych samych nazw dla atrybutów:

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

Pojawia się błąd, który to mówi myattr1i myattr2są już zdefiniowane. Uważam, że należy pominąć formatatrybut dla myattr1i myattr2na MyView2, ale jeśli to zrobię, mogę uzyskać następujący błąd w konsoli:

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

Czy jest jakiś sposób, aby to osiągnąć, może jakiś rodzaj przestrzeni nazw (tylko zgadywanie)?

Venator85
źródło

Odpowiedzi:

401

Rozwiązanie: po prostu wyodrębnij wspólne atrybuty z obu widoków i dodaj je bezpośrednio jako dzieci <resources>węzła:

<?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
źródło
11
co się stanie, gdy myattr1zostanie wprowadzony ciąg MyView1i liczba całkowita MyView2?
foxx1337
4
Nie sądzę, np. Możemy mieć atrybut „orientacji” i dla niektórych widoków jest on „poziomy” / „pionowy”, a dla innych „poziomych” / „pionowych” / „kwadratowych”. Z mojego punktu widzenia jest to błąd (lub przynajmniej niespójne zachowanie) w Androidzie (pamiętaj, że: 1. atrybuty nadające się do rysowania zawsze zaczynają się od przedrostka = nazwa widoku i 2. jeśli tworzysz osobne projekty bibliotek dla takich widoków, wszystko będzie działało dobrze )
se.solovyev
4
Kiedy podążam za tą odpowiedzią, otrzymuję ERROR: In <declare-styleable> com_app_view_widget, unable to find attribute customAttr Za cały widok, za który staram się głosić. Jakieś pomysły?
Dapp
45
@Google: Crappy design
Glenn Bech
6
@ foxx1337 Wystarczy użyć <attr name="myattr1" format="string|integer" />. Pracuje dla mnie.
Mygod
57

Publikuję tę odpowiedź, ponieważ powyższe rozwiązanie nie zadziałało w moim przypadku w Android Studio. Muszę udostępnić moje niestandardowe atrybuty w moich niestandardowych widokach, więc wypróbowałem powyższe rozwiązanie w Android Studio, ale nie miałem szczęścia. Więc eksperymentuję i idę na to. Mam nadzieję, że może to pomóc komuś, kto szuka tego samego problemu.

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

To działa dla mnie całkowicie. Musimy ustawić styl nadrzędny, a następnie odziedziczyć styl nadrzędny. Na przykład, tak jak to zrobiłem powyżej: nadająca się do stylizacji nazwa MyView i odziedziczyła ją do moich innych stylowalnych, takich jak MyView1 i MyView2 odpowiednio.

Priya Singhal
źródło
1
To zadziałało dla mnie. Nie mogłem znaleźć sposobu na odniesienie do wyodrębnionych atrybutów w kodzie z zaakceptowanej odpowiedzi.
Nemanja Kovacevic
Hmm ... to dziwne ... przyjęte rozwiązanie wydaje się działać dobrze dla mnie (targetSdkVersion 27). Może dlatego, że atrybuty takie jak „tekst” są wspólne i mogły istnieć w innych plikach attrs.xml ..?
Aba
Próbowałem z nazwą, która najprawdopodobniej jest rzadka, a przyjęte rozwiązanie nadal działało dla mnie.
Aba
Zgadzam się z pierwszym komentarzem! Nie ma możliwości odwołania się do wyodrębnionego atrybutu z typedArray. Dlatego musisz zdefiniować stylowego rodzica
ponownie
Aby to działało, nie zapomnij, aby rozwiązać ten atrybut w rodzica, a nie dziecka (dla klasy MyView2prawej: R.styleable.MyView2_myattr1, źle: R.styleable.MyView_myattr1)
vigilancer
27

Jak odpowiedziała Priya Singhal, Android Studio wymaga zdefiniowania wspólnych nazw atrybutów w ramach własnej nazwy stylu. Nie mogą już być u podstaw.

Należy jednak zwrócić uwagę na kilka innych rzeczy (dlatego dodam również odpowiedź):

  • Typowe style nie muszą być nazywane tak samo jak widok. (Dzięki tej odpowiedzi za wskazanie tego.)
  • Nie musisz używać dziedziczenia z rodzicem.

Przykład

Oto, co zrobiłem w ostatnim projekcie, który ma dwa niestandardowe widoki, które mają te same atrybuty. Tak długo, jak niestandardowe widoki nadal mają nazwy atrybutów i nie zawierają znaku a format, nadal mogę uzyskać do nich dostęp normalnie z kodu.

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

Uproszczony przykład

W rzeczywistości nie muszę nawet umieszczać atrybutów pod niestandardową nazwą. Tak długo, jak je zdefiniuję (daję im format) dla co najmniej jednego niestandardowego widoku, mogę ich używać w dowolnym miejscu (bez format). Działa to również (i wygląda na czystsze):

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

Jednak w przypadku dużego projektu może to być nieuporządkowane, a zdefiniowanie ich u góry w jednym miejscu może być lepsze (zgodnie z zaleceniem tutaj ).

Suragch
źródło
Co jest nie tak z dziedziczeniem? Mam niestandardowe hierarchie widoków, które powiązane styleable nie odzwierciedlają tej relacji, co można wywnioskować tylko w powiązanych definicjach stylów poprzez konwencje nazewnictwa i określone w nich określone elementy (zauważając, że kilka należy do jednego stylu, a kilka do drugiego). Wolę wyrazić to jasno za pomocą parentatrybutu, ale nie widziałem wielu postów sugerujących jego użycie.
samis
@samis, nie pracowałem nad tym przez jakiś czas, ale nie wiem nic złego w użyciu parent. Myślę, że tylko mówiłem, że nie było to wymagane.
Suragch
Nie jest to wymagane, właśnie utworzyłem kolejną podklasę wokół dodatkowego atrybutu, którego nie chciałem umieszczać w klasie podstawowej. Używam tylko komentarzy i konwencji nazewnictwa, aby wskazać separację.
samis
8

Dzięki Lewis, miałem ten sam problem, a twoje rozwiązanie dziedziczenia dało mi wskazówkę, jak to zrobić poniżej i działa dobrze. Właśnie zadeklarowałem wspólne atrybuty powyżej i przepisałem je ponownie w treści deklaracji stylu bez formatowania. Mam nadzieję, że to komuś pomoże

<?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
źródło
1

Na wypadek, gdyby ktoś nadal tkwił w tym problemie po wypróbowaniu dostępnego rozwiązania. Utknąłem z subtitleatrybutem add z stringformatem.

Moim rozwiązaniem jest usunięcie formatu.

przed:

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

po:

<attr name="subtitle"/>

Ahmad Muzakki
źródło