web-dev-qa-db-fra.com

Comment: définir un thème (style) pour un widget personnalisé

J'ai écrit un widget personnalisé pour un contrôle que nous utilisons largement dans notre application. La classe de widget dérive de ImageButton et l'étend de quelques manières simples. J'ai défini un style que je peux appliquer au widget tel qu'il est utilisé, mais je préfère le configurer via un thème. Dans R.styleable Je vois des attributs de style de widget comme imageButtonStyle et textViewStyle. Existe-t-il un moyen de créer quelque chose comme ça pour le widget personnalisé que j'ai écrit?

81
jsmith

Oui, il y a un moyen:

Supposons que vous ayez une déclaration d'attributs pour votre widget (dans attrs.xml):

<declare-styleable name="CustomImageButton">
    <attr name="customAttr" format="string"/>
</declare-styleable>

Déclarez un attribut que vous utiliserez comme référence de style (dans attrs.xml):

<declare-styleable name="CustomTheme">
    <attr name="customImageButtonStyle" format="reference"/>
</declare-styleable>

Déclarez un ensemble de valeurs d'attribut par défaut pour le widget (dans styles.xml):

<style name="Widget.ImageButton.Custom" parent="Android:style/Widget.ImageButton">
    <item name="customAttr">some value</item>
</style>

Déclarez un thème personnalisé (dans themes.xml):

<style name="Theme.Custom" parent="@Android:style/Theme">
    <item name="customImageButtonStyle">@style/Widget.ImageButton.Custom</item>
</style>

Utilisez cet attribut comme troisième argument dans le constructeur de votre widget (dans CustomImageButton.Java):

public class CustomImageButton extends ImageButton {
    private String customAttr;

    public CustomImageButton( Context context ) {
        this( context, null );
    }

    public CustomImageButton( Context context, AttributeSet attrs ) {
        this( context, attrs, R.attr.customImageButtonStyle );
    }

    public CustomImageButton( Context context, AttributeSet attrs,
            int defStyle ) {
        super( context, attrs, defStyle );

        final TypedArray array = context.obtainStyledAttributes( attrs,
            R.styleable.CustomImageButton, defStyle,
            R.style.Widget_ImageButton_Custom ); // see below
        this.customAttr =
            array.getString( R.styleable.CustomImageButton_customAttr, "" );
        array.recycle();
    }
}

Vous devez maintenant appliquer Theme.Custom à toutes les activités qui utilisent CustomImageButton (dans AndroidManifest.xml):

<activity Android:name=".MyActivity" Android:theme="@style/Theme.Custom"/>

C'est tout. Maintenant, CustomImageButton essaie de charger les valeurs d'attribut par défaut à partir de l'attribut customImageButtonStyle du thème actuel. Si aucun attribut de ce type n'est trouvé dans le thème ou si la valeur de l'attribut est @null alors l'argument final de obtainStyledAttributes sera utilisé: Widget.ImageButton.Custom dans ce cas.

Vous pouvez modifier les noms de toutes les instances et de tous les fichiers (sauf AndroidManifest.xml) mais il serait préférable d'utiliser Android convention de dénomination.

205
Michael

Un autre aspect, en plus de l'excellente réponse de Michael, est le remplacement des attributs personnalisés dans les thèmes. Supposons que vous ayez un certain nombre de vues personnalisées qui font toutes référence à l'attribut personnalisé "custom_background".

<declare-styleable name="MyCustomStylables">
    <attr name="custom_background" format="color"/>
</declare-styleable>

Dans un thème, vous définissez quelle est la valeur

<style name="MyColorfulTheme" parent="AppTheme">
    <item name="custom_background">#ff0000</item>
</style>

ou

<style name="MyBoringTheme" parent="AppTheme">
    <item name="custom_background">#ffffff</item>
</style>

Vous pouvez faire référence à l'attribut dans un style

<style name="MyDefaultLabelStyle" parent="AppTheme">
    <item name="Android:background">?background_label</item>
</style>

Remarquez le point d'interrogation, également utilisé pour la référence Android comme dans

?android:attr/colorBackground

Comme la plupart d'entre vous l'ont remarqué, vous pouvez -et probablement devriez- utiliser des références @color au lieu de couleurs codées en dur.

Alors pourquoi ne pas faire

<item name="Android:background">@color/my_background_color</item>

Vous ne pouvez pas modifier la définition de "my_background_color" au moment de l'exécution, tandis que vous pouvez facilement changer de thème.

4
mir