Question Déclaration d'un élément d'interface utilisateur Android personnalisé à l'aide de XML


Comment déclarer un élément d'interface utilisateur Android en utilisant XML?


442
2018-04-23 01:36


origine


Réponses:


Le Guide du développeur Android contient une section intitulée Construire des composants personnalisés. Malheureusement, la discussion des attributs XML ne couvre que la déclaration du contrôle dans le fichier de mise en page et ne gère pas réellement les valeurs à l'intérieur de 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 le declare-styleable marque. Attributs android non standard comme extraInformation besoin de faire déclarer leur type. Les balises déclarées dans la superclasse seront disponibles dans des sous-classes sans avoir à être redéclarées.

2. Créez des constructeurs

Comme il y a deux constructeurs qui utilisent un AttributeSet pour l'initialisation, il convient 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 un autogenerated int[] ressource où chaque élément est l'identifiant d'un attribut. Les attributs sont générés pour chaque propriété dans le fichier XML en ajoutant le nom de l'attribut au nom de l'élément. Par exemple, R.styleable.MyCustomView_android_text contient le android_text attribut pour MyCustomView. Les attributs peuvent ensuite être récupérés à partir du TypedArray en utilisant divers get les fonctions. Si l'attribut n'est pas défini dans le fichier XML, alors null est retourné. Sauf, bien sûr, si le type de retour est une primitive, auquel cas le second argument est renvoyé.

Si vous ne voulez pas récupérer tous les attributs, il est possible de créer ce tableau manuellement. L'ID des attributs android standard est inclus dans android.R.attr, alors 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 ne pas utiliser quoi que ce soit dans android.R.styleable, selon ce fil cela pourrait changer dans le futur. Il est toujours dans la documentation de voir toutes ces constantes au même endroit est utile.

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

Inclure la déclaration d'espace de noms 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 pour éviter les conflits qui se produisent parfois lorsque différents schémas utilisent les mêmes noms d'éléments (voir Cet article pour plus d'informations). L'URL est simplement une manière de schémas d'identification unique - rien n'a vraiment besoin d'être hébergé à cette URL. Si cela ne semble pas faire quoi que ce soit, c'est parce que vous n'avez pas réellement besoin d'ajouter le préfixe d'espace de nommage à moins que vous ayez besoin de 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"
/> 

Référencez la vue personnalisée à l'aide du nom complet.

Android LabelView Sample

Si vous voulez un exemple complet, regardez 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 nommage: xmlns:app="http://schemas.android.com/apk/res-auto"

Liens


809
2018-04-23 01:36



Bonne 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 noms de projet, pas celui de la bibliothèque. Par exemple:

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

Ne fonctionnera pas (montre l'erreur "error: Aucun identificateur de ressource trouvé pour l'attribut 'newAttr' dans le paquet      '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" />

89
2017-11-28 06:24



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

obtenirStyledAttributes ()

Je veux ajouter quelques mots à propos de l'utilisation de getStyledAttributes (), lorsque nous créons une vue personnalisée en utilisant android: xxx attributs prédéfinis. Surtout quand nous utilisons TextAppearance.
Comme mentionné dans "2. Création de constructeurs", la vue personnalisée obtient AttributeSet lors de sa création. Utilisation principale que nous pouvons voir dans le code source 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)
L'ensemble d'attributs est traité par thème selon 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 du style, et enfin les valeurs exactes du XML pour l'instance de vue spéciale remplacent les autres.
Tableau des 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 des éléments TypedArray de résultat.
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 de android.R.attr. *.

Donc, si nous voulons utiliser l'attribut TextAppearance standard dans une vue personnalisée et lire ses valeurs dans constructeur, nous pouvons modifier le code 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>

Peut-être, je me trompe d'une certaine façon, mais la documentation Android sur obtenirStyledAttributes () est très pauvre.

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 ce sera impossible pour implémenter toutes les fonctionnalités dans overriden onMeasure () et onDraw ().

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

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


26
2018-02-28 14:35



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

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


11
2018-06-29 07:19



Merci beaucoup pour la première réponse.

En ce qui me concerne, 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
2018-05-03 20:00



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 page dans la balise include sont d’autres fichiers de disposition .xml dans le même dossier res.


0
2018-02-04 08:16