Introductie van de nieuwe Lollipop-activiteitsovergangen

Wat je gaat creëren

Invoering

Een van de meest interessante aspecten van de Material Design-specificaties is de visuele continuïteit tussen activiteiten. Met slechts enkele coderegels kunt u met de nieuwe Lollipop-API's zinvol schakelen tussen twee activiteiten, dankzij naadloze en doorlopende animaties. Hiermee worden de klassieke activiteitsgrenzen van de vorige Android-versies verbroken en kan de gebruiker begrijpen hoe elementen van het ene punt naar het andere gaan.

In deze zelfstudie laat ik u zien hoe u dit resultaat behaalt door een voorbeeldtoepassing te maken die overeenkomt met de Material Design-richtlijnen van Google.

voorwaarden

In deze zelfstudie ga ik ervan uit dat u al bekend bent met Android-ontwikkeling en dat u Android Studio als uw IDE gebruikt. Ik zal Android-intenties uitgebreid gebruiken, uitgaande van een basiskennis van de levenscyclus van de activiteit en de nieuwe RecyclerView widget geïntroduceerd met API 21, afgelopen juni. Ik ga niet in op de details van deze les, maar als je geïnteresseerd bent, kun je een geweldige uitleg vinden in deze Tutorial + tutorial.

1. Maak de eerste activiteit

De basisstructuur van de applicatie is eenvoudig. Er zijn twee activiteiten, een hoofdactiviteit, MainActivity.java, wiens taak het is om een ​​lijst met items weer te geven, en een tweede, DetailActivity.java, die de details van het item toont dat in de vorige lijst is geselecteerd.

Stap 1: De RecyclerView widget

Om de lijst met items weer te geven, gebruikt de hoofdactiviteit de RecyclerViewwidget geïntroduceerd in Android Lollipop. Het eerste dat u moet doen, is de volgende regel toevoegen aan de afhankelijkheden sectie in uw project build.grade bestand om achterwaartse compatibiliteit in te schakelen:

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

Stap 2: Gegevensdefinitie

Kortheidshalve zullen we geen echte database of een vergelijkbare gegevensbron voor de applicatie definiëren. In plaats daarvan zullen we een aangepaste klasse gebruiken, Contact. Aan elk item is een naam, een kleur en basiscontactinformatie gekoppeld. Dit is wat de implementatie van de Contact klasse ziet eruit als:

public class Contact // De velden die zijn gekoppeld aan de persoon private final String mName, mPhone, mEmail, mCity, mColor; Contact (tekenreeksnaam, tekenreekskleur, String-telefoon, Tekenreeks-e-mail, Stringstad) mName = name; mColor = kleur; mPhone = telefoon; mEmail = e-mail; mCity = stad;  // Met deze methode kan het item worden gekoppeld aan een bepaalde id, // uniek gegenereerd door de methode getId gedefinieerd onder public static Contact getItem (int id) for (Contactitem: CONTACTS) if (item.getId () == id) item retourneren;  return null;  // sinds mName en mPhone gecombineerd zeker uniek zijn, // hoeven we geen ander id-veld toe te voegen openbaar int getId () return mName.hashCode () + mPhone.hashCode ();  public static enum Veld NAME, COLOR, PHONE, EMAIL, CITY public String get (Veld f) switch (f) hoofdletter KLEUR: return mColor; case PHONE: return mPhone; case EMAIL: return mEmail; case CITY: return mCity; case NAME: default: return mName; 

Je krijgt een leuke container voor de informatie waar je om geeft. Maar we moeten het met wat gegevens vullen. Aan de top van de Contactclass, voeg het volgende stuk code toe om de dataset te vullen.

Door de gegevens te definiëren als openbaar en statisch, elke klas in het project kan het lezen. In zekere zin bootsen we het gedrag van een database na met de uitzondering dat we het hardcoderen naar een klasse.

public static final Contact [] CONTACTS = nieuw Contact [] new Contact ("John", "# 33b5e5", "+01 123456789", "[email protected]", "Venice"), nieuw Contact ("Valter" , "# ffbb33", "+01 987654321", "[email protected]", "Bologna"), nieuw contact ("Eadwine", "# ff4444", "+01 123456789", "[email protected]" , "Verona"), nieuw contact ("Teddy", "# 99cc00", "+01 987654321", "[email protected]", "Rome"), nieuw contact ("Ives", "# 33b5e5", " +01 11235813 "," [email protected] "," Milan "), nieuw Contact (" Alajos "," # ffbb33 "," +01 123456789 "," [email protected] "," Bologna "), nieuw Contact ("Gianluca", "# ff4444", "+01 11235813", "[email protected]", "Padova"), nieuwe contactpersoon ("Fane", "# 99cc00", "+01 987654321", "fane @ example.com "," Venice "),;

Stap 3: De hoofdlay-outs definiëren

De lay-out van de hoofdactiviteit is eenvoudig, omdat de lijst het volledige scherm zal vullen. De lay-out omvat een Relatieve layout als de wortel, maar het kan net zo goed een zijn LinearLayout ook - en a RecyclerView als zijn enige kind.

  

Omdat het RecyclerView widget regelt subelementen en niets meer, je moet ook de lay-out van een enkel lijstitem ontwerpen. We willen een gekleurde cirkel links van elk item van de contactenlijst zodat u eerst het tekenbare moet definiëren circle.xml.

   

U hebt nu alle elementen die nodig zijn om de lay-out van het lijstitem te definiëren.

      

Stap 4: Toon de gegevens met behulp van de RecyclerView

We zijn bijna aan het einde van het eerste deel van de tutorial aangekomen. Je moet nog steeds het RecyclerView.ViewHolder en de RecyclerView.Adapter, en alles toewijzen aan de bijbehorende weergave in de onCreate methode van de hoofdactiviteit. In dit geval, de RecyclerView.ViewHolder moet ook klikken kunnen verwerken, dus u moet een specifieke klasse toevoegen die hiervoor in staat is. Laten we beginnen met het definiëren van de klasse die verantwoordelijk is voor de afhandeling van klikken.

public class RecyclerClickListener implementeert RecyclerView.OnItemTouchListener private OnItemClickListener mListener; GestureDetector mGestureDetector; openbare interface OnItemClickListener public void onItemClick (View view, int position);  public RecyclerClickListener (Context-context, OnItemClickListener-listener) mListener = listener; mGestureDetector = nieuwe GestureDetector (context, nieuwe GestureDetector.SimpleOnGestureListener () @Override public boolean onSingleTapUp (MotionEvent e) return true;);  @Override public boolean onInterceptTouchEvent (RecyclerView-weergave, MotionEvent e) View childView = view.findChildViewUnder (e.getX (), e.getY ()); if (childView! = null && mListener! = null && mGestureDetector.onTouchEvent (e)) mListener.onItemClick (childView, view.getChildPosition (childView)); geef waar terug;  return false;  @Override public void onTouchEvent (RecyclerView-weergave, MotionEvent motionEvent) 

Het is noodzakelijk om de RecyclerView.Adapter, welke ik het zal noemen Gegevens manager. Het is verantwoordelijk voor het laden van de gegevens en invoegen in de weergaven van de lijst. Deze gegevensmanagerklasse bevat ook de definitie van de RecyclerView.ViewHolder.

public class DataManager breidt RecyclerView.Adapter uit public static class RecyclerViewHolder breidt RecyclerView.ViewHold uit TextView mName, mPhone; Bekijk mCircle; RecyclerViewHolder (View 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 ()). Inflate (R.layout.contact_item, viewGroup, false); retourneer nieuwe RecyclerViewHolder (v);  @Override public void onBindViewHolder (RecyclerViewHolder viewHolder, int i) // haal het enkele element uit de hoofdarray definitief Contact contact = Contact.CONTACTS [i]; // Stel de waarden viewHolder.mName.setText (contact.get (Contact.Field.NAME)) in; viewHolder.mPhone.setText (contact.get (Contact.Field.PHONE)); // Stel de kleur van de vorm in GradientDrawable bgShape = (GradientDrawable) viewHolder.mCircle.getBackground (); bgShape.setColor (Color.parseColor (contact.get (Contact.Field.COLOR)));  @Override public int getItemCount () return Contact.CONTACTS.length; 

Voeg ten slotte de volgende code toe aan de onCreatemethode, hieronder setContentView. De hoofdactiviteit is klaar.

RecyclerView rv = (RecyclerView) findViewById (R.id.rv); // layout reference LinearLayoutManager llm = new LinearLayoutManager (this); rv.setLayoutManager (LLM); rv.setHasFixedSize (true); // om de prestaties rv.setAdapter (nieuwe DataManager ()) te verbeteren; // de gegevensmanager is toewijzer aan de RV rv.addOnItemTouchListener (// en de klik is behandeld met nieuwe RecyclerClickListener (dit, nieuwe RecyclerClickListener.OnItemClickListener () @Override public void onItemClick (View view, int position) // STUB: // De klik op het item moet worden behandeld));

Dit is hoe de applicatie eruit ziet als je hem bouwt en uitvoert.

2. Maak de detailsactiviteit aan

Stap 1: De lay-out

De tweede activiteit is veel eenvoudiger. Het neemt de ID van de geselecteerde contactpersoon en haalt de aanvullende informatie op die de eerste activiteit niet toont.

Vanuit ontwerpoogpunt is de lay-out van deze activiteit van cruciaal belang, omdat dit het belangrijkste onderdeel van de toepassing is. Maar voor wat betreft de XML, het is triviaal. De lay-out is een reeks van Tekstweergave instanties geplaatst op een prettige manier, met behulp van Relatieve layout en LinearLayout. Zo ziet de lay-out eruit:

                    

Stap 2: Verstuur en ontvang de ID via Intent Extras

Aangezien de twee activiteiten met een intentie zijn gekoppeld, moet u een stuk informatie verzenden waarmee de tweede activiteit kan begrijpenneem contact op met u om de details op te vragen.

Een optie kan de positievariabele als referentie gebruiken. De positie van het element in de lijst komt overeen met de positie van het element in de array, dus er mag niets slecht zijn aan het gebruik van dit gehele getal als een unieke referentie.

Dit zou werken, maar als u deze aanpak volgt en, om welke reden dan ook, de dataset tijdens runtime wordt gewijzigd, zal de referentie niet overeenkomen met de contactpersoon waarin u bent geïnteresseerd. Dit is de reden waarom het beter is om een ​​ID te gebruikenAD hoc. Deze informatie is de getId methode gedefinieerd in de Contact klasse.

Bewerk de onItemClick handler van de lijst met items, zoals hieronder weergegeven.

@Override public void onItemClick (View view, int position) Intent intent = new Intent (MainActivity.this, DetailsActivity.class); intent.putExtra (DetailsActivity.ID, Contact.CONTACTS [positie] .getId ()); startActivity (intent); 

De DetailsActivity ontvangt de informatie van de voornemen extra's en construeer het juiste object met de ID als referentie. Dit wordt getoond in het volgende codeblok.

// Voor de onCreate openbare definitieve statische String ID = "ID"; publiek contact mContact;
// In de onCreate, na de methode setContentView mContact = Contact.getItem (getIntent (). GetIntExtra (ID, 0));

Net zoals eerder in de onCreateViewHolder methode van de RecylerView, de views worden geïnitialiseerd met behulp van de findViewById methode en ingevuld met setText. Om bijvoorbeeld het naamveld te configureren, doen we het volgende:

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

Het proces is hetzelfde voor de andere velden. De tweede activiteit is eindelijk klaar.

3. Betekenisvolle overgangen

We zijn eindelijk aangekomen bij de kern van de tutorial en animeren de twee activiteiten met behulp van de nieuwe Lollipop-methode voor transitie met behulp van een gedeeld element.

Stap 1: Configureer uw project

Het eerste dat u moet doen is uw thema bewerken in de style.xml bestand in de -waarden v21 map. Op deze manier schakelt u inhoudsovergangen in en stelt u de ingang en het uitgangspunt in van de weergaven die niet worden gedeeld tussen de twee activiteiten.

 

Houd er rekening mee dat uw project moet worden getarget op (en dus moet worden gecompileerd met) ten minste Android API 21.

De animaties worden genegeerdop systemen waarop Lollipop niet is geïnstalleerd. Helaas is vanwege prestatieredenen de AppCompat bibliotheek biedt geen volledige achterwaartse compatibiliteit voor deze animaties.

Stap 2: Wijs de Transition Name toe aan de Layout Files

Nadat je je hebt bewerkt style.xml bestand, moet u wijzen op de relatietussen de twee gemeenschappelijke elementen van de opvattingen.

In ons voorbeeld zijn de gedeelde weergaven het veld met de naam van het contact, dat van het telefoonnummer en de gekleurde cirkel. Voor elk daarvan moet u een algemene transitie naam. Begin daarom in de strings.xml bronbestand de volgende items:

transitie NAME transitie CIRKEL transitie PHONE 

Voeg vervolgens voor elk van de drie paren in de lay-outbestanden de android: transitionName attribuut met de bijbehorende waarde. Voor de gekleurde cirkel ziet de code er als volgt uit:

 
 

Dankzij dit kenmerk weet Android welke weergaven tussen beide activiteiten worden gedeeld en wordt de overgang correct geanimeerd. Herhaal hetzelfde proces voor de andere twee weergaven.

Stap 3: Configureer de intentie

Vanuit coderingsoogpunt moet u een specifieke toevoegen ActivityOptions bundelen naar de intentie. De methode die je nodig hebt is makeSceneTransitionAnimation, die als parameters de context van de applicatie en evenveel gedeelde elementen als we nodig hebben. In de onItemClick methode van de RecyclerView, bewerk de eerder gedefinieerde voornemen zoals dit:

@Override public void onItemClick (View view, int position) Intent intent = new Intent (MainActivity.this, DetailsActivity.class); intent.putExtra (DetailsActivity.ID, Contact.CONTACTS [positie] .getId ()); ActivityOptionsCompat-opties = ActivityOptionsCompat.makeSceneTransitionAnimation (// de context van de activiteit MainActivity.this, // Voeg voor elk gedeeld element een nieuw Pair-item toe aan deze methode //, dat de verwijzing bevat naar de view die we overzetten * van *, // en de waarde van de transitionName-eigenschap new Pair(view.findViewById (R.id.CONTACT_circle), getString (R.string.transition_name_circle)), nieuw paar(view.findViewById (R.id.CONTACT_name), getString (R.string.transition_name_name)), nieuw paar(view.findViewById (R.id.CONTACT_phone), getString (R.string.transition_name_phone))); ActivityCompat.startActivity (MainActivity.this, intent, options.toBundle ()); 

Voor elk gedeeld element dat moet worden geanimeerd, moet u toevoegen aan de makeSceneTransitionAnimation methode een nieuw Paar item. Elk Paar heeft twee waarden, de eerste is een verwijzing naar de weergave die u aan het omzetten bent van, de tweede is de waarde van de transitionName attribuut.

Wees voorzichtig bij het importeren van de Paar klasse. U moet de android.support.v4.util pakket, niet de android.util pakket. Vergeet ook niet te gebruiken ActivityCompat.startActivity methode in plaats van de startActivity methode, omdat u anders uw applicatie niet kunt uitvoeren op omgevingen met een API onder de 16.

Dat is het. U bent klaar. Zo simpel is het.

Conclusie

In deze zelfstudie leer je hoe je naadloos en naadloos kunt overschakelen tussen twee activiteiten die één of meer gemeenschappelijke elementen delen, waardoor je een visueel aangename en zinvolle continuïteit hebt.

U begon met het maken van de eerste van de twee activiteiten, waarvan het de rol is om de lijst met contacten weer te geven. Vervolgens hebt u de tweede activiteit voltooid, de lay-out ontworpen en een manier geïmplementeerd om een ​​unieke referentie tussen de twee activiteiten door te geven. Ten slotte keek je naar de manier waarop makeSceneTransitionAnimation werkt, dankzij de XML transitionName attribuut.

Bonustip: stilistische details

Om een ​​echte Material Design-zoektoepassing te maken, zoals weergegeven in de vorige schermafbeeldingen, moet u ook de kleuren van uw thema wijzigen. Bewerk uw basisthema in de -waarden v21 map om een ​​mooi resultaat te bereiken.