Introduzione alle nuove transizioni di attività lecca-lecca

Cosa starai creando

introduzione

Uno degli aspetti più interessanti delle specifiche di Material Design è la continuità visiva tra le attività. Con poche righe di codice, le nuove API Lollipop ti consentono di passare significativamente da una attività all'altra, grazie a animazioni continue e continue. Questo rompe i classici limiti di attività delle precedenti versioni di Android e consente all'utente di capire come gli elementi vanno da un punto all'altro.

In questo tutorial, ti mostrerò come raggiungere questo risultato, rendendo un'applicazione di esempio coerente con le linee guida di Google per il material design.

Prerequisiti

In questo tutorial, presumo che tu abbia già familiarità con lo sviluppo di Android e che utilizzi Android Studio come IDE. Userò ampiamente intenti Android, assumendo una conoscenza di base del ciclo di vita delle attività e del nuovo RecyclerView widget introdotto con API 21, lo scorso giugno. Non ho intenzione di immergermi nei dettagli di questo corso, ma, se sei interessato, puoi trovare un'ottima spiegazione in questo Tuts + tutorial.

1. Crea la prima attività

La struttura di base dell'applicazione è semplice. Ci sono due attività, una principale, MainActivity.java, il cui compito è visualizzare un elenco di elementi e un secondo, DetailActivity.java, che mostrerà i dettagli dell'elemento selezionato nell'elenco precedente.

Step 1: The RecyclerView widget

Per mostrare l'elenco di elementi, l'attività principale utilizzerà il RecyclerViewwidget introdotto in Android Lollipop. La prima cosa che devi fare è aggiungere la seguente riga a dipendenze sezione del tuo progetto build.grade file per abilitare la retrocompatibilità:

compila "com.android.support:recyclerview-v7:+"

Passaggio 2: definizione dei dati

Per brevità, non definiremo un vero database o una fonte di dati simile per l'applicazione. Invece, useremo una classe personalizzata, Contatto. Ad ogni articolo saranno associati un nome, un colore e informazioni di contatto di base. Questo è ciò che l'implementazione del Contatto la classe sembra:

public class Contatto // I campi associati alla persona privata finale String mName, mPhone, mEmail, mCity, mColor; Contatto (Nome stringa, Colore stringa, Telefono stringa, Email stringa, Città stringa) mName = nome; mColore = colore; mPhone = telefono; mEmail = email; mCity = città;  // Questo metodo consente di ottenere l'elemento associato a un particolare id, // generato univocamente dal metodo getId definito sotto public static Contact getItem (int id) for (voce contatto: CONTACTS) if (item.getId () == id) return item;  restituisce null;  // poiché mName e mPhone combinati sono sicuramente unici, // non è necessario aggiungere un altro campo id pubblico int getId () return mName.hashCode () + mPhone.hashCode ();  public statico enum Field NOME, COLORE, TELEFONO, EMAIL, CITTÀ public String get (Field f) switch (f) case COLOR: return mColor; case PHONE: return mPhone; case EMAIL: return mEmail; case CITY: return mCity; case NAME: default: return mName; 

Finirai con un bel contenitore per le informazioni che ti interessano. Ma dobbiamo riempirlo con alcuni dati. Nella parte superiore del Contattoclasse, aggiungi il seguente pezzo di codice per popolare il set di dati.

Definendo i dati come pubblico e statico, ogni classe nel progetto è in grado di leggerla. In un certo senso, imitiamo il comportamento di un database con l'eccezione che lo stiamo codificando in una classe.

public static final Contact [] CONTACTS = new Contact [] new Contact ("John", "# 33b5e5", "+01 123456789", "[email protected]", "Venezia"), nuovo contatto ("Valter") , "# ffbb33", "+01 987654321", "[email protected]", "Bologna"), nuovo contatto ("Eadwine", "# ff4444", "+01 123456789", "[email protected]" , "Verona"), nuovo contatto ("Teddy", "# 99cc00", "+01 987654321", "[email protected]", "Roma"), nuovo contatto ("Ives", "# 33b5e5", " +01 11235813 "," [email protected] "," Milano "), nuovo contatto (" Alajos "," # ffbb33 "," +01 123456789 "," [email protected] "," Bologna "), nuovo Contatto ("Gianluca", "# ff4444", "+01 11235813", "[email protected]", "Padova"), nuovo contatto ("Fane", "# 99cc00", "+01 987654321", "fane @ esempio.com "," Venezia "),;

Passaggio 3: Definire i layout principali

Il layout dell'attività principale è semplice, perché l'elenco riempirà l'intero schermo. Il layout include a RelativeLayout come la radice - ma può anche essere un a LinearLayout anche e a RecyclerView come suo unico figlio.

  

Perché il RecyclerView widget organizza i sottoelementi e nient'altro, è inoltre necessario progettare il layout di una singola voce di elenco. Vogliamo avere un cerchio colorato alla sinistra di ogni elemento della lista dei contatti, quindi devi prima definire il drawable circle.xml.

   

Ora disponi di tutti gli elementi necessari per definire il layout dell'elemento dell'elenco.

      

Passaggio 4: Mostra i dati usando il RecyclerView

Siamo quasi arrivati ​​alla fine della prima parte del tutorial. Devi ancora scrivere il RecyclerView.ViewHolder e il RecyclerView.Adapter, e assegnare tutto alla vista associata nel onCreate metodo dell'attività principale. In questo caso, il RecyclerView.ViewHolder deve anche essere in grado di gestire i clic, quindi è necessario aggiungere una classe specifica in grado di farlo. Iniziamo a definire la classe responsabile della gestione dei clic.

public class RecyclerClickListener implementa RecyclerView.OnItemTouchListener private OnItemClickListener mListener; GestureDetector mGestureDetector; interfaccia pubblica OnItemClickListener public void onItemClick (Visualizza vista, posizione int);  public RecyclerClickListener (Context context, OnItemClickListener listener) mListener = listener; mGestureDetector = new GestureDetector (context, new GestureDetector.SimpleOnGestureListener () @Override public boolean onSingleTapUp (MotionEvent e) return true;);  @Override public boolean onInterceptTouchEvent (vista RecyclerView, MotionEvent e) Visualizza childView = view.findChildViewUnder (e.getX (), e.getY ()); if (childView! = null && mListener! = null && mGestureDetector.onTouchEvent (e)) mListener.onItemClick (childView, view.getChildPosition (childView)); ritorna vero;  return false;  @Override public void onTouchEvent (vista RecyclerView, MotionEvent motionEvent) 

È necessario specificare il RecyclerView.Adapter, che lo chiamerò Gestore dati. È responsabile del caricamento dei dati e dell'inserimento nelle viste dell'elenco. Questa classe del data manager conterrà anche la definizione di RecyclerView.ViewHolder.

DataManager di classe pubblica estende RecyclerView.Adapter public static class RecyclerViewHolder estende RecyclerView.ViewHolder TextView mName, mPhone; Visualizza mCircle; RecyclerViewHolder (Visualizza 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) Visualizza v = LayoutInflater.from (viewGroup.getContext ()). Gonfia (R.layout.contact_item, viewGroup, false); restituire nuovo RecyclerViewHolder (v);  @Override public void onBindViewHolder (RecyclerViewHolder viewHolder, int i) // richiama il singolo elemento dall'array principale finale Contact contact = Contact.CONTACTS [i]; // Imposta i valori viewHolder.mName.setText (contact.get (Contact.Field.NAME)); viewHolder.mPhone.setText (contact.get (Contact.Field.PHONE)); // Imposta il colore della forma GradientDrawable bgShape = (GradientDrawable) viewHolder.mCircle.getBackground (); bgShape.setColor (Color.parseColor (contact.get (Contact.Field.COLOR)));  @Override public int getItemCount () return Contact.CONTACTS.length; 

Infine, aggiungi il seguente codice al onCreatemetodo, di seguito setContentView. L'attività principale è pronta.

RecyclerView rv = (RecyclerView) findViewById (R.id.rv); // riferimento layout LinearLayoutManager llm = new LinearLayoutManager (this); rv.setLayoutManager (LLM); rv.setHasFixedSize (true); // per migliorare le prestazioni rv.setAdapter (new DataManager ()); // il gestore dati è l'assegnatario di RV rv.addOnItemTouchListener (// e il clic è gestito da nuovo RecyclerClickListener (questo, nuovo RecyclerClickListener.OnItemClickListener () @Override pubblico void onItemClick (Visualizza vista, posizione int) // STUB: // Il clic sull'elemento deve essere gestito));

Questo è l'aspetto dell'applicazione se lo costruisci ed esegui.

2. Creare l'attività Dettagli

Passaggio 1: il layout

La seconda attività è molto più semplice. Prende l'ID del contatto selezionato e recupera le informazioni aggiuntive che la prima attività non mostra.

Dal punto di vista del design, il layout di questa attività è fondamentale poiché è la parte più importante dell'applicazione. Ma per quanto riguarda l'XML, è banale. Il layout è una serie di TextView istanze posizionate in modo piacevole, usando RelativeLayout e LinearLayout. Ecco come appare il layout:

                    

Passo 2: Invia e ricevi l'ID tramite Intent Extras

Poiché le due attività sono collegate da un intento, è necessario inviare alcune informazioni che consentano alla seconda attività di comprendere qualicontatto hai richiesto i dettagli.

Un'opzione può essere l'utilizzo della variabile di posizione come riferimento. La posizione dell'elemento nella lista corrisponde alla posizione dell'elemento nell'array quindi non dovrebbe esserci nulla di male nell'uso di questo intero come riferimento univoco.

Funzionerebbe, ma se si utilizza questo approccio e, per qualsiasi motivo, il set di dati viene modificato in fase di esecuzione, il riferimento non corrisponderà al contatto a cui si è interessati. Questo è il motivo per cui è preferibile utilizzare un IDad hoc. Questa informazione è il getId metodo definito nel Contatto classe.

Modifica il onItemClick gestore dell'elenco di elementi come mostrato di seguito.

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

Il DetailsActivity riceverà le informazioni dal Intento extra e costruire l'oggetto corretto usando l'ID come riferimento. Questo è mostrato nel seguente blocco di codice.

// Prima di onCreate public final String ID statico = "ID"; contatto pubblico mContact;
// In onCreate, dopo il metodo setContentView mContact = Contact.getItem (getIntent (). GetIntExtra (ID, 0));

Proprio come prima nel onCreateViewHolder metodo del RecylerView, le viste sono inizializzate usando il findViewById metodo e popolato usando setText. Ad esempio, per configurare il campo del nome facciamo quanto segue:

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

Il processo è lo stesso per gli altri campi. La seconda attività è finalmente pronta.

3. Transizioni significative

Siamo finalmente arrivati ​​al cuore del tutorial, animando le due attività usando il nuovo metodo Lollipop per la transizione usando un elemento condiviso.

Passaggio 1: configura il tuo progetto

La prima cosa che devi fare è modificare il tuo tema in style.xml file nel Valori-V21 cartella. In questo modo, abiliti le transizioni di contenuto e imposta l'ingresso e l'uscita delle viste che non sono condivise tra le due attività.

 

Si noti che il progetto deve essere mirato (e quindi essere compilato con) almeno API Android 21.

Le animazioni saranno ignoratesu sistemi che non hanno installato Lollipop. Sfortunatamente, a causa di motivi di prestazioni, il AppCompat la libreria non fornisce una retrocompatibilità completa per queste animazioni.

Passo 2: Assegna il nome della transizione nei file di layout

Una volta che hai modificato il tuo style.xml file, devi indicare la relazionetra i due elementi comuni delle viste.

Nel nostro esempio, le viste condivise sono il campo che contiene il nome del contatto, quello del numero di telefono e il cerchio colorato. Per ognuno di essi, devi specificare a nome di transizione comune. Per questo motivo, inizia ad aggiungere nel strings.xml file di risorse i seguenti elementi:

transizione: NAME transizione: CIRCLE transizione: TELEFONO 

Quindi, per ognuna delle tre coppie, nei file di layout aggiungere il Android: transitionName attributo con il valore corrispondente. Per il cerchio colorato, il codice è simile al seguente:

 
 

Grazie a questo attributo, Android saprà quali viste sono condivise tra le due attività e animerà correttamente la transizione. Ripeti la stessa procedura per le altre due viste.

Passaggio 3: Configura l'intento

Dal punto di vista della codifica, è necessario allegare uno specifico ActivityOptions pacchetto per l'intento. Il metodo che ti serve è makeSceneTransitionAnimation, che prende come parametri il contesto dell'applicazione e tutti gli elementi condivisi di cui abbiamo bisogno. Nel onItemClick metodo del RecyclerView, modificare il precedente definito Intento come questo:

@Override public void onItemClick (Visualizza view, int position) Intent intent = new Intent (MainActivity.this, DetailsActivity.class); intent.putExtra (DetailsActivity.ID, Contact.CONTACTS [position] .getId ()); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation (// il contesto dell'attività MainActivity.this, // Per ciascun elemento condiviso, aggiungi a questo metodo un nuovo elemento Pair, // che contiene il riferimento della vista che stiamo transitando * da *, // e il valore dell'attributo transitionName new Pair(view.findViewById (R.id.CONTACT_circle), getString (R.string.transition_name_circle)), nuova coppia(view.findViewById (R.id.CONTACT_name), getString (R.string.transition_name_name)), nuova coppia(view.findViewById (R.id.CONTACT_phone), getString (R.string.transition_name_phone))); ActivityCompat.startActivity (MainActivity.this, intent, options.toBundle ()); 

Per ogni elemento condiviso da animare, dovrai aggiungere al makeSceneTransitionAnimation metodo un nuovo Paio articolo. Ogni Paio ha due valori, il primo è un riferimento alla vista che stai passando a partire dal, il secondo è il valore del transitionName attributo.

Fare attenzione durante l'importazione di Paio classe. Dovrai includere il android.support.v4.util pacchetto, non il android.util pacchetto. Inoltre, ricorda di usare ActivityCompat.startActivity metodo invece del startActivity metodo, altrimenti non sarà possibile eseguire l'applicazione in ambienti con API inferiore a 16.

Questo è tutto. Hai finito. E 'così semplice.

Conclusione

In questo tutorial hai imparato come passare magnificamente e senza interruzioni tra due attività che condividono uno o più elementi comuni, consentendo una continuità visivamente piacevole e significativa.

Hai iniziato facendo la prima delle due attività, il cui ruolo è visualizzare l'elenco dei contatti. Hai quindi completato la seconda attività, progettandone il layout e implementando un modo per passare un riferimento univoco tra le due attività. Infine, hai guardato il modo in cui makeSceneTransitionAnimation funziona, grazie all'XML transitionName attributo.

Bonus Suggerimento: dettagli stilistici

Per creare un'applicazione di aspetto di Material Design vera, come mostrato nelle schermate precedenti, dovrai anche cambiare i colori del tuo tema. Modifica il tema base in Valori-V21 cartella per ottenere un buon risultato.