web-dev-qa-db-fra.com

Déclaration d'attributs stylables dans Android

Il existe une précieuse documentation sur le declare-styleable balise par laquelle nous pouvons déclarer des styles personnalisés pour les composants. J'ai trouvé cette liste de valeurs valides pour l'attribut format de la balise attr. Bien que ce soit une bonne chose, cela n'explique pas comment utiliser certaines de ces valeurs. En parcourant attr.xml (la Android pour les attributs standard)), j'ai découvert que vous pouvez faire des choses comme:

<!-- The most prominent text color.  -->
<attr name="textColorPrimary" format="reference|color" />

L'attribut format peut évidemment être défini sur une combinaison de valeurs. Vraisemblablement, l'attribut format aide l'analyseur à interpréter une valeur de style réelle. Puis j'ai découvert ceci dans attr.xml:

<!-- Default text typeface. -->
<attr name="typeface">
    <enum name="normal" value="0" />
    <enum name="sans" value="1" />
    <enum name="serif" value="2" />
    <enum name="monospace" value="3" />
</attr>

<!-- Default text typeface style. -->
<attr name="textStyle">
    <flag name="normal" value="0" />
    <flag name="bold" value="1" />
    <flag name="italic" value="2" />
</attr>

Les deux semblent déclarer un ensemble de valeurs autorisées pour le style indiqué.

J'ai donc deux questions:

  1. Quelle est la différence entre un attribut de style pouvant prendre l'une d'un ensemble de valeurs enum et un autre pouvant prendre un ensemble de valeurs flag?
  2. Quelqu'un connaît-il une meilleure documentation sur la façon dont declare-styleable fonctionne (autre que le reverse engineering du Android)?
77
Ted Hopp

Il y a cette question ici: Définition d'attributs personnalisés avec certains info, mais pas beaucoup.

Et ceci post . Il contient de bonnes informations sur les drapeaux et les enums:

Indicateurs d'attribut XML personnalisés

Les drapeaux sont des types d'attributs spéciaux dans la mesure où ils ne sont autorisés que dans un très petit sous-ensemble de valeurs, à savoir celles qui sont définies sous la balise d'attribut. Les indicateurs sont spécifiés par un attribut "name" et un attribut "value". Les noms doivent être uniques dans ce type d'attribut, mais les valeurs ne doivent pas l'être. C’est la raison pour laquelle, lors de l’évolution de la plate-forme Android, nous avons eu “fill_parent” et “match_parent” mappés tous deux sur le même comportement. Leurs valeurs étaient identiques.

L'attribut name correspond au nom utilisé à la place de la valeur dans le XML de présentation et ne nécessite pas de préfixe d'espace de nom. Par conséquent, pour le "tilingMode" ci-dessus, j'ai choisi "centre" comme valeur d'attribut. J'aurais pu tout aussi facilement choisir "étiré" ou "répéter", mais rien d'autre. Aucune substitution dans les valeurs réelles n'aurait été autorisée.

L'attribut value doit être un entier. Vous avez le choix entre une représentation numérique hexadécimale ou standard. Il y a quelques endroits dans le code Android) où les deux sont utilisés et le compilateur Android est heureux de l’accepter.

Enumères d'attributs XML personnalisés

Les énumérations sont utilisées presque de la même manière que les drapeaux avec une disposition, elles peuvent être utilisées indifféremment avec des entiers. Sous le capot, les énumérations et les entiers sont mappés sur le même type de données, à savoir un entier. Lorsqu'ils apparaissent dans la définition d'attribut avec des entiers, les énumérations servent à empêcher les "nombres magiques" qui sont toujours mauvais. C'est pourquoi vous pouvez avoir un "Android: layout_width" avec une dimension, un entier ou la chaîne nommée "fill_parent".

Pour mettre cela dans le contexte, supposons que je crée un attribut personnalisé appelé "layout_scroll_height" qui accepte soit un entier, soit une chaîne "scroll_to_top". Pour cela, j’ajouterais un attribut de format "entier" et le suivrais avec l’énumération:

<attr name="layout_scroll_height" format="integer">  
    <enum name="scroll_to_top" value="-1"/> 
</attr>

La seule stipulation lors de l’utilisation de Enums de cette manière est qu’un développeur utilisant votre vue personnalisée puisse délibérément placer la valeur "-1" dans les paramètres de présentation. Cela déclencherait la logique de cas particuliers de "scroll_to_top". Un tel comportement inattendu (ou attendu) pourrait rapidement reléguer votre bibliothèque dans la pile de "code hérité" si les valeurs Enum étaient mal choisies.


Selon moi, les vraies valeurs que vous pouvez ajouter en réalité à un attribut sont limitées par ce que vous pouvez en obtenir. Vérifiez la référence de la classe AttributeSetici pour plus de conseils.

Vous pouvez obtenir:

  • booléens (getAttributeBooleanValue),
  • des flotteurs (getAttributeFloatValue),
  • ints (getAttributeIntValue),
  • ints (comme getAttributeUnsignedIntValue),
  • et des chaînes de caractères (getAttributeValue)
69
Aleadam

La réponse de @Aleadam est très utile, mais elle omet une différence majeure entre enum et flag. Le premier est destiné à nous permettre de choisir une valeur et une seule valeur lorsque nous affectons l'attribut correspondant à une vue. Les valeurs de ce dernier peuvent toutefois être combinées à l'aide de l'opérateur binaire OR.

Un exemple, dans res/values/attr.xml

<!-- declare myenum attribute -->
<attr name="myenum">
    <enum name="zero" value="0" />
    <enum name="one" value="1" />
    <enum name="two" value="2" />
    <enum name="three" value="3" />
</attr>

<!-- declare myflags attribute -->
<attr name="myflags">
    <flag name="one" value="1" />
    <flag name="two" value="2" />
    <flag name="four" value="4" />
    <flag name="eight" value="8" />
</attr>

<!-- declare our custom widget to be styleable by these attributes -->
<declare-styleable name="com.example.MyWidget">
    <attr name="myenum" />
    <attr name="myflags" />
</declare-styleable>

Dans res/layout/mylayout.xml nous pouvons maintenant faire

<com.example.MyWidget
    myenum="two"
    myflags="one|two"
    ... />

Ainsi, une énumération sélectionne l'une de ses valeurs possibles, tandis que les indicateurs peuvent être combinés. Les valeurs numériques doivent refléter cette différence. En général, vous voudrez que la séquence aille 0,1,2,3,... pour les énumérations (à utiliser comme index de tableau, par exemple) et les indicateurs à parcourir 1,2,4,8,... afin qu’ils puissent être ajoutés ou supprimés indépendamment, en utilisant bitwise OR | pour combiner des drapeaux.

Nous pourrions définir explicitement les "méta flags" avec des valeurs qui ne sont pas une puissance de 2 et introduire ainsi une sorte de raccourci pour les combinaisons courantes. Par exemple, si nous avions inclus cela dans notre myflags déclaration

<flag name="three" value="3" />

alors nous aurions pu écrire myflags="three" au lieu de myflags="one|two", pour des résultats complètement identiques à 3 == 1|2.

Personnellement, j'aime toujours inclure

<flag name="none" value="0" /> <!-- or "normal, "regular", and so on -->
<flag name="all" value="15" /> <!-- 15 == 1|2|4|8 -->

ce qui me permettra de désactiver ou de mettre tous les drapeaux à la fois.

Plus subtilement, il se peut qu'un drapeau soit impliqué par un autre. Donc, dans notre exemple, supposons que le drapeau eight en cours de définition force le drapeau four (s'il ne l'était pas déjà). Nous pourrions alors redéfinir eight pour pré-inclure, pour ainsi dire, le drapeau four,

<flag name="eight" value="12" /> <!-- 12 == 8|4 -->

Enfin, si vous déclarez les attributs dans un projet de bibliothèque mais souhaitez les appliquer aux présentations d'un autre projet (dépendant de la bibliothèque), vous devez utiliser un préfixe d'espace de nom que vous devez lier dans l'élément racine XML. Par exemple.,

<RelativeLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:auto="http://schemas.Android.com/apk/res-auto"
    ... >

    <com.example.MyWidget
        auto:myenum="two"
        auto:myflags="one|two"
        ... />

</RelativeLayout>
63
Rad Haring