• Non classé
  • 21

Material Design : RecyclerView et CardView

Lors de la Google IO 2014, la firme a introduit un nouveau design, nommé Material Design. Ce dernier a pour but de standardiser le design des applications, en proposant une ergonomie adaptée et un look qui déchire 😀

Cette design est la base de leur nouvelle version d’Android : Lollipop

material

 

Voyons ensemble comment l’implémenter !

cards

 

Le principal atout du material design est l’ajout d’une profondeur dans l’application. Les vues sont donc soumises à un 3ème index (z-index en css), nommé ici l’élévation. Parmi les nouvelles vues sont disponibles les CardsView (d’où le nom : afficher des cards/cartes, comme ci dessus) et le RecyclerView, venant succéder aux ListView/GridView.

elevation

Ces nouvelles vues sont disponibles dans la librairie de support v7 d’android, il vous faut donc commencer par importer dans votre fichier gralde :

build.gralde

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.1.1'
    compile 'com.android.support:recyclerview-v7:22.1.1'
    compile 'com.android.support:cardview-v7:22.1.1'
}

CardView

Une CardView s’utilise simplement comme n’importe quelle vue, depuis nos layout xml :

Petit  détail, la CardView agit comme une FrameView, c’est à dire qu’elle n’influence pas la disposition de ses sous-vues, si vous souhaitez les ordonnez, ajoutez-y un LinearLayout ou un RelativeLayout.

cell_cards.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"

    app:cardBackgroundColor="@android:color/white"
    app:cardCornerRadius="2dp"
    app:cardElevation="2dp">

    <!-- Les CardView possèdent des attributs supplémentaires dont
         - cardBackgroundColor
         - cardElevation pour l'élévation (donc aussi l'ombre)
         - cardCornerRadius pour arrondir les angles
     -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <!-- Les CardView agissent comme des FrameLayout,
         pour avoir une organisation verticale nous devons
         donc rajouter un LinearLayout -->

        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:scaleType="centerCrop"
            tools:src="@drawable/parisguidetower" />

        <TextView
            android:id="@+id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?android:selectableItemBackground"
            android:padding="20dp"
            tools:text="Paris"
            android:fontFamily="sans-serif"
            android:textColor="#333"
            android:textSize="18sp" />
    </LinearLayout>

</android.support.v7.widget.CardView>

Voici ce qui devrait apparaître dans votre preview :

Capture d’écran 2015-05-01 à 15.06.14

RecyclerView

Une RecyclerView est une nouvelle façon d’afficher une liste ou une grille de vues.

Elle peux s’apparenter à une ListView mais permet beaucoup plus de personnalisation.

Comme toutes vues, il faut la déclarer dans nos layout xml :

activity_main.xml

<android.support.v7.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

A la même façon que les ListView, il faut créer un Adapter, mais cette fois ci il doit étendre RecyclerView.Adapter<>. Cet adapter est typé pour un fonctionner avec une certaine classe de ViewHolder (objet qui va garder les références vers les vues de chaque cellule). Dans notre cas il va donc falloir créer un ViewHolder, nous le nommerons MyViewHolder :

public class MyViewHolder extends RecyclerView.ViewHolder{

    private TextView textViewView;
    private ImageView imageView;

    //itemView est la vue correspondante à 1 cellule
    public MyViewHolder(View itemView) {
        super(itemView);

        //c'est ici que l'on fait nos findView

        textViewView = (TextView) itemView.findViewById(R.id.text);
        imageView = (ImageView) itemView.findViewById(R.id.image);
    }

    //puis ajouter une fonction pour remplir la cellule en fonction d'un MyObject
    public void bind(MyObject myObject){
        textViewView.setText(myObject.getText());
        Picasso.with(imageView.getContext()).load(myObject.getImageUrl()).centerCrop().fit().into(imageView);
    }
}

Notre adapter va donc être du type

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>

Il possède relativement les mêmes fonctions qu’un ArrayAdapter, à la différence que la fonction onCreateView est divisée en 2 appels : onCreateViewHolder et onBindViewHolder

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

    List<MyObject> list;

    //ajouter un constructeur prenant en entrée une liste
    public MyAdapter(List<MyObject> list) {
        this.list = list;
    }

    //cette fonction permet de créer les viewHolder
    //et par la même indiquer la vue à inflater (à partir des layout xml)
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int itemType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_cards,viewGroup,false);
        return new MyViewHolder(view);
    }

    //c'est ici que nous allons remplir notre cellule avec le texte/image de chaque MyObjects
    @Override
    public void onBindViewHolder(MyViewHolder myViewHolder, int position) {
        MyObject myObject = list.get(position);
        myViewHolder.bind(myObject);
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

}

MyObject.java

public class MyObject {
    private String text;
    private String imageUrl;

    public MyObject(String text, String imageUrl) {
        this.text = text;
        this.imageUrl = imageUrl;
    }

    //getters & setters
}

Utilisons le maintenant dans notre Activity
Une étape importante est d’affecter un LayoutManager à notre activity, sans quoi une erreur sera levée
RecyclerView﹕ No layout manager attached; skipping layout

setLayoutManager(new LinearLayoutManager(this));

public class MainActivity extends ActionBarActivity {

    private RecyclerView recyclerView;

    private List<MyObject> cities = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //remplir la ville
        ajouterVilles();

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

        //définit l'agencement des cellules, ici de façon verticale, comme une ListView
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        //pour adapter en grille comme une RecyclerView, avec 2 cellules par ligne
        //recyclerView.setLayoutManager(new GridLayoutManager(this,2));

        //puis créer un MyAdapter, lui fournir notre liste de villes.
        //cet adapter servira à remplir notre recyclerview
        recyclerView.setAdapter(new MyAdapter(cities));
    }

    private void ajouterVilles() {
        cities.add(new MyObject("France","http://www.telegraph.co.uk/travel/destination/article130148.ece/ALTERNATES/w620/parisguidetower.jpg"));
        cities.add(new MyObject("Angleterre","http://www.traditours.com/images/Photos%20Angleterre/ForumLondonBridge.jpg"));
        cities.add(new MyObject("Allemagne","http://tanned-allemagne.com/wp-content/uploads/2012/10/pano_rathaus_1280.jpg"));
        cities.add(new MyObject("Espagne","http://www.sejour-linguistique-lec.fr/wp-content/uploads/espagne-02.jpg"));
        cities.add(new MyObject("Italie","http://retouralinnocence.com/wp-content/uploads/2013/05/Hotel-en-Italie-pour-les-Vacances2.jpg"));
        cities.add(new MyObject("Russie","http://www.choisir-ma-destination.com/uploads/_large_russie-moscou2.jpg"));
    }

}

Compilez et admirez le résultat :

cardsview_sample_1

 

 

Afficher nos cards en grille

L’objet RecyclerView permet aussi d’afficher les éléments sous forme de grille au lieu de liste, en une simple modification du LayoutManager (objet qui définit l’organisation des vues dans la RecyclerView). Il suffit de le passer à une GridViewManager(context,nombreCellulesParLigne)

recyclerView.setLayoutManager(new GridLayoutManager(this,2));

 

device-2015-05-01-201313

 

 

 

Les sources de ce tuto sont disponibles à l’adresse suivante : https://github.com/florent37/TutosAndroidFrance/tree/master/RecyclerViewSample

 

Si vous aussi vous aimez le material design, je vous invite à regardez la documentation officielle de material design, elle est très bien faite et plutôt détaillée

Et pour l’inspiration venez découvrir MaterialUp, comment ne pas aimer ce site 😀

Capture d’écran 2015-05-01 à 15.18.00

Vous aimerez aussi...

21 réponses

  1. Vuffray dit :

    Salut, je suis débutant en android et je voudrais juste savoir quelles sont les getters et setters. parce que j’ai une erreur avec ceux que j’ai fait moi même.
    Merci de ta reponse. Sinon très bon tuto ;D

  2. Vuffray dit :

    Je suis désolé de revenir encore une fois. mais j’ai un problème avec l’Adapter.

    public class MyAdapter extends RecyclerView.Adapter { //une erreur à cette ligne

    List list;

    //ajouter un constructeur prenant en entrée une liste
    public MyAdapter(List list) {
    this.list = list;
    }

    //cette fonction permet de créer les viewHolder
    //et par la même indiquer la vue à inflater (à partir des layout xml)
    @Override //une ici
    public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int itemType) {
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_cards,viewGroup,false);
    return new MyViewHolder(view); //une autre ici
    }

    //c’est ici que nous allons remplir notre cellule avec le texte/image de chaque MyObjects
    @Override
    public void onBindViewHolder(MyViewHolder myViewHolder, int position) {
    MyObject myObject = list.get(position);
    myViewHolder.bind(myObject);
    }

    @Override
    public int getItemCount() {
    return list.size();
    }

    }

    J’ai bien copié le java du MyViewHolder et MyObject, je sais pas si quelqu’un peu m’aider ?

  3. Vuffray dit :

    08 10:41:42.566 22513 22513 E AndroidRuntime: Process: com.test.urgencecall, PID: 22513

    05-08 10:41:42.566 22513 22513 E AndroidRuntime: at com.test.urgencecall.MyViewHolder.(MyViewHolder.java:24)

    05-08 10:41:42.566 22513 22513 E AndroidRuntime: at com.test.urgencecall.MyAdapter.onCreateViewHolder(MyAdapter.java:27)

    05-08 10:41:42.566 22513 22513 E AndroidRuntime: at com.test.urgencecall.MyAdapter.onCreateViewHolder(MyAdapter.java:13)

    voilà, je sais pas si ça va t’aider …

    • Florent Champigny dit :

      envoie moi le log complet s’il te plait, il indique les lignes mais pas quelle erreur est remontée. Il serait bien de savoir si c’est un NullPointerException ou autre 🙂

      • Vuffray dit :

        https://www.dropbox.com/s/1q1u4tduq3koj44/UrgenceCall.zip?dl=0 ici tu pourras trouver tout mon code.

        ici il y a le LogCat : —- 8 mai 2015 10:54:58 —-

        05-08 10:54:51.352 2176 2176 I Timeline: Timeline: Activity_launch_request id:com.test.urgencecall time:104195225

        05-08 10:54:51.353 809 1687 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.test.urgencecall/.MainActivity bnds=[224,267][435,537] (has extras)} from uid 10032 on display 0

        05-08 10:54:51.437 809 1356 I ActivityManager: Start proc com.test.urgencecall for activity com.test.urgencecall/.MainActivity: pid=25174 uid=10200 gids={50200, 9997, 3003, 1028, 1015} abi=armeabi-v7a

        05-08 10:54:51.831 25196 25196 I dex2oat : /system/bin/dex2oat –runtime-arg -classpath –runtime-arg –instruction-set=arm –instruction-set-features=div –runtime-arg -Xrelocate –boot-image=/system/framework/boot.art –dex-file=/data/data/com.test.urgencecall/cache/ads926679025.jar –oat-fd=24 –oat-location=/data/data/com.test.urgencecall/cache/ads926679025.dex –runtime-arg -Xms64m –runtime-arg -Xmx512m

        05-08 10:54:52.146 25174 25174 E AndroidRuntime: Process: com.test.urgencecall, PID: 25174

        05-08 10:54:52.146 25174 25174 E AndroidRuntime: at com.test.urgencecall.MyViewHolder.(MyViewHolder.java:24)

        05-08 10:54:52.146 25174 25174 E AndroidRuntime: at com.test.urgencecall.MyAdapter.onCreateViewHolder(MyAdapter.java:27)

        05-08 10:54:52.146 25174 25174 E AndroidRuntime: at com.test.urgencecall.MyAdapter.onCreateViewHolder(MyAdapter.java:13)

        05-08 10:54:52.148 809 1534 W ActivityManager: Force finishing activity com.test.urgencecall/.MainActivity

        05-08 10:54:52.451 809 1534 I WindowManager: Screenshot max retries 4 of Token{37a8c4f ActivityRecord{31b486ae u0 com.test.urgencecall/.MainActivity t399 f}} appWin=Window{1688431a u0 com.test.urgencecall/com.test.urgencecall.MainActivity} drawState=1

        05-08 10:54:52.953 809 829 W ActivityManager: Activity pause timeout for ActivityRecord{31b486ae u0 com.test.urgencecall/.MainActivity t399 f}

        05-08 10:54:53.806 809 1196 I WindowState: WIN DEATH: Window{1688431a u0 com.test.urgencecall/com.test.urgencecall.MainActivity}

        05-08 10:54:53.857 809 1709 I ActivityManager: Process com.test.urgencecall (pid 25174) has died

        —- 8 mai 2015 10:54:58 —-

        • Florent Champigny dit :

          enfin j’ai trouvé !
          c’est un problème de ClassCast. Dans ton code tu utilise des com.rey.material.widget.TextView, et dans ta vue tu as des TextView Android

          retire simplement
          import com.rey.material.widget.TextView;

          de ton ViewHolder 🙂

  4. Vuffray dit :

    J’ai encore une question, ce serait possible de changer d’activité avec un OnItemClickeListener et un switch ?

    • Florent Champigny dit :

      Le onitemclick n’existe plus, mais vous pouvez ajouter un clickListener sur les cellules, faire remonter l’évènement au fragment ou à l’activity pour lancer la seconde

  5. JAccess dit :

    bnsr.. j ai un probleme avec ma cardview qui peut m aider stp car je suis encor debutant en Android… merci d avance

    • Kevin De Jesus Ferreira dit :

      Bonsoir,

      Quel est votre soucis avec votre cardview, il nous faut plus de détail pour pouvoir vous aider.

  6. JAccess dit :

    j veux mettrw une action sur une carte view pour qu il m ouvre un autr activite en cliquant

  7. JAccess dit :

    j peux vous envoyer mes lignes d code svp aidez moi suis tt a fait bloqué

    • Kevin De Jesus Ferreira dit :

      Oui n’hésitez pas à publier ici, votre code et de nous dire exactement ce qu’il ne va pas dedans, ou ce que vous n’arrivez pas à faire avec.

  1. juin 22, 2015

    […] ce n’est pas encore fait, je vous invite à suivre mon tutoriel d’introduction aux RecyclerView et CardsView, qui introduit par la même le Material […]

  2. février 12, 2016

    […] Material Design : RecyclerView et CardView | Tutos Android France. Lors de la Google IO 2014, la firme a introduit un nouveau design, nommé Material Design. Ce dernier a pour but de standardiser le design des applications, en proposant une ergonomie adaptée et un look qui déchire Cette design est la base de leur nouvelle version d’Android : Lollipop Voyons ensemble comment l’implémenter ! Le principal atout du material design est l’ajout d’une profondeur dans l’application. Les vues sont donc soumises à un 3ème index (z-index en css), nommé ici l’élévation. Ces nouvelles vues sont disponibles dans la librairie de support v7 d’android, il vous faut donc commencer par importer dans votre fichier gralde : build.gralde CardView Une CardView s’utilise simplement comme n’importe quelle vue, depuis nos layout xml : Petit détail, la CardView agit comme une FrameView, c’est à dire qu’elle n’influence pas la disposition de ses sous-vues, si vous souhaitez les ordonnez, ajoutez-y un LinearLayout ou un RelativeLayout. cell_cards.xml RecyclerView activity_main.xml. […]

Laisser un commentaire

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