web-dev-qa-db-fra.com

Déclarer un élément personnalisé Android UI en utilisant XML

Comment déclarer un élément Android UI en utilisant XML?

464
Casebash

Le Guide du développeur Android comporte une section intitulée Création de composants personnalisés . Malheureusement, la discussion sur les attributs XML ne couvre que la déclaration du contrôle dans le fichier de présentation et pas la gestion des valeurs dans l'initialisation de la classe. Les étapes sont les suivantes:

1. Déclarez les attributs dans values\attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="Android:text"/>
        <attr name="Android:textColor"/>            
        <attr name="extraInformation" format="string" />
    </declare-styleable>
</resources>

Notez l'utilisation d'un nom non qualifié dans la balise declare-styleable. Les attributs non standard Android tels que extraInformation doivent avoir leur type déclaré. Les étiquettes déclarées dans la superclasse seront disponibles dans les sous-classes sans avoir à être redéclarées.

2. Créer des constructeurs

Etant donné que deux constructeurs utilisent un AttributeSet pour l'initialisation, il est pratique de créer une méthode d'initialisation distincte pour les constructeurs à appeler.

private void init(AttributeSet attrs) { 
    TypedArray a=getContext().obtainStyledAttributes(
         attrs,
         R.styleable.MyCustomView);

    //Use a
    Log.i("test",a.getString(
         R.styleable.MyCustomView_Android_text));
    Log.i("test",""+a.getColor(
         R.styleable.MyCustomView_Android_textColor, Color.BLACK));
    Log.i("test",a.getString(
         R.styleable.MyCustomView_extraInformation));

    //Don't forget this
    a.recycle();
}

R.styleable.MyCustomView est une ressource int[] générée automatiquement, où chaque élément est l'ID d'un attribut. Les attributs sont générés pour chaque propriété du code XML en ajoutant le nom de l'attribut au nom de l'élément. Par exemple, R.styleable.MyCustomView_Android_text contient l'attribut Android_text pour MyCustomView. Les attributs peuvent ensuite être extraits de la TypedArray à l'aide de diverses fonctions get. Si l'attribut n'est pas défini de la manière définie dans le XML, alors null est renvoyé. Sauf, bien sûr, si le type de retour est une primitive, auquel cas le deuxième argument est renvoyé.

Si vous ne souhaitez pas récupérer tous les attributs, il est possible de créer ce tableau manuellement.L'ID des attributs standard Android est inclus dans Android.R.attr, tandis que les attributs de ce projet sont en R.attr.

int attrsWanted[]=new int[]{Android.R.attr.text, R.attr.textColor};

S'il vous plaît noter que vous devriez pas utiliser quoi que ce soit dans Android.R.styleable, comme ce fil cela peut changer à l'avenir . Dans la documentation, il est toujours utile de visualiser toutes ces constantes au même endroit.

3. Utilisez-le dans un fichier de mise en page tel que layout\main.xml

Incluez la déclaration d'espace de nom xmlns:app="http://schemas.Android.com/apk/res-auto" dans l'élément XML de niveau supérieur. Les espaces de noms fournissent une méthode permettant d'éviter les conflits qui surviennent parfois lorsque différents schémas utilisent les mêmes noms d'élément (voir cet article pour plus d'informations). L'URL est simplement une manière d'identifier de manière unique les schémas - rien ne doit en réalité être hébergé sur cette URL . Si cela ne semble pas avoir d'effet, c'est qu'il n'est pas nécessaire d'ajouter le préfixe d'espace de nom, sauf si vous devez résoudre un conflit.

<com.mycompany.projectname.MyCustomView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:background="@Android:color/transparent"
    Android:text="Test text"
    Android:textColor="#FFFFFF"
    app:extraInformation="My extra information"
/> 

Faites référence à la vue personnalisée en utilisant le nom complet.

Exemple LabelView Android

Si vous souhaitez un exemple complet, consultez l'exemple de vue d'étiquette Android.

LabelView.Java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);

attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>

custom_view_1.xml

<com.example.Android.apis.view.LabelView
    Android:background="@drawable/blue"
    Android:layout_width="fill_parent"
    Android:layout_height="wrap_content"
    app:text="Blue" app:textSize="20dp"/>

Ceci est contenu dans un LinearLayout avec un attribut d'espace de nom: xmlns:app="http://schemas.Android.com/apk/res-auto"

Liens

833
Casebash

Grande référence. Merci! Un ajout à cela:

Si vous avez un projet de bibliothèque inclus qui a déclaré des attributs personnalisés pour une vue personnalisée, vous devez déclarer votre espace de nom de projet, et non celui de la bibliothèque. Par exemple:

Étant donné que la bibliothèque contient le package "com.example.library.customview" et que le projet de travail contient le package "com.example.customview", alors:

Ne fonctionnera pas (affiche l'erreur "error: aucun identifiant de ressource trouvé pour l'attribut 'newAttr' dans le package 'com.example.library.customview'"):

<com.library.CustomView
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:app="http://schemas.Android.com/apk/res/com.example.library.customview"
        Android:id="@+id/myView"
        app:newAttr="value" />

Marchera:

<com.library.CustomView
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:app="http://schemas.Android.com/apk/res/com.example.customview"
        Android:id="@+id/myView"
        app:newAttr="value" />
90
Andy

Ajout à la réponse la plus votée.

obtenirStyledAttributes ()

Je souhaite ajouter quelques mots sur l'utilisation de getStyledAttributes () lorsque nous créons une vue personnalisée à l'aide d'Android: attributs prédéfinis xxx. Surtout quand nous utilisons TextAppearance.
Comme indiqué dans "2. Création de constructeurs", la vue personnalisée obtient AttributeSet lors de sa création. L'utilisation principale que nous pouvons voir dans le code source de TextView (API 16).

final Resources.Theme theme = context.getTheme();

// TextAppearance is inspected first, but let observe it later

TypedArray a = theme.obtainStyledAttributes(
            attrs, com.Android.internal.R.styleable.TextView, defStyle, 0);

int n = a.getIndexCount();
for (int i = 0; i < n; i++) 
{
    int attr = a.getIndex(i);
    // huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();

Que pouvons-nous voir ici?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Les attributs sont traités par thème conformément à la documentation. Les valeurs d'attribut sont compilées étape par étape. Les premiers attributs sont remplis à partir du thème, puis les valeurs sont remplacées par les valeurs de style, et enfin les valeurs exactes de XML pour l'instance de vue spéciale remplacent les autres.
Tableau d'attributs demandés - com.Android.internal.R.styleable.TextView
C'est un tableau ordinaire de constantes. Si nous demandons des attributs standard, nous pouvons construire ce tableau manuellement.

Ce qui n'est pas mentionné dans la documentation - ordre de résultat, éléments TypedArray.
Lorsque la vue personnalisée est déclarée dans attrs.xml, des constantes spéciales pour les index d'attribut sont générées. Et nous pouvons extraire les valeurs de cette façon: a.getString(R.styleable.MyCustomView_Android_text). Mais pour le manuel int[], il n'y a pas de constantes. Je suppose que getXXXValue (arrayIndex) fonctionnera correctement.

Et une autre question est: "Comment pouvons-nous remplacer les constantes internes et demander des attributs standard?" Nous pouvons utiliser les valeurs Android.R.attr. *.

Donc, si nous voulons utiliser l'attribut TextAppearance standard dans une vue personnalisée et lire ses valeurs dans le constructeur, nous pouvons modifier le code à partir de TextView de cette façon:

ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;

Resources.Theme theme = context.getTheme();

TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_Android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
    appearance = 
        theme.obtainStyledAttributes(apResourceId, new int[] { Android.R.attr.textColor, Android.R.attr.textSize, 
            Android.R.attr.typeface, Android.R.attr.textStyle });
}
if (appearance != null)
{
    textColorApp = appearance.getColorStateList(0);
    textSize = appearance.getDimensionPixelSize(1, textSize);
    typefaceIndex = appearance.getInt(2, -1);
    styleIndex = appearance.getInt(3, -1);

    appearance.recycle();
}

Où CustomLabel est défini:

<declare-styleable name="CustomLabel">
    <!-- Label text. -->
    <attr name="Android:text" />
    <!-- Label text color. -->
    <attr name="Android:textColor" />
    <!-- Combined text appearance properties. -->
    <attr name="Android:textAppearance" />
</declare-styleable>

Je me trompe peut-être, mais Android la documentation sur obtenirStyledAttributes () est très médiocre.

Extension du composant d'interface utilisateur standard

Dans le même temps, nous pouvons simplement étendre le composant d'interface utilisateur standard en utilisant tous ses attributs déclarés. Cette approche n'est pas très bonne, car TextView par exemple déclare beaucoup de propriétés. Et il sera impossible d'implémenter toutes les fonctionnalités des fonctions onMeasure () et onDraw () surchargées.

Mais nous pouvons sacrifier la réutilisation théorique large du composant personnalisé. Dites "Je sais exactement quelles fonctionnalités je vais utiliser" et ne partagez pas le code avec qui que ce soit.

Ensuite, nous pouvons implémenter le constructeur CustomComponent(Context, AttributeSet, defStyle). Après avoir appelé super(...), tous les attributs seront analysés et disponibles via les méthodes getter.

26
yuriy.weiss

Il semble que Google ait mis à jour sa page de développeur et y ait ajouté diverses formations.

L'un d'eux concerne la création de vues personnalisées et peut être trouvé ici

11
mitch000001

Merci beaucoup pour la première réponse.

Quant à moi, je n’avais qu’un seul problème. En gonflant ma vue, j'ai eu un bug: Java.lang.NoSuchMethodException: MyView (contexte, attributs)

Je l'ai résolu en créant un nouveau constructeur:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}

J'espère que cela aidera!

5
user2346922

Vous pouvez inclure n’importe quel fichier de mise en page dans un autre fichier de mise en page.

             <RelativeLayout
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"
                Android:layout_marginLeft="10dp"
                Android:layout_marginRight="30dp" >

                <include
                    Android:id="@+id/frnd_img_file"
                    Android:layout_width="wrap_content"
                    Android:layout_height="wrap_content"
                    layout="@layout/include_imagefile"/>

                <include
                    Android:id="@+id/frnd_video_file"
                    Android:layout_width="wrap_content"
                    Android:layout_height="wrap_content"
                    layout="@layout/include_video_lay" />

                <ImageView
                    Android:id="@+id/downloadbtn"
                    Android:layout_width="30dp"
                    Android:layout_height="30dp"
                    Android:layout_centerInParent="true"
                    Android:src="@drawable/plus"/>
            </RelativeLayout>

ici, les fichiers de mise en forme de la balise include sont d’autres fichiers de mise en forme .xml situés dans le même dossier res.

0
Akshay Paliwal