Introduction aux nouvelles transitions d'activité de Lollipop

Ce que vous allez créer

introduction

L'un des aspects les plus intéressants des spécifications de Material Design est la continuité visuelle entre les activités. Avec seulement quelques lignes de code, les nouvelles API Lollipop vous permettent de faire une transition significative entre deux activités, grâce à des animations transparentes et continues. Cela brise les limites classiques de l'activité des versions précédentes d'Android et permet à l'utilisateur de comprendre comment les éléments vont d'un point à un autre..

Dans ce didacticiel, je vais vous montrer comment atteindre ce résultat, en rendant un exemple d'application conforme aux consignes de Google relatives à la conception de matériaux..

Conditions préalables

Dans ce tutoriel, je suppose que vous êtes déjà familiarisé avec le développement Android et que vous utilisez Android Studio comme IDE. J'utiliserai intensivement les intentions d'Android, en supposant une connaissance de base du cycle de vie de l'activité et du nouveau RecyclerVoir widget introduit avec API 21, Juin dernier. Je ne vais pas me plonger dans les détails de ce cours, mais si cela vous intéresse, vous pouvez trouver une bonne explication dans ce tutoriel Tuts +..

1. Créer la première activité

La structure de base de l'application est simple. Il y a deux activités, une principale, MainActivity.java, dont la tâche est d’afficher une liste d’articles et une seconde, DetailActivity.java, qui affichera les détails de l'élément sélectionné dans la liste précédente.

Étape 1: le RecyclerVoir Widget

Pour afficher la liste des éléments, l’activité principale utilisera le RecyclerVoirwidget introduit dans Android Lollipop. La première chose à faire est d’ajouter la ligne suivante au les dépendances section dans votre projet build.grade fichier pour activer la compatibilité ascendante:

compiler 'com.android.support:recyclerview-v7:+'

Étape 2: Définition des données

Par souci de brièveté, nous ne définirons pas une base de données réelle ni une source de données similaire pour l'application. Au lieu de cela, nous allons utiliser une classe personnalisée, Contact. Chaque élément est associé à un nom, une couleur et des informations de contact de base. C’est ce que la mise en œuvre de la Contact la classe ressemble à:

public class Contact // Les champs associés à la personne privée final String mName, mPhone, mEmail, mCity, mColor; Contact (Nom de chaîne, Couleur de chaîne, Téléphone de chaîne, Email de chaîne, Ville de chaîne) mName = name; mCouleur = couleur; mPhone = téléphone; mEmail = email; mCity = ville;  // Cette méthode permet d'obtenir l'élément associé à un identifiant particulier, // généré de manière unique par la méthode getId définie ci-dessous. Public statique Contact getItem (int id) pour (élément de contact: CONTACTS) if (item.getId () == id) return item;  return null;  // puisque mName et mPhone combinés sont sûrement uniques, // nous n'avons pas besoin d'ajouter un autre champ id public int getId () return mName.hashCode () + mPhone.hashCode ();  énumération statique publique Field NAME, COLOR, PHONE, EMAIL, CITY public String get (Champ f) switch (f) case COLOR: return mColor; TÉLÉPHONE: retour mPhone; EMAIL du cas: renvoyer mEmail; case CITY: retour mCity; case NAME: défaut: return mName; 

Vous vous retrouverez avec un joli conteneur pour les informations qui vous intéressent. Mais nous devons le remplir avec certaines données. Au sommet de la Contactclasse, ajoutez le code suivant pour renseigner le jeu de données.

En définissant les données comme Publique et statique, chaque classe du projet est capable de le lire. En un sens, nous imitons le comportement d’une base de données avec l’exception que nous la codons en dur dans une classe..

public statique final Contact [] CONTACTS = nouveau contact [] nouveau contact ("John", "# 33b5e5", "+01 123456789", "[email protected]", "Venise"), nouveau contact ("Valter" , "# ffbb33", "+01 987654321", "[email protected]", "Bologne"), nouveau contact ("Eadwine", "# ff4444", "+01 123456789", "[email protected]" , "Vérone"), nouveau contact ("Teddy", "# 99cc00", "+01 987654321", "[email protected]", "Rome"), nouveau contact ("Ives", "# 33b5e5", " +01 11235813 "," [email protected] "," Milan "), nouveau contact (" Alajos "," # ffbb33 "," +01 123456789 "," [email protected] "," Bologne "), nouveau Contact ("Gianluca", "# ff4444", "+01 11235813", "[email protected]", "Padova"), nouveau contact ("Fane", "# 99cc00", "+01 987654321", "fane @ exemple.com "," Venise "),;

Étape 3: Définir les principales dispositions

La présentation de l'activité principale est simple, car la liste remplira tout l'écran. La mise en page comprend un Disposition relative comme la racine, mais il peut tout aussi bien être un LinearLayout aussi et RecyclerVoir comme son seul enfant.

  

Parce que le RecyclerVoir widget organise les sous-éléments et rien de plus, vous devez également concevoir la mise en page d'un élément de liste unique. Nous voulons avoir un cercle de couleur à gauche de chaque élément de la liste de contacts. Vous devez donc définir le dessin. cercle.xml.

   

Vous avez maintenant tous les éléments nécessaires pour définir la mise en page de l'élément de liste.

      

Étape 4: Afficher les données à l'aide du RecyclerVoir

Nous sommes presque arrivés à la fin de la première partie du didacticiel. Tu dois encore écrire le RecyclerView.ViewHolder et le RecyclerView.Adapter, et tout affecter à la vue associée dans le onCreate méthode de l'activité principale. Dans ce cas, le RecyclerView.ViewHolder doit également être capable de gérer les clics, il vous faudra donc ajouter une classe spécifique capable de le faire. Commençons à définir la classe responsable de la gestion des clics.

Classe publique RecyclerClickListener implémente RecyclerView.OnItemTouchListener private OnItemClickListener mListener; GestureDetector mGestureDetector; interface publique OnItemClickListener public void onItemClick (Vue de la vue, position int);  public RecyclerClickListener (contexte de contexte, écouteur OnItemClickListener) mListener = listener; mGestureDetector = new GestureDetector (contexte, new GestureDetector.SimpleOnGestureListener () @Override public boolean onSingleTapUp (MotionEvent e) return true;);  @Override public boolean onInterceptTouchEvent (vue RecyclerView, MotionEvent e) View childView = view.findChildViewUnder (e.getX (), e.getY ()); if (childView! = null && mListener! = null && mGestureDetector.onTouchEvent (e)) mListener.onItemClick (childView, view.getChildPosition (childView)); retourne vrai;  return false;  @Override public void onTouchEvent (vue RecyclerView, MotionEvent motionEvent) 

Il est nécessaire de spécifier le RecyclerView.Adapter, que je vais appeler ça Gestionnaire de données. Il est responsable du chargement des données et de leur insertion dans les vues de la liste. Cette classe de gestionnaire de données contiendra également la définition du RecyclerView.ViewHolder.

Classe publique DataManager étend RecyclerView.Adapter public static class RecyclerViewHolder étend RecyclerView.ViewHolder TextView mName, mPhone; Voir mCircle; RecyclerViewHolder (Voir itemView) super (itemView); mName = (TextView) itemView.findViewById (R.id.CONTACT_name); mPhone = (TextView) itemView.findViewById (R.id.CONTACT_phone); mCircle = itemView.findViewById (R.id.CONTACT_circle);  @Override public RecyclerViewHolder onCreateViewHolder (ViewGroup viewGroup, int i) View v = LayoutInflater.from (viewGroup.getContext ()) .flate (R.layout.contact_item, viewGroup, false); renvoyer le nouveau RecyclerViewHolder (v);  @Override public void onBindViewHolder (RecyclerViewHolder viewHolder, int i) // extrait l'élément unique du tableau principal final Contact contact = Contact.CONTACTS [i]; // Définit les valeurs viewHolder.mName.setText (contact.get (Contact.Field.NAME)); viewHolder.mPhone.setText (contact.get (Contact.Field.PHONE)); // Définit la couleur de la forme GradientDrawable bgShape = (GradientDrawable) viewHolder.mCircle.getBackground (); bgShape.setColor (Color.parseColor (contact.get (Contact.Field.COLOR)));  @Override public int getItemCount () return Contact.CONTACTS.length; 

Enfin, ajoutez le code suivant à la onCreateméthode, ci-dessous setContentView. L'activité principale est prête.

RecyclerView rv = (RecyclerView) findViewById (R.id.rv); // référence de présentation LinearLayoutManager llm = new LinearLayoutManager (this); rv.setLayoutManager (llm); rv.setHasFixedSize (true); // pour améliorer les performances rv.setAdapter (new DataManager ()); // le gestionnaire de données est assignateur au rv RV.addOnItemTouchListener (// et le clic est géré nouvel RecyclerClickListener (this, nouveau RecyclerClickListener.OnItemClickListener () @Override public null surItemClick (View view, position int.) // STUB: // Le clic sur l'élément doit être traité));

Voici à quoi ressemble l'application si vous la construisez et l'exécutez..

2. Créez l'activité Détails

Étape 1: la mise en page

La deuxième activité est beaucoup plus simple. Il prend l'ID du contact sélectionné et récupère les informations supplémentaires que la première activité ne montre pas.

Du point de vue de la conception, la présentation de cette activité est essentielle car il s’agit de la partie la plus importante de l’application. Mais pour ce qui concerne le XML, c'est trivial. La mise en page est une série de Affichage instances positionnées de manière agréable, en utilisant Disposition relative et LinearLayout. Voici à quoi ressemble la mise en page:

                    

Étape 2: Envoyer et recevoir l'ID via Intent Extras

Puisque les deux activités sont liées par une intention, vous devez envoyer des informations permettant à la deuxième activité de comprendre lesquelles.contacter vous demandé les détails.

Une option peut être d'utiliser la variable de position comme référence. La position de l'élément dans la liste correspond à la position de l'élément dans le tableau, il n'y a donc rien de mal à utiliser cet entier comme référence unique..

Cela fonctionnerait, mais si vous utilisez cette approche et, pour une raison quelconque, le jeu de données est modifié à l'exécution, la référence ne correspondra pas au contact qui vous intéresse. C'est pourquoi il est préférable d'utiliser un ID.ad hoc. Cette information est la getId méthode définie dans le Contact classe.

Modifier le onItemClick gestionnaire de la liste des éléments comme indiqué ci-dessous.

@Override public void onItemClick (Vue d'affichage, position int) Intention d'intention = nouvelle intention (MainActivity.this, DetailsActivity.class); intent.putExtra (DetailsActivity.ID, Contact.CONTACTS [position] .getId ()); startActivity (intention); 

le DétailsActivité recevra les informations du Intention extras et construire le bon objet en utilisant l’ID comme référence. Ceci est montré dans le bloc de code suivant.

// Avant la version finale publique statique onCreate String ID = "ID"; public Contact mContact;
// Dans onCreate, après la méthode setContentView mContact = Contact.getItem (getIntent (). GetIntExtra (ID, 0));

Juste comme avant dans le onCreateViewHolder méthode du RecylerView, les vues sont initialisées à l'aide du findViewById méthode et peuplé en utilisant Définir le texte. Par exemple, pour configurer le champ de nom, procédez comme suit:

mName = (TextView) findViewById (R.id.DETAILS_name); mName.setText (mContact.get (Contact.Field.NAME));

Le processus est le même pour les autres champs. La deuxième activité est enfin prête.

3. Transitions significatives

Nous sommes enfin arrivés au cœur du didacticiel, en animant les deux activités à l'aide de la nouvelle méthode Lollipop pour la transition à l'aide d'un élément partagé..

Étape 1: Configurez votre projet

La première chose à faire est de modifier votre thème dans le style.xml déposer dans le valeurs-v21 dossier. De cette manière, vous activez les transitions de contenu et définissez l'entrée et la sortie des vues qui ne sont pas partagées entre les deux activités..

 

Veuillez noter que votre projet doit cibler (et donc être compilé avec) au moins Android API 21.

Les animations seront ignoréessur les systèmes sur lesquels Lollipop n'est pas installé. Malheureusement, pour des raisons de performance, le AppCompat la bibliothèque ne fournit pas une compatibilité ascendante complète pour ces animations.

Étape 2: Attribuer le nom de la transition dans les fichiers de mise en page

Une fois que vous avez édité votre style.xml fichier, vous devez signaler la relationentre les deux éléments communs des vues.

Dans notre exemple, les vues partagées sont le champ contenant le nom du contact, celui du numéro de téléphone et le cercle de couleur. Pour chacun d’eux, vous devez spécifier un nom de transition commun. Pour cette raison, commencez à ajouter dans le strings.xml resource file les éléments suivants:

transition: NOM transition: CERCLE transition: téléphone 

Ensuite, pour chacune des trois paires, dans les fichiers de mise en page, ajoutez le android: transitionName attribut avec la valeur correspondante. Pour le cercle de couleur, le code ressemble à ceci:

 
 

Grâce à cet attribut, Android saura quelles vues sont partagées entre les deux activités et animera correctement la transition. Répétez le même processus pour les deux autres vues.

Étape 3: Configurez l'intention

Du point de vue du codage, vous devrez joindre un code spécifique ActivitéOptions bundle à l'intention. La méthode dont vous avez besoin est makeSceneTransitionAnimation, qui prend comme paramètres le contexte de l’application et autant d’éléments partagés que nous avons besoin. dans le onItemClick méthode du RecyclerVoir, éditer le précédemment défini Intention comme ça:

@Override public void onItemClick (Vue d'affichage, position int) Intention d'intention = nouvelle intention (MainActivity.this, DetailsActivity.class); intent.putExtra (DetailsActivity.ID, Contact.CONTACTS [position] .getId ()); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation (// le contexte de l'activité MainActivity.this, //, pour chaque élément partagé, ajoutez à cette méthode un nouvel élément Pair, // contenant la référence de la vue que nous sommes en train de passer * à partir de *, // et la valeur de l'attribut transitionName new Pair(view.findViewById (R.id.CONTACT_circle), getString (R.string.transition_name_circle)), nouvelle paire(view.findViewById (R.id.CONTACT_name), getString (R.string.transition_name_name)), nouvelle paire(view.findViewById (R.id.CONTACT_phone), getString (R.string.transition_name_phone))); ActivityCompat.startActivity (MainActivity.this, intent, options.toBundle ()); 

Pour chaque élément partagé à animer, vous devrez ajouter à la makeSceneTransitionAnimation méthode un nouveau Paire article. Chaque Paire a deux valeurs, la première est une référence à la vue en cours de transition de, le second est le valeur du nom de transition attribut.

Faites attention lorsque vous importez le Paire classe. Vous devrez inclure le android.support.v4.util paquet, ne pas la android.util paquet. En outre, n'oubliez pas d'utiliser ActivityCompat.startActivity méthode au lieu de startActivity méthode, sinon vous ne pourrez pas exécuter votre application sur des environnements avec une API inférieure à 16.

C'est tout. Vous avez terminé. C'est aussi simple que ça.

Conclusion

Dans ce didacticiel, vous avez appris à faire la transition entre deux activités partageant un ou plusieurs éléments communs, de manière transparente et transparente, pour une continuité visuellement agréable et significative..

Vous avez commencé par créer la première des deux activités, dont le rôle est d’afficher la liste des contacts. Vous avez ensuite terminé la deuxième activité en concevant sa mise en page et en mettant en œuvre un moyen de passer une référence unique entre les deux activités. Enfin, vous avez examiné la manière dont makeSceneTransitionAnimation fonctionne, grâce au XML nom de transition attribut.

Astuce Bonus: Détails stylistiques

Pour créer une véritable application ressemblant à Material Design, comme illustré dans les captures d'écran précédentes, vous devez également modifier les couleurs de votre thème. Modifiez votre thème de base dans le valeurs-v21 dossier pour obtenir un bon résultat.