Créer des vues personnalisées

Dans ce tutoriel, je vais vous montrer comment créez vos propres vues, utilisables facilement dans un layout XML.
Vous avez surement déjà eu l’occasion d’utiliser des Custom Views, c’est à dire des widget qui ne sont pas disponibles de base dans le SDK android. On peux les repérer facilement par le fait qu’elles sont précédées du nom de package.

Par exemple :

android.support.v7.widget.RecyclerView

ou encore

com.github.florent37.materialviewpager.MaterialViewPager

Ces vues sont facilement insérables dans un fichier layout et possèdent souvent des attributs personnalisés.

Exemple:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.github.florent37.materialviewpager.MaterialViewPager
        android:id="@+id/materialViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:viewpager_color="@color/colorPrimary"
        />

</FrameLayout>

Où le app:viewpager_color n’est pas un attribut commun à toutes les classes, comme android:layout_height, mais bien propre au MaterialViewPager . Les attributs personnalisés ne sont pas précédé d’android:.

Regardons ensemble comment créer notre propre vue, je prendrai comme exemple simple une vue nommée HelloWordView, affichant la phrase Hello World, en ayant 2 couleurs paramétrable depuis les attributs.

HelloWorldView

Premièrement nous allons créer un objet Java HelloWorldView, qui va hériter de FrameLayout

public class HelloWorldView extends FrameLayout {

    public HelloWorldView(Context context) {
        super(context);
    }

    public HelloWorldView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public HelloWorldView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        handleAttributes(context, attrs);
    }
}

Puis nous allons créer un layout layout/hello_world.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="5sp">

    <TextView
        android:id="@+id/hello"
        android:textSize="25sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <TextView
        android:id="@+id/world"
        android:layout_marginLeft="5sp"
        android:textSize="30sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/world" />

</LinearLayout>

Ce qui donne dans notre preview :Capture d’écran 2015-06-27 à 21.01.33

J’ai décrit dans ce layout définit le contenu de ma vue HelloWorldView.

Il suffit maintenant de l’inflater afin qu’elle soit insérée dans notre Custom View :

public class HelloWorldView extends FrameLayout {
    ...

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        //à l'ajout d'un HelloWorldView, je demande de rajouter dans celui-ci le contenu de layout/hello_world.xml
        View helloWorld = LayoutInflater.from(getContext())
                .inflate(R.layout.hello_world, this, false);
        addView(helloWorld);

        //je récupère les TextView de layout/hello_world.xml
        TextView hello = (TextView) findViewById(R.id.hello);
        TextView world = (TextView) findViewById(R.id.world);
    }
}

onFinishInflate est appelé après la création de notre CustomView, c’est donc ici que nous allons inflater notre hello_world.

Attributs

Regardons maintenant comment ajouter nos propres attributs à cette vue

Premièrement, il faut les déclarer dans un fichier nommé attrs.xml

res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="HelloWorldView">
        <attr name="helloColor" format="color"/>
        <attr name="worldColor" format="color"/>
    </declare-styleable>
</resources>

Le fichier attrs.xml est à créer dans votre repertoire res/values.

Comme vous le voyez, il faut déclarer notre vue HelloWorldView en tant que stylable, avec la balise declare-styleable name=”HelloWorldView”, puis indiquer quels attributs on veux ajouter. Ici j’ai ajouté deux couleurs, helloColor et worldColor.

Il suffit maintenant de les récupérer depuis notre fichier Java

public class HelloWorldView extends FrameLayout {

    //on stock la couleur du hello défini en attribut
    private int helloColor;
    //on stock la couleur du world défini en attribut
    private int worldColor;

    //récupère les valeurs affectées en attribut
    private void handleAttributes(Context context, AttributeSet attrs) {
        try {
            TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.HelloWorldView);

            helloColor = styledAttrs.getColor(R.styleable.HelloWorldView_helloColor, Color.BLACK);
            worldColor = styledAttrs.getColor(R.styleable.HelloWorldView_worldColor, Color.BLACK);

            styledAttrs.recycle();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public HelloWorldView(Context context) {
        super(context);
    }

    public HelloWorldView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //je récupère les attributs
        handleAttributes(context, attrs);
    }

    public HelloWorldView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        handleAttributes(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        //à l'ajout d'un HelloWorldView, je demande de rajouter dans celui-ci le contenu de layout/hello_world.xml
        View helloWorld = LayoutInflater.from(getContext())
                .inflate(R.layout.hello_world, this, false);
        addView(helloWorld);

        //je récupère les TextView de layout/hello_world.xml
        TextView hello = (TextView) findViewById(R.id.hello);
        TextView world = (TextView) findViewById(R.id.world);

        //puis je change leurs couleurs
        hello.setTextColor(helloColor);
        world.setTextColor(worldColor);
    }
}

Je récupère ici les valeurs que l’utilisateur a utilisé avec nos attributs personnalisés, puis modifie la couleur des textview dans le onFinishInflate.

Ce qui donne, lorsque l’on écrit :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">

    <com.tutosandroidfrance.customview.HelloWorldView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:helloColor="#A00"
        app:worldColor="#0A0"
        />

    <com.tutosandroidfrance.customview.HelloWorldView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:helloColor="@android:color/darker_gray"
        app:worldColor="@android:color/holo_blue_dark"
        />

</LinearLayout>

La vue suivante

Capture d’écran 2015-06-27 à 21.05.19

Vous pouvez retrouver les sources de ce tutorial sur github

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *