En av de mest interessante aspektene ved Material Design-spesifikasjonene er den visuelle kontinuiteten mellom aktivitetene. Med bare noen få linjer med kode, gir de nye Lollipop-APIene deg en meningsfull overgang mellom to aktiviteter, takket være sømløse og kontinuerlige animasjoner. Dette bryter de klassiske aktivitetsgrensene til tidligere Android-versjoner og lar brukeren forstå hvordan elementene går fra ett punkt til et annet.
I denne opplæringen vil jeg vise deg hvordan du oppnår dette resultatet, og gjør en prøveapplikasjon i samsvar med Googles retningslinjer for materialdesign.
I denne opplæringen antar jeg at du allerede er kjent med Android-utvikling, og at du bruker Android Studio som din IDE. Jeg bruker Android-intensjoner i stor utstrekning, forutsatt at det er grunnleggende kunnskaper om aktivitetslivssyklusen og den nye RecyclerView
widget introdusert med API 21, siste juni. Jeg kommer ikke til å dykke inn i detaljene i denne klassen, men hvis du er interessert, kan du finne en flott forklaring i denne Tuts + opplæringen.
Den grunnleggende strukturen i søknaden er rettferdig. Det er to aktiviteter, en hoveddel, MainActivity.java, hvis oppgave det er å vise en liste over elementer, og en annen, DetailActivity.java, som vil vise detaljene til elementet valgt i forrige liste.
RecyclerView
WidgetFor å vise listen over elementer, vil hovedaktiviteten bruke RecyclerView
widget introdusert i Android Lollipop. Det første du må gjøre er å legge til følgende linje i avhengig delen i prosjektets build.grade fil for å aktivere bakoverkompatibilitet:
kompilere 'com.android.support:recyclerview-v7:+'
For korthets skyld vil vi ikke definere en faktisk database eller en tilsvarende datakilde for søknaden. I stedet vil vi bruke en egendefinert klasse, Ta kontakt med
. Hvert element vil ha et navn, en farge og grunnleggende kontaktinformasjon knyttet til den. Dette er hva implementeringen av Ta kontakt med
klassen ser ut som:
offentlig klasse Kontakt // Feltene knyttet til privatpersonens endelige sluttstreng mName, mPhone, mEmail, mCity, mColor; Kontakt (Strenge navn, String farge, String telefon, String e-post, String by) mName = navn; mColor = farge; mPhone = telefon; mEmail = email; mCity = by; // Denne metoden gjør det mulig å få elementet tilknyttet et bestemt ID, // unikt generert ved metoden getId definert under offentlig statisk kontakt GetItem (int id) for (Kontaktpunkt: KONTAKTER) hvis (item.getId () == id) return item; returnere null; // siden mName og mPhone kombinert er helt unikt, // vi trenger ikke å legge til et annet id-felt offentlig int getId () return mName.hashCode () + mPhone.hashCode (); offentlig statisk enum Felt NAME, FARGE, TELEFON, EMAIL, STAD offentlig String få (Felt f) bytte (f) tilfelle FARGE: returner mColor; tilfelle PHONE: returner mPhone; sak EMAIL: returner mEmail; sak CITY: retur mCity; tilfelle NAME: default: return mName;
Du vil ende opp med en fin container for informasjonen du bryr deg om. Men vi må fylle den med noen data. På toppen av Ta kontakt med
klasse, legg til følgende stykke kode for å fylle ut datasettet.
Ved å definere dataene som offentlig
og statisk
, Hver klasse i prosjektet er i stand til å lese den. På en måte etterlikner vi oppførselen til en database, med unntak av at vi hardcoding det inn i en klasse.
offentlig statisk endelig kontakt [] CONTACTS = ny kontakt [] ny kontakt ("John", "# 33b5e5", "+01 123456789", "[email protected]", "Venezia"), ny kontakt , "# ffbb33", "+01 987654321", "[email protected]", "Bologna"), ny kontakt ("Eadwine", "# ff4444", "+01 123456789", "[email protected]" , "Verona"), ny kontakt ("Teddy", "# 99cc00", "+01 987654321", "[email protected]", "Rome"), ny kontakt ("Ives", "# 33b5e5" +01 11235813 "," [email protected] "," Milan "), ny kontakt (" Alajos "," # ffbb33 "," +01 123456789 "," [email protected] "," Bologna "), ny Kontakt ("Gianluca", "# ff4444", "+01 11235813", "[email protected]", "Padova"), ny kontakt ("Fane", "# 99cc00", "+01 987654321", "fane @ example.com "," Venice "),;
Oppsettet for hovedaktiviteten er enkelt, fordi listen fyller hele skjermen. Oppsettet inneholder a RelativeLayout
som roten - men det kan like godt være en LinearLayout
også-og a RecyclerView
som sitt eneste barn.
Fordi det RecyclerView
widgeten ordner underdelinger og ingenting mer, du må også utforme utformingen av en enkelt liste element. Vi vil ha en farget sirkel til venstre for hvert element i kontaktlisten, slik at du først må definere drawable circle.xml.
Du har nå alle elementene som trengs for å definere utformingen av listeposten.
RecyclerView
Vi har nesten kommet til slutten av første del av opplæringen. Du må fortsatt skrive RecyclerView.ViewHolder
og RecyclerView.Adapter
, og tilordne alt til tilhørende visning i onCreate
metode for hovedaktiviteten. I dette tilfellet er det RecyclerView.ViewHolder
må også kunne håndtere klikk, så du må legge til en bestemt klasse som er i stand til å gjøre det. La oss begynne å definere klassen som er ansvarlig for klikkhåndtering.
offentlig klasse RecyclerClickListener implementerer RecyclerView.OnItemTouchListener private OnItemClickListener mListener; GestureDetector mGestureDetector; offentlig grensesnitt OnItemClickListener offentlig ugyldig onItemClick (Vis visning, int posisjon); offentlig RecyclerClickListener (Kontekst kontekst, OnItemClickListener lytter) mListener = listener; mGestureDetector = ny GestureDetector (kontekst, ny GestureDetector.SimpleOnGestureListener () @Override public boolean onSingleTapUp (MotionEvent e) return true;); @Override public boolean onInterceptTouchEvent (RecyclerView visning, MotionEvent e) Se childView = view.findChildViewUnder (e.getX (), e.getY ()); hvis (childView! = null && mListener! = null && mGestureDetector.onTouchEvent (e)) mListener.onItemClick (childView, view.getChildPosition (childView)); returnere sant; returner falsk; @Override public void onTouchEvent (RecyclerView-visning, MotionEvent motionEvent)
Det er nødvendig å spesifisere RecyclerView.Adapter
, som jeg vil kalle det DataManager
. Det er ansvarlig for å laste inn dataene og sette det inn i visningen av listen. Denne datahåndteringsklassen vil også inneholde definisjonen av RecyclerView.ViewHolder
.
Public Class DataManager utvider RecyclerView.Adapteroffentlig statisk klasse RecyclerViewHolder strekker RecyclerView.ViewHolder TextView mName, mPhone; Se mCircle; RecyclerViewHolder (Se elementView) 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); @ Overstyr offentlige RecyclerViewHolder onCreateViewHolder (ViewGroup viewGroup, int i) Vis v = LayoutInflater.from (viewGroup.getContext ()). Oppblås (R.layout.contact_item, viewGroup, false); returnere ny RecyclerViewHolder (v); @Override public void onBindViewHolder (RecyclerViewHolder viewHolder, int i) // få det enkle elementet fra hovedmatrisefinalen Kontaktkontakt = Contact.CONTACTS [i]; // Sett verdiene viewHolder.mName.setText (contact.get (Contact.Field.NAME)); viewHolder.mPhone.setText (contact.get (Contact.Field.PHONE)); // Angi fargen på formen GradientDrawable bgShape = (GradientDrawable) viewHolder.mCircle.getBackground (); bgShape.setColor (Color.parseColor (contact.get (Contact.Field.COLOR))); @Override public int getItemCount () returnér kontakt.CONTACTS.length;
Til slutt legger du til følgende kode i onCreate
metode, under setContentView.
Hovedaktiviteten er klar.
RecyclerView rv = (RecyclerView) findViewById (R.id.rv); // layout referanse LinearLayoutManager llm = ny LinearLayoutManager (dette); rv.setLayoutManager (LLM); rv.setHasFixedSize (true); // for å forbedre ytelsen rv.setAdapter (new DataManager ()); // dataadministratoren er tilordnet RV rv.addOnItemTouchListener (// og klikket håndteres ny RecyclerClickListener (dette, ny RecyclerClickListener.OnItemClickListener () @Override public void onItemClick (Vis visning, int posisjon) // STUB: // Klikk på elementet må håndteres));
Slik ser programmet ut hvis du bygger og kjører det.
Den andre aktiviteten er mye enklere. Det tar IDen til kontakten valgt og henter tilleggsinformasjonen som den første aktiviteten ikke viser.
Fra et designsynspunkt er utformingen av denne aktiviteten kritisk siden den er den viktigste delen av søknaden. Men for det som gjelder XML, er det trivielt. Oppsettet er en serie av TextView
forekomster plassert på en hyggelig måte, ved hjelp av RelativeLayout
og LinearLayout
. Slik ser layouten ut:
Siden de to aktivitetene er knyttet sammen med en hensikt, må du sende litt informasjon som gjør det mulig for den andre aktiviteten å forstå hvilkekontakt deg om etterspørselen.
Et alternativ kan bruke posisjonsvariabelen som referanse. Plasseringen av elementet i listen tilsvarer plasseringen av elementet i arrayet, så det bør ikke være noe dårlig å bruke dette heltallet som en unik referanse.
Dette ville fungere, men hvis du tar denne tilnærmingen og, uansett grunn, datasettet er endret ved kjøring, vil referansen ikke matche kontakten du er interessert i. Dette er grunnen til at det er bedre å bruke en IDad hoc. Denne informasjonen er getId
metode definert i Ta kontakt med
klasse.
Rediger onItemClick
håndterer av listen over elementer som vist nedenfor.
@Override public void onItemClick (Vis visning, int posisjon) Intent intention = new Intent (MainActivity.this, DetailsActivity.class); intent.putExtra (DetailsActivity.ID, Contact.CONTACTS [posisjon] .getId ()); startActivity (hensikt);
De DetailsActivity
vil motta informasjonen fra Intent
ekstramateriale og konstruere det riktige objektet ved hjelp av ID som en referanse. Dette vises i følgende kodeblokk.
// Før onCreate offentlige endelige statiske String ID = "ID"; offentlig kontakt mContact;
// I onCreate, etter setContentView-metoden mContact = Contact.getItem (getIntent (). GetIntExtra (ID, 0));
Akkurat som før i onCreateViewHolder
metode av RecylerView
, visningene initialiseres ved hjelp av findViewById
metode og befolket med setText
. For eksempel, for å konfigurere navnefeltet gjør vi følgende:
mName = (TextView) findViewById (R.id.DETAILS_name); mName.setText (mContact.get (Contact.Field.NAME));
Prosessen er den samme for de andre feltene. Den andre aktiviteten er endelig klar.
Vi har endelig kommet til kjernen i opplæringen, og animerer de to aktivitetene ved hjelp av den nye Lollipop-metoden for overgang ved å bruke et delt element.
Det første du må gjøre er å redigere temaet ditt i style.xml fil i Verdiene-V21 mappe. På denne måten aktiverer du innholdsoverganger og angir inngangen og utgangen av visningene som ikke deles mellom de to aktivitetene.
Vær oppmerksom på at prosjektet ditt må være målrettet mot (og dermed kompilert med) i det minste Android API 21.
Animasjonene blir ignorertpå systemer som ikke har Lollipop installert. Dessverre, på grunn av ytelsesårsaker, AppCompat biblioteket gir ikke komplett bakoverkompatibilitet for disse animasjonene.
Når du har redigert din style.xml fil, du må påpeke forholdetmellom de to vanlige elementene i utsikten.
I vårt eksempel er de delte visningene feltet som inneholder navnet på kontakten, det ene av telefonnummeret og den fargede sirkelen. For hver av dem må du spesifisere en felles overgangsnavn. Av denne grunn, begynn å legge til i strings.xml ressursfilen følgende elementer:
overgang: NAVN overgang: CIRCLE overgang: TELEFON
Deretter legger du til hvert av de tre parene i layoutfilene android: transitionName
Tilordne med tilsvarende verdi. For den fargede sirkelen ser koden slik ut:
Takket være dette attributtet, vil Android vite hvilke visninger som deles mellom de to aktivitetene, og vil riktig animere overgangen. Gjenta den samme prosessen for de andre to visningene.
Fra et kodende synspunkt må du legge ved en bestemt ActivityOptions
bunt til hensikten. Metoden du trenger er makeSceneTransitionAnimation
, som tar som parametere sammenhengen mellom søknaden og så mange delte elementer som vi trenger. I onItemClick
metode av RecyclerView
, rediger den tidligere definerte Intent
som dette:
@Override public void onItemClick (Vis visning, int posisjon) Intent intention = new Intent (MainActivity.this, DetailsActivity.class); intent.putExtra (DetailsActivity.ID, Contact.CONTACTS [posisjon] .getId ()); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation (// konteksten til aktiviteten MainActivity.this, // For hvert delt element legger du til denne metoden et nytt Par-element, // som inneholder referansen til visningen vi overfører * fra *, // og verdien av overgangenName attributt ny par(view.findViewById (R.id.CONTACT_circle), getString (R.string.transition_name_circle)), ny par (view.findViewById (R.id.CONTACT_name), getString (R.string.transition_name_name)), nye par (view.findViewById (R.id.CONTACT_phone), getString (R.string.transition_name_phone))); ActivityCompat.startActivity (MainActivity.this, intent, options.toBundle ());
For hvert delt element som skal animeres, må du legge til i makeSceneTransitionAnimation
Metode en ny Par
punkt. Hver Par
har to verdier, den første er en referanse til visningen du overfører fra, den andre er verdi av transitionName
Egenskap.
Vær forsiktig når du importerer Par
klasse. Du må inkludere android.support.v4.util
pakke, ikke de android.util
pakke. Husk også å bruke ActivityCompat.startActivity
metode i stedet for startActivity
metode, fordi ellers vil du ikke kunne kjøre programmet på miljøer med API under 16.
Det er det. Du er ferdig. Det er så enkelt.
I denne opplæringen lærte du hvordan du smidig og sømløst skal overgå mellom to aktiviteter som deler en eller flere vanlige elementer, noe som gir en visuelt hyggelig og meningsfull kontinuitet.
Du startet med å lage den første av de to aktivitetene, hvis rolle det er å vise listen over kontakter. Deretter fullførte du den andre aktiviteten, utformet utformingen, og implementerte en måte å passere en unik referanse mellom de to aktivitetene. Til slutt så du på hvordan makeSceneTransitionAnimation
fungerer, takket være XML transitionName
Egenskap.
For å skape en ekte Material Design-applikasjon, som vist i de forrige skjermbildene, må du også endre fargene på temaet ditt. Rediger ditt grunntema i Verdiene-V21 mappe for å oppnå et godt resultat.