Vous l'aurez remarqué, l'apparence de notre RecyclerView pourrait être améliorée ! Il serait bien de pouvoir récupérer la photo d'avatar de chaque utilisateur ainsi que d'implémenter un "Pull To Refresh" permettant aux utilisateurs de pouvoir réactualiser le contenu du RecyclerView.
Eh bien c'est justement ce que nous allons faire immédiatement...
Personnalisez la RecyclerView
Reprenez votre application NetApp (disponible également à ce commit). Pour cela, nous allons modifier le fichier XML représentant chaque ligne de notre RecyclerView. Le but est d'afficher l'avatar de chaque utilisateur, ainsi que l'adresse de son Github.
Layout XML fragment_main_item.xml :
<?xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dip"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:id="@+id/fragment_main_item_image"
android:layout_width="50dip"
android:layout_height="50dip"
android:layout_margin="5dip"
android:id="@+id/fragment_main_item_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="22sp"
android:textStyle="bold"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:id="@+id/fragment_main_item_website"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textStyle="italic"
Explications : Nous avons ici simplement ajouté dans notre XML un second TextView ainsi qu'une ImageView, le tout organisé grâce à des LinearLayout. Rien de bien compliqué !
Comme vous vous en doutez, nous allons maintenant modifier le ViewHolder correspondant, afin de mettre à jour nos nouvelles vues créées (TextView et ImageView).
En revanche, un petit problème risque de se poser : Comment afficher dans notre ImageView, une image distante accessible via une URL ? Eh bien en fait assez facilement, grâce à une superbe librairie : Glide. Cette dernière va effectuer toute seule la récupération de l'image à partir d'une URL en arrière-plan, puis l'afficher dans une ImageView que vous lui aurez spécifiée.
Afin de l'installer, nous allons comme d'habitude utiliser Gradle notre gestionnaire de dépendances.
Extrait de build.gradle :
dependencies {
...
implementation 'com.github.bumptech.glide:glide:4.2.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.2.0'
}
Modifions maintenant le code de notre ViewHolder afin de pouvoir mettre à jour les vues précédemment créées.
Classe GithubUserViewHolder.java :
public class GithubUserViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.fragment_main_item_title) TextView textView;
// 1 - Adding a TextView and an ImageView
@BindView(R.id.fragment_main_item_website) TextView texViewWebsite;
@BindView(R.id.fragment_main_item_image) ImageView imageView;
public GithubUserViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public void updateWithGithubUser(GithubUser githubUser, RequestManager glide){
this.textView.setText(githubUser.getLogin());
// 2 - Update TextView & ImageView
this.texViewWebsite.setText(githubUser.getHtmlUrl());
glide.load(githubUser.getAvatarUrl()).apply(RequestOptions.circleCropTransform()).into(imageView);
}
}
Explications : Nous avons répertorié les deux vues que nous venons d'ajouter en XML (Un TextView et une ImageView). Nous avons également modifié notre méthode updateWithGithubUser()
afin de lui ajouter en paramètre un objet Glide (RequestManager), qui nous permettra de télécharger l'avatar de chaque utilisateur, la transformer en un cercle plus joli, puis l'afficher dans notre ImageView.
Effectuons également la modification liée au passage de notre objet Glide dans notre Adapter ainsi que dans notre contrôleur MainFragment...
Extrait de GithubUserAdapter.java :
public class GithubUserAdapter extends RecyclerView.Adapter<GithubUserViewHolder> {
// 1 - Declaring a Glide object
private RequestManager glide;
// 2 - Updating our constructor adding a Glide Object
public GithubUserAdapter(List<GithubUser> githubUsers, RequestManager glide) {
this.githubUsers = githubUsers;
this.glide = glide;
}
...
@Override
public void onBindViewHolder(GithubUserViewHolder viewHolder, int position) {
// - 3 Passing the Glide object to each ViewHolder
viewHolder.updateWithGithubUser(this.githubUsers.get(position), this.glide);
}
...
}
Explications : Nous modifions notre Adapter afin de lui passer la référence d'une instance de Glide, que nous allons créer par la suite dans notre Fragment. Nous mettrons ensuite à disposition cette référence à chaque ViewHolder.
Modifions maintenant notre fragment MainFragment.
Extrait de MainFragment :
public class MainFragment extends Fragment {
...
// -----------------
// CONFIGURATION
// -----------------
private void configureRecyclerView(){
...
this.adapter
= new GithubUserAdapter(this.githubUsers, Glide.with(this));
...
}
...
}
Explications : Comme vous pouvez le voir, une instance de Glide se créée assez facilement. Cependant cette dernière a besoin d'un contexte (Context) en paramètre afin de parfaitement se synchroniser avec le cycle de vie de votre fragment/activité.
Et voilà ! Exécutons maintenant notre application NetApp. Le design est plutôt réussi non ?

Ajouter un "Pull To Refresh"
La fonctionnalité "Pull To Refresh" est quelque chose de largement utilisé par les utilisateurs d'Android sur énormément d'applications. C'est devenu presque un réflexe pour beaucoup de personnes afin d'actualiser le contenu de leurs applications.
Dans le but de faciliter l'implémentation de cette fonctionnalité, Android a ajouté dans sa librairie de support-v4, la classe SwipeRefreshLayout.
Pour commencer, nous allons d'abord modifier le layout contenant notre RecyclerView.
Layout fragment_main.xml :
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_main_swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment_main_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
Explications : Nous avons ici englobé le RecyclerView dans un SwipeRefreshLayout, qui va nous permettre d'activer plus facilement la fonctionnalité "Pull To Refresh" dans notre RecyclerView.
Puis nous allons modifier notre contrôleur, MainFragment, afin de configurer correctement le SwipeRefreshLayout.
Extrait de MainFragment.java :
public class MainFragment extends Fragment {
...
// 1 - Declare the SwipeRefreshLayout
@BindView(R.id.fragment_main_swipe_container) SwipeRefreshLayout swipeRefreshLayout;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
// 4 - Configure the SwipeRefreshLayout
this.configureSwipeRefreshLayout();
...
}
...
// 2 - Configure the SwipeRefreshLayout
private void configureSwipeRefreshLayout(){
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
executeHttpRequestWithRetrofit();
}
});
}
...
private void updateUI(List<GithubUser> users){
// 3 - Stop refreshing and clear actual list of users
swipeRefreshLayout.setRefreshing(false);
githubUsers.clear();
githubUsers.addAll(users);
adapter.notifyDataSetChanged();
}
}
Explications : Dans ce fragment, nous allons simplement configurer notre SwipeRefreshLayout en lui ajoutant un listener qui exécutera notre stream habituelle qui elle rafraichira nos données.
Ligne 1 : On déclare notre SwipeRefreshLayout.
Ligne 2 : On crée une méthode qui nous permettra de configurer notre SwipeRefreshLayout et surtout de lui ajouter un listener. Ce dernier sera lancé quand l'utilisateur exécutera un "Pull To Refresh" et déclenchera la méthode
onRefresh()
qui elle lancera notre stream habituelle.Ligne 3 : On pense également à arrêter l'animation du SwipeRefreshLayout une fois que notre requête réseau s'est correctement terminée (
setRefreshing(false)
). Puis, on efface à chaque fois complètement la précédente liste d'utilisateurs (githubUsers.clear()
) afin d'éviter de dupliquer celle-ci à cause du.addAll()
.Ligne 4 : Et enfin, on n'oublie pas d'appeler la méthode de configuration du SwipeRefreshLayout dans le
onCreateView()
de notre fragment.
Et voilà ! Lancez maintenant votre application et testez la fonctionnalité "Pull To Refresh". Comment la trouvez-vous ?
