• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 15/04/2019

Définissez une structure de données avec des entités

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Maintenant que Room (notre ORM) est installé, nous allons pouvoir commencer à représenter nos données sous forme de modèles (via des simples classes POJO). Le but sera de modéliser la fonctionnalité "liste de choses à faire" de notre écran TodoListActivity.

Pour y voir un peu plus clair, reprenons cet écran afin de mieux visualiser nos besoins et surtout les modèles que nous devrons créer.

Exemple de modélisation
Exemple de modélisation

Dans notre cas, j'ai choisi de modéliser cette fonctionnalité de la manière suivante :

  • La classe  User  : Représentera l'utilisateur actuellement connecté à notre application. Ce dernier possèdera un nom et une photo de profil.

  • La classe  Item  : Représentera une chose à faire. Elle aura un titre ainsi qu'une catégorie et aura la possibilité d'être marquée comme étant "Faite". Elle gardera également en mémoire l'identifiant de l'utilisateur qui l'aura créé.

Maintenant, créons les classes correspondantes en Java... :) Pour cela, créez un nouveau package appelé models/ puis ajoutez-y les classes User.java et Item.java.

Classe Item.java :

public class Item {
    
    private long id;
    private String text;
    private int category;
    private Boolean isSelected;
    private long userId;

    public Item() { }

    public Item(String text, int category, long userID) {
        this.text = text;
        this.category = category;
        this.userId = userID;
        this.isSelected = false;
    }

    // --- GETTER ---
    public long getId() { return id; }
    public String getText() { return text; }
    public int getCategory() { return category; }
    public Boolean getSelected() { return isSelected; }
    public long getUserId() { return userId; }

    // --- SETTER ---
    public void setId(long id) { this.id = id; }
    public void setText(String text) { this.text = text; }
    public void setCategory(int category) { this.category = category; }
    public void setSelected(Boolean selected) { isSelected = selected; }
    public void setUserId(long userId) { this.userId = userId; }
}

Classe User.java :

public class User {

    private long id;
    private String username;
    private String urlPicture;

    public User(long id, String username, String urlPicture) {
        this.id = id;
        this.username = username;
        this.urlPicture = urlPicture;
    }

    // --- GETTER ---

    public long getId() { return id; }
    public String getUsername() { return username; }
    public String getUrlPicture() { return urlPicture; }

    // --- SETTER ---

    public void setId(long id) { this.id = id; }
    public void setUsername(String username) { this.username = username; }
    public void setUrlPicture(String urlPicture) { this.urlPicture = urlPicture; }
}

Explications : Nous avons créé pour le moment ces deux objets qui semblent assez basiques. Nous leurs avons ajouté à chacun un constructeur et des getters/setters.

Effectivement, ces classes modèles sont assez simples, mais comment faire pour les sauvegarder en base de données ? :euh:

Pour nous aider, j'ai créé dans un premier temps un Modèle Physique de Données (MPD) afin de mieux représenter les relations entre ces objets, qui deviendront par la suite... des tables ! :)

Le modèle physique de données pour notre application SaveMyTrip
Le modèle physique de données pour notre application SaveMyTrip

Nous avons ici deux tables, correspondant à nos deux précédents modèles :

  • La table User

    • Clé primaire (PK) : Le champ id représentant l'identifiant unique pour notre utilisateur.

  • La table  Item :

    • Clé primaire (PK) : Le champ id représentant l'identifiant unique d'une "chose à faire".

    • Clé étrangère (FK) : Le champ userId représentant l'identifiant unique de l'utilisateur qui a créé la "chose à faire".

Maintenant que nous avons une représentation un peu plus avancée de la structure de notre base de données, nous allons modifier nos deux classes de modèles afin de les définir comme étant des tables aux yeux de Room.

Extrait de User.java :

@Entity
public class User {

    @PrimaryKey
    private long id;
    
    ...
}

Explications : Pour notre plus grand plaisir, Room nous propose énormément d'annotations afin de faciliter sa configuration. Ainsi, nous avons défini ici notre classe "User" comme étant une table grâce à l'annotation  @Entity.

Une table devant posséder au moins une clé primaire, nous définissons la propriété id comme étant la clé primaire de la table "User" grâce à l'annotation  @PrimaryKey . Rapide et efficace non ? :)

Extrait de Item.java :

@Entity(foreignKeys = @ForeignKey(entity = User.class,
        parentColumns = "id",
        childColumns = "userId"))

public class Item {

    @PrimaryKey(autoGenerate = true)
    private long id;
    ...
}

Explications : La table "Item" est un peu différente, car nous avons ajouté, à l'intérieur de l'annotation @Entity, la relation clé-étrangère/clé-primaire grâce à l'annotation  @ForeignKey . 

Vous l'avez peut-être remarqué, mais nous avons ajouté le paramètre autoGenerate = true à l'annotation @PrimaryKey : cela permettra à Room de générer automatiquement un identifiant unique pour chaque item sauvegardé... ;)

Une bonne chose de faite ! Maintenant, je vais vous donner simplement le contenu de l'Adapter et du ViewHolder que nous utiliserons par la suite à travers la RecyclerView de l'activité TodoListActivity. Et vous vous en doutez, la RecyclerView contiendra une liste... d'Item ! :D 

Placez ces classes dans le package todolist/.

Classe todolist/ItemViewHolder.java : 

public class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    @BindView(R.id.activity_todo_list_item_text) TextView textView;
    @BindView(R.id.activity_todo_list_item_image) ImageView imageView;
    @BindView(R.id.activity_todo_list_item_remove) ImageButton imageButton;

    // FOR DATA
    private WeakReference<ItemAdapter.Listener> callbackWeakRef;

    public ItemViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }

    public void updateWithItem(Item item, ItemAdapter.Listener callback){
        this.callbackWeakRef = new WeakReference<ItemAdapter.Listener>(callback);
        this.textView.setText(item.getText());
        this.imageButton.setOnClickListener(this);
        switch (item.getCategory()){
            case 0: // TO VISIT
                this.imageView.setBackgroundResource(R.drawable.ic_room_black_24px);
                break;
            case 1: // IDEAS
                this.imageView.setBackgroundResource(R.drawable.ic_lightbulb_outline_black_24px);
                break;
            case 2: // RESTAURANTS
                this.imageView.setBackgroundResource(R.drawable.ic_local_cafe_black_24px);
                break;
        }
        if (item.getSelected()){
            textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
        } else {
            textView.setPaintFlags(textView.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
        }
    }

    @Override
    public void onClick(View view) {
        ItemAdapter.Listener callback = callbackWeakRef.get();
        if (callback != null) callback.onClickDeleteButton(getAdapterPosition());
    }
}

Explications : La classe représentant chaque ligne de la RecyclerView. Je ne l'explique pas plus que ça, car vous devez normalement comprendre assez facilement l'essentiel du code.

Classe todolist/ItemAdapter.java : 

public class ItemAdapter extends RecyclerView.Adapter<ItemViewHolder> {

    // CALLBACK
    public interface Listener { void onClickDeleteButton(int position); }
    private final Listener callback;

    // FOR DATA
    private List<Item> items;

    // CONSTRUCTOR
    public ItemAdapter(Listener callback) {
        this.items = new ArrayList<>();
        this.callback = callback;
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.activity_todo_list_item, parent, false);

        return new ItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ItemViewHolder viewHolder, int position) {
        viewHolder.updateWithItem(this.items.get(position), this.callback);
    }

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

    public Item getItem(int position){
        return this.items.get(position);
    }

    public void updateData(List<Item> items){
        this.items = items;
        this.notifyDataSetChanged();
    }
}

Explications : Cette classe représente l'Adapter faisant le lien entre la RecyclerView et le ViewHolder. Je ne vais également pas plus l'expliquer que ça, car celle-ci reste assez compréhensible en soi. Si vous avez des difficultés à assimiler le fonctionnement du callback et de son interface, n'hésitez pas à relire le chapitre de ce cours.

Maintenant que tout semble prêt, nous allons voir dès le prochain chapitre comment créer nos premières requêtes SQL sur notre base de données SQLite... ;)

Exemple de certificat de réussite
Exemple de certificat de réussite