web-dev-qa-db-fra.com

Définir les attributs personnalisés

J'ai besoin d'implémenter mes propres attributs comme dans com.Android.R.attr

N'ayant rien trouvé dans la documentation officielle, j'ai donc besoin d'informations sur la manière de définir ces attributs et de les utiliser à partir de mon code.

441

Actuellement, la meilleure documentation est la source. Vous pouvez y jeter un oeil here (attrs.xml) .

Vous pouvez définir des attributs dans l'élément <resources> supérieur ou à l'intérieur d'un élément <declare-styleable>. Si je vais utiliser un attr à plusieurs endroits, je le mets dans l'élément racine. Notez que tous les attributs partagent le même espace de noms global. Cela signifie que même si vous créez un nouvel attribut à l'intérieur d'un élément <declare-styleable>, il peut être utilisé en dehors de celui-ci et vous ne pouvez pas en créer un autre avec le même nom, mais d'un type différent.

Un élément <attr> a deux attributs XML name et format. name vous permet d'appeler quelque chose et c'est ainsi que vous vous y référez dans le code, par exemple, R.attr.my_attribute. L'attribut format peut avoir différentes valeurs en fonction du "type" d'attribut souhaité.

  • référence - s'il fait référence à un autre ID de ressource (par exemple, "@ color/my_color", "@ layout/my_layout")
  • couleur
  • booléen
  • dimension
  • float
  • entier
  • chaîne
  • fraction
  • enum - normalement défini implicitement
  • flag - normalement défini implicitement

Vous pouvez définir le format sur plusieurs types en utilisant |, par exemple, format="reference|color".

Les attributs enum peuvent être définis comme suit:

<attr name="my_enum_attr">
  <enum name="value1" value="1" />
  <enum name="value2" value="2" />
</attr>

Les attributs flag sont similaires, à ceci près que les valeurs doivent être définies pour pouvoir être binaires ensemble:

<attr name="my_flag_attr">
  <flag name="fuzzy" value="0x01" />
  <flag name="cold" value="0x02" />
</attr>

En plus des attributs, il y a l'élément <declare-styleable>. Cela vous permet de définir les attributs qu'une vue personnalisée peut utiliser. Pour ce faire, vous spécifiez un élément <attr>. S'il avait déjà été défini, vous ne spécifiez pas le paramètre format. Si vous souhaitez réutiliser un Android attr, par exemple, Android: gravity, vous pouvez le faire dans la name, comme suit.

Exemple de vue personnalisée <declare-styleable>:

<declare-styleable name="MyCustomView">
  <attr name="my_custom_attribute" />
  <attr name="Android:gravity" />
</declare-styleable>

Lorsque vous définissez vos attributs personnalisés au format XML sur votre vue personnalisée, vous devez effectuer certaines tâches. Tout d'abord, vous devez déclarer un espace de noms pour trouver vos attributs. Vous faites cela sur l'élément de présentation racine. Normalement, il n'y a que xmlns:Android="http://schemas.Android.com/apk/res/Android". Vous devez maintenant également ajouter xmlns:whatever="http://schemas.Android.com/apk/res-auto".

Exemple:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:Android="http://schemas.Android.com/apk/res/Android"
  xmlns:whatever="http://schemas.Android.com/apk/res-auto"
  Android:orientation="vertical"
  Android:layout_width="fill_parent"
  Android:layout_height="fill_parent">

    <org.example.mypackage.MyCustomView
      Android:layout_width="fill_parent"
      Android:layout_height="wrap_content"
      Android:gravity="center"
      whatever:my_custom_attribute="Hello, world!" />
</LinearLayout>

Enfin, pour accéder à cet attribut personnalisé, vous le faites normalement dans le constructeur de votre vue personnalisée comme suit.

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

  TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);

  String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);

  //do something with str

  a.recycle();
}

La fin. :)

920
Rich Schuler

La réponse de Qberticus est bonne, mais il manque un détail utile. Si vous implémentez ceux-ci dans une bibliothèque, remplacez:

xmlns:whatever="http://schemas.Android.com/apk/res/org.example.mypackage"

avec:

xmlns:whatever="http://schemas.Android.com/apk/res-auto"

Sinon, l'application qui utilise la bibliothèque aura des erreurs d'exécution.

84
Neil Miller

La réponse ci-dessus couvre tout ce qui est détaillé, à part quelques choses.

Premièrement, s'il n'y a pas de styles, la signature de méthode (Context context, AttributeSet attrs) sera utilisée pour instancier la préférence. Dans ce cas, utilisez simplement context.obtainStyledAttributes(attrs, R.styleable.MyCustomView) pour obtenir le TypedArray.

Deuxièmement, il ne traite pas de la façon de gérer les ressources de plaurals (chaînes de quantité). Ceux-ci ne peuvent pas être traités avec TypedArray. Voici un extrait de code de mon SeekBarPreference qui définit le résumé de la préférence, en formatant sa valeur en fonction de la valeur de la préférence. Si le xml de la préférence définit Android: summary en chaîne de texte ou en chaîne, la valeur de la préférence est formatée dans la chaîne (il devrait contenir% d pour récupérer la valeur). Si Android: summary est défini sur une ressource Plaurals, il est utilisé pour formater le résultat.

// Use your own name space if not using an Android resource.
final static private String Android_NS = 
    "http://schemas.Android.com/apk/res/Android";
private int pluralResource;
private Resources resources;
private String summary;

public SeekBarPreference(Context context, AttributeSet attrs) {
    // ...
    TypedArray attributes = context.obtainStyledAttributes(
        attrs, R.styleable.SeekBarPreference);
    pluralResource =  attrs.getAttributeResourceValue(Android_NS, "summary", 0);
    if (pluralResource !=  0) {
        if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {
            pluralResource = 0;
        }
    }
    if (pluralResource ==  0) {
        summary = attributes.getString(
            R.styleable.SeekBarPreference_Android_summary);
    }
    attributes.recycle();
}

@Override
public CharSequence getSummary() {
    int value = getPersistedInt(defaultValue);
    if (pluralResource != 0) {
        return resources.getQuantityString(pluralResource, value, value);
    }
    return (summary == null) ? null : String.format(summary, value);
}

  • Ceci n'est donné qu'à titre d'exemple. Toutefois, si vous souhaitez tenter de définir le résumé sur l'écran des préférences, vous devez appeler notifyChanged() dans la méthode onDialogClosed de la préférence.
15
Steve Waring

L'approche traditionnelle est pleine de code passe-partout et de gestion des ressources maladroite. C'est pourquoi j'ai créé le cadre Spyglass . Pour illustrer son fonctionnement, voici un exemple montrant comment créer une vue personnalisée qui affiche un titre de chaîne.

Étape 1: Créez une classe de vue personnalisée.

public class CustomView extends FrameLayout {
    private TextView titleView;

    public CustomView(Context context) {
        super(context);
        init(null, 0, 0);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0, 0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr, 0);
    }

    @RequiresApi(21)
    public CustomView(
            Context context, 
            AttributeSet attrs,
            int defStyleAttr,
            int defStyleRes) {

        super(context, attrs, defStyleAttr, defStyleRes);
        init(attrs, defStyleAttr, defStyleRes);
    }

    public void setTitle(String title) {
        titleView.setText(title);
    }

    private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        inflate(getContext(), R.layout.custom_view, this);

        titleView = findViewById(R.id.title_view);
    }
}

Étape 2: définissez un attribut de chaîne dans le fichier de ressources values/attrs.xml:

<resources>
    <declare-styleable name="CustomView">
        <attr name="title" format="string"/>
    </declare-styleable>
</resources>

Étape 3: Appliquez l'annotation @StringHandler à la méthode setTitle pour indiquer au cadre Spyglass d'acheminer la valeur d'attribut vers cette méthode lorsque la vue est gonflée.

@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
    titleView.setText(title);
}

Maintenant que votre classe a une annotation Spyglass, le framework Spyglass la détectera au moment de la compilation et générera automatiquement la classe CustomView_SpyglassCompanion.

Étape 4: Utilisez la classe générée dans la méthode init de la vue personnalisée:

private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    inflate(getContext(), R.layout.custom_view, this);

    titleView = findViewById(R.id.title_view);

    CustomView_SpyglassCompanion
            .builder()
            .withTarget(this)
            .withContext(getContext())
            .withAttributeSet(attrs)
            .withDefaultStyleAttribute(defStyleAttr)
            .withDefaultStyleResource(defStyleRes)
            .build()
            .callTargetMethodsNow();
}

C'est ça. Désormais, lorsque vous instanciez la classe à partir de XML, le compagnon Spyglass interprète les attributs et effectue l'appel de méthode requis. Par exemple, si nous gonflons la disposition suivante, alors setTitle sera appelé avec "Hello, World!" comme argument.

<FrameLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:width="match_parent"
    Android:height="match_parent">

    <com.example.CustomView
        Android:width="match_parent"
        Android:height="match_parent"
        app:title="Hello, World!"/>
</FrameLayout>

La structure n'est pas limitée aux ressources de chaîne et comporte de nombreuses annotations différentes pour la gestion d'autres types de ressources. Il comporte également des annotations permettant de définir les valeurs par défaut et de transmettre des valeurs de substitution si vos méthodes ont plusieurs paramètres.

Consultez le rapport Github pour plus d’informations et des exemples.

5
Helios