Pengenalan kepada Transisi Aktivitas Lollipop Baru
Indonesian (Bahasa Indonesia) translation by Ilham Saputra (you can also view the original English article)
Pengenalan
Salah satu aspek yang paling menarik dari spesifikasi Material Design adalah kesinambungan visual antar aktivitas. Dengan hanya beberapa baris kode, API Lollipop baru memungkinkan Anda untuk melakukan transisi yang bermakna di antara dua aktivitas, berkat animasi yang lancar dan berkesinambungan. Ini memecah batas-batas aktivitas klasik dari versi Android sebelumnya dan memungkinkan pengguna untuk memahami bagaimana elemen berpindah dari satu titik ke titik lainnya.
Dalam tutorial ini, saya akan menunjukkan kepada Anda cara mencapai hasil ini, membuat aplikasi sampel konsisten dengan pedoman Material Design Google.
Prasyarat
Dalam tutorial ini, saya akan berasumsi bahwa Anda sudah akrab dengan pengembangan Android dan bahwa Anda menggunakan Android Studio sebagai IDE Anda. Saya akan menggunakan maksud Android secara luas, dengan asumsi pengetahuan dasar siklus hidup aktivitas, dan widget RecyclerView
baru diperkenalkan dengan API 21, Juni lalu. Saya tidak akan menyelami detail kelas ini, tetapi, jika Anda tertarik, Anda dapat menemukan penjelasan yang bagus dalam tutorial Tuts+ ini.
1. Buat Activity Pertama
Struktur dasar dari aplikasi ini sangat mudah. Ada dua aktivitas, yang utama, MainActivity.java, yang tugasnya adalah menampilkan daftar item, dan yang kedua, DetailActivity.java, yang akan menunjukkan rincian item yang dipilih dalam daftar sebelumnya.
Langkah 1: Widget RecyclerView
Untuk menampilkan daftar item, activity utama akan menggunakan widget RecyclerView
yang dikenalkan di Android Lollipop. Hal pertama yang perlu Anda lakukan adalah, tambahkan baris berikut ke bagian dependencies dalam file build.grade proyek Anda untuk mengaktifkan kompatibilitas ke belakang:
compile 'com.android.support:recyclerview-v7:+'
Langkah 2: Definisi Data
Demi keringkasan, kami tidak akan mendefinisikan database aktual atau sumber data yang serupa untuk aplikasi tersebut. Sebagai gantinya, kita akan menggunakan kelas khusus, Contact
. Setiap item akan memiliki nama, warna, dan informasi kontak dasar yang terkait dengannya. Seperti inilah implementasi kelas Contact
:
public class Contact { // The fields associated to the person private final String mName, mPhone, mEmail, mCity, mColor; Contact(String name, String color, String phone, String email, String city) { mName = name; mColor = color; mPhone = phone; mEmail = email; mCity = city; } // This method allows to get the item associated to a particular id, // uniquely generated by the method getId defined below public static Contact getItem(int id) { for (Contact item : CONTACTS) { if (item.getId() == id) { return item; } } return null; } // since mName and mPhone combined are surely unique, // we don't need to add another id field public int getId() { return mName.hashCode() + mPhone.hashCode(); } public static enum Field { NAME, COLOR, PHONE, EMAIL, CITY } 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; } } }
Anda akan berakhir dengan container yang bagus untuk informasi yang Anda minati. Tetapi kita perlu mengisinya dengan beberapa data. Di bagian atas kelas Contact
, tambahkan potongan kode berikut untuk mengisi kumpulan data.
Dengan mendefinisikan data sebagai public
dan static
, setiap kelas dalam proyek dapat membacanya. Dalam arti, kita meniru perilaku database dengan pengecualian bahwa kita meng-hardcoding-nya ke dalam kelas.
public static final Contact[] CONTACTS = new Contact[] { new Contact("John", "#33b5e5", "+01 123456789", "john@example.com", "Venice"), new Contact("Valter", "#ffbb33", "+01 987654321", "valter@example.com", "Bologna"), new Contact("Eadwine", "#ff4444", "+01 123456789", "eadwin@example.com", "Verona"), new Contact("Teddy", "#99cc00", "+01 987654321", "teddy@example.com", "Rome"), new Contact("Ives", "#33b5e5", "+01 11235813", "ives@example.com", "Milan"), new Contact("Alajos", "#ffbb33", "+01 123456789", "alajos@example.com", "Bologna"), new Contact("Gianluca", "#ff4444", "+01 11235813", "me@gian.lu", "Padova"), new Contact("Fane", "#99cc00", "+01 987654321", "fane@example.com", "Venice"), };
Langkah 3: Mendefinisikan Layout Utama
Tata letak aktivitas utama sederhana, karena daftar akan mengisi seluruh layar. Tata letaknya termasuk RelativeLayout
sebagai root — tetapi bisa juga menjadi LinearLayout
juga — dan RecyclerView
sebagai child satu-satunya.
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#f5f5f5"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/rv" /> </RelativeLayout>
Karena widget RecyclerView
mengatur sub-elemen dan tidak lebih, Anda juga perlu merancang tata letak item daftar tunggal. Kami ingin memiliki lingkaran berwarna di sebelah kiri setiap item dari daftar kontak sehingga Anda harus terlebih dahulu menentukan drawable circle.xml.
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#000"/> <size android:width="32dp" android:height="32dp"/> </shape>
Anda sekarang memiliki semua elemen yang diperlukan untuk menentukan tata letak item daftar.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="82dp" android:padding="@dimen/activity_horizontal_margin" android:background="?android:selectableItemBackground" android:clickable="true" android:focusable="true" android:orientation="vertical" > <View android:id="@+id/CONTACT_circle" android:layout_width="40dp" android:layout_height="40dp" android:background="@drawable/circle" android:layout_centerVertical="true" android:layout_alignParentLeft="true"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@+id/CONTACT_circle" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:orientation="vertical"> <TextView android:id="@+id/CONTACT_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Jonh Doe" android:textColor="#000" android:textSize="18sp"/> <TextView android:id="@+id/CONTACT_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="+01 123456789" android:textColor="#9f9f9f" android:textSize="15sp"/> </LinearLayout> </RelativeLayout>
Langkah 4: Tampilkan Data Menggunakan RecyclerView
Kami hampir tiba di akhir bagian pertama dari tutorial. Anda masih harus menulis RecyclerView.ViewHolder
dan RecyclerView.Adapter
, dan menugaskan semuanya ke tampilan terkait dalam metode onCreate
dari aktivitas utama. Dalam hal ini, RecyclerView.ViewHolder
juga harus dapat menangani klik sehingga Anda perlu menambahkan kelas khusus yang mampu melakukannya. Mari kita mulai mendefinisikan kelas yang bertanggung jawab untuk penanganan klik.
public class RecyclerClickListener implements RecyclerView.OnItemTouchListener { private OnItemClickListener mListener; GestureDetector mGestureDetector; public interface OnItemClickListener { public void onItemClick(View view, int position); } 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(RecyclerView view, MotionEvent e) { View childView = view.findChildViewUnder(e.getX(), e.getY()); if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { mListener.onItemClick(childView, view.getChildPosition(childView)); return true; } return false; } @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { } }
Anda perlu menentukan RecyclerView.Adapter
, yang akan saya sebut sebagai DataManager
. Ini bertanggung jawab untuk memuat data dan memasukkannya ke tampilan daftar. Kelas pengelola data ini juga akan berisi definisi RecyclerView.ViewHolder
.
public class DataManager extends RecyclerView.Adapter<DataManager.RecyclerViewHolder> { public static class RecyclerViewHolder extends RecyclerView.ViewHolder { TextView mName, mPhone; View 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); return new RecyclerViewHolder(v); } @Override public void onBindViewHolder(RecyclerViewHolder viewHolder, int i) { // get the single element from the main array final Contact contact = Contact.CONTACTS[i]; // Set the values viewHolder.mName.setText(contact.get(Contact.Field.NAME)); viewHolder.mPhone.setText(contact.get(Contact.Field.PHONE)); // Set the color of the shape GradientDrawable bgShape = (GradientDrawable) viewHolder.mCircle.getBackground(); bgShape.setColor(Color.parseColor(contact.get(Contact.Field.COLOR))); } @Override public int getItemCount() { return Contact.CONTACTS.length; } }
Akhirnya, tambahkan kode berikut ke metode onCreate
, di bawah setContentView
. Activity utama sudah siap.
RecyclerView rv = (RecyclerView) findViewById(R.id.rv); // layout reference LinearLayoutManager llm = new LinearLayoutManager(this); rv.setLayoutManager(llm); rv.setHasFixedSize(true); // to improve performance rv.setAdapter(new DataManager()); // the data manager is assigner to the RV rv.addOnItemTouchListener( // and the click is handled new RecyclerClickListener(this, new RecyclerClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { // STUB: // The click on the item must be handled } }));
Ini adalah apa yang tampak seperti aplikasi jika Anda membangun dan menjalankannya.



2. Buat Rincian Activity
Langkah 1: Tata letak
Activity kedua jauh lebih sederhana. Dibutuhkan ID dari kontak yang dipilih dan mengambil informasi tambahan yang tidak ditampilkan aktivitas pertama.
Dari sudut pandang desain, tata letak kegiatan ini sangat penting karena ini adalah bagian terpenting dari aplikasi. Tetapi untuk apa yang menyangkut XML, itu sepele. Tata letak adalah serangkaian contoh TextView
yang diposisikan dengan cara yang menyenangkan, menggunakan RelativeLayout
dan LinearLayout
. Seperti inilah tata letaknya:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop" android:src="@mipmap/material_wallpaper"/> <RelativeLayout android:layout_width="match_parent" android:layout_height="82dp" android:padding="@dimen/activity_vertical_margin"> <View android:id="@+id/DETAILS_circle" android:layout_width="48dp" android:layout_height="48dp" android:background="@drawable/circle" android:layout_centerVertical="true" android:layout_alignParentLeft="true"/> <TextView android:id="@+id/DETAILS_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Jonh Doe" android:layout_toRightOf="@+id/DETAILS_circle" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:layout_centerVertical="true" android:textColor="#000" android:textSize="25sp"/> </RelativeLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:padding="@dimen/activity_horizontal_margin" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/DETAILS_phone_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Phone:" android:textColor="#000" android:textSize="20sp"/> <TextView android:id="@+id/DETAILS_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/DETAILS_phone_label" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:text="+01 123456789" android:textColor="#9f9f9f" android:textSize="20sp"/> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_vertical_margin"> <TextView android:id="@+id/DETAILS_email_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Email:" android:textColor="#000" android:textSize="20sp"/> <TextView android:id="@+id/DETAILS_email" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/DETAILS_email_label" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:text="jonh.doe@example.com" android:textColor="#9f9f9f" android:textSize="20sp"/> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_vertical_margin"> <TextView android:id="@+id/DETAILS_city_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="City:" android:textColor="#000" android:textSize="20sp"/> <TextView android:id="@+id/DETAILS_city" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/DETAILS_city_label" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:text="Rome" android:textColor="#9f9f9f" android:textSize="20sp"/> </RelativeLayout> </LinearLayout> </LinearLayout>
Langkah 2: Kirim dan Terima ID melalui Intent Ekstra
Karena kedua aktivitas itu dihubungkan oleh suatu maksud, Anda perlu mengirimkan beberapa informasi yang memungkinkan kegiatan kedua untuk memahami kontak mana yang Anda minta rinciannya.
Satu opsi mungkin menggunakan variabel posisi sebagai referensi. Posisi elemen dalam daftar sesuai dengan posisi elemen dalam larik sehingga tidak ada yang buruk dalam menggunakan bilangan bulat ini sebagai referensi unik.
Ini akan berfungsi, tetapi jika Anda mengambil pendekatan ini dan, untuk alasan apa pun, kumpulan data diubah saat waktu proses, referensi tidak akan cocok dengan kontak yang Anda minati. Inilah alasan mengapa lebih baik menggunakan ID ad hoc. Informasi ini adalah metode getId
yang didefinisikan di kelas Contact
.
Edit handler onItemClick
dari daftar item seperti yang ditunjukkan di bawah ini.
@Override public void onItemClick(View view, int position) { Intent intent = new Intent(MainActivity.this, DetailsActivity.class); intent.putExtra(DetailsActivity.ID, Contact.CONTACTS[position].getId()); startActivity(intent); }
DetailsActivity
akan menerima informasi dari ekstra Intent
dan membangun objek yang benar menggunakan ID sebagai referensi. Ini ditampilkan dalam blok kode berikut.
// Before the onCreate public final static String ID = "ID"; public Contact mContact;
// In the onCreate, after the setContentView method mContact = Contact.getItem(getIntent().getIntExtra(ID, 0));
Sama seperti sebelumnya dalam metode onCreateViewHolder
dari RecylerView
, view diinisialisasi menggunakan metode findViewById
dan diisi menggunakan setText
. Misalnya, untuk mengonfigurasi bidang nama, kami melakukan hal berikut:
mName = (TextView) findViewById(R.id.DETAILS_name); mName.setText(mContact.get(Contact.Field.NAME));
Prosesnya sama untuk bidang lainnya. Kegiatan kedua akhirnya siap.



3. Transisi Berarti
Kami akhirnya tiba di inti dari tutorial, animasi dua activity menggunakan metode Lollipop baru untuk transisi menggunakan elemen bersama.
Langkah 1: Mengkonfigurasi Proyek Anda
Hal pertama yang perlu Anda lakukan adalah mengedit tema Anda dalam file style.xml di folder values-v21. Dengan cara ini, Anda mengaktifkan transisi konten dan mengatur pintu masuk dan keluar dari tampilan yang tidak dibagi di antara kedua aktivitas tersebut.
<style name="AppTheme" parent="AppTheme.Base"></style> <style name="AppTheme.Base" parent="android:Theme.Material.Light"> <item name="android:windowContentTransitions">true</item> <item name="android:windowEnterTransition">@android:transition/slide_bottom</item> <item name="android:windowExitTransition">@android:transition/slide_bottom</item> <item name="android:windowAllowEnterTransitionOverlap">true</item> <item name="android:windowAllowReturnTransitionOverlap">true</item> <item name="android:windowSharedElementEnterTransition">@android:transition/move</item> <item name="android:windowSharedElementExitTransition">@android:transition/move</item> </style>
Harap dicatat bahwa proyek Anda harus ditargetkan ke (dan dengan demikian dapat dikompilasi dengan) setidaknya Android API 21.
Animasi akan diabaikan pada sistem yang tidak menginstal Lollipop. Sayangnya, karena alasan kinerja, pustaka AppCompat tidak menyediakan kompatibilitas mundur lengkap untuk animasi ini.
Langkah 2: Tetapkan Nama Transisi di File Tata Letak
Setelah Anda mengedit file style.xml Anda, Anda harus menunjukkan hubungan antara dua elemen umum dari tampilan.
Dalam contoh kami, tampilan yang dibagikan adalah bidang yang berisi nama kontak, salah satu nomor telepon, dan lingkaran berwarna. Untuk masing-masing, Anda harus menentukan nama transisi umum. Untuk alasan ini, mulailah menambahkan dalam file sumber daya strings.xml item berikut:
<string name="transition_name_name">transition:NAME</string> <string name="transition_name_circle">transition:CIRCLE</string> <string name=“transition_name_phone”>transition:PHONE</string>
Kemudian, untuk masing-masing dari tiga pasangan, di file tata letak menambahkan android:transitionName
atribut dengan nilai yang sesuai. Untuk lingkaran berwarna, kode akan terlihat seperti ini:
<!— In the single item layout: the item we are transitioning *from* —> <View android:id=“@+id/CONTACT_circle” android:transitionName=“@string/transition_name_circle” android:layout_width=“40dp” android:layout_height=“40dp” android:background=“@drawable/circle” android:layout_centerVertical=“true” android:layout_alignParentLeft=“true”/>
<!— In the details activity: the item we are transitioning *to* —> <View android:id=“@+id/DETAILS_circle” android:transitionName=“@string/transition_name_circle” android:layout_width=“48dp” android:layout_height=“48dp” android:background=“@drawable/circle” android:layout_centerVertical=“true” android:layout_alignParentLeft=“true”/>
Berkat atribut ini, Android akan tahu tampilan mana yang dibagikan di antara dua aktivitas dan akan membenarkan animasi transisi. Ulangi proses yang sama untuk dua view lainnya.
Langkah 3: Mengkonfigurasi Intent
Dari sudut pandang coding, Anda harus melampirkan bundel ActivityOptions
spesifik ke intent. Metode yang Anda perlukan adalah makeSceneTransitionAnimation
, yang mengambil parameter konteks aplikasi dan banyak elemen bersama yang kita butuhkan. Dalam metode onItemClick
dari RecyclerView
, edit Intent
yang didefinisikan sebelumnya seperti ini:
@Override public void onItemClick(View view, int position) { Intent intent = new Intent(MainActivity.this, DetailsActivity.class); intent.putExtra(DetailsActivity.ID, Contact.CONTACTS[position].getId()); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation( // the context of the activity MainActivity.this, // For each shared element, add to this method a new Pair item, // which contains the reference of the view we are transitioning *from*, // and the value of the transitionName attribute new Pair<View, String>(view.findViewById(R.id.CONTACT_circle), getString(R.string.transition_name_circle)), new Pair<View, String>(view.findViewById(R.id.CONTACT_name), getString(R.string.transition_name_name)), new Pair<View, String>(view.findViewById(R.id.CONTACT_phone), getString(R.string.transition_name_phone)) ); ActivityCompat.startActivity(MainActivity.this, intent, options.toBundle()); }
Untuk setiap elemen bersama yang akan dianimasikan, Anda harus menambahkan metode makeSceneTransitionAnimation
pada item Pair
baru. Setiap Pair
memiliki dua nilai, yang pertama adalah referensi ke tampilan yang Anda transisikan dari, yang kedua adalah value dari atribut transitionName
.
Berhati-hatilah ketika mengimpor kelas Pair
. Anda harus menyertakan paket android.support.v4.util
, bukan paket android.util
. Juga, ingat untuk menggunakan metode ActivityCompat.startActivity
sebagai ganti metode startActivity
, karena jika tidak Anda tidak akan dapat menjalankan aplikasi Anda pada lingkungan dengan API di bawah 16.
Itu dia. Anda sudah selesai. Sesederhana itu.
Kesimpulan
Dalam tutorial ini Anda belajar bagaimana transisi yang indah dan mulus antara dua aktivitas yang berbagi satu atau lebih elemen umum, memungkinkan kontinuitas yang secara visual menyenangkan dan bermakna.
Anda memulai dengan membuat yang pertama dari dua aktivitas, yang berperan untuk menampilkan daftar kontak. Anda kemudian menyelesaikan kegiatan kedua, merancang tata letaknya, dan menerapkan cara untuk menyampaikan referensi unik antara dua kegiatan tersebut. Akhirnya, Anda melihat cara kerja makeSceneTransitionAnimation
, berkat atribut XML transitionName
.
Tip Bonus: Detail Gaya
Untuk membuat aplikasi tampilan Material Design sejati, seperti yang ditunjukkan pada screenshot sebelumnya, Anda juga perlu mengubah warna tema Anda. Edit tema dasar Anda di folder values-v21 untuk mendapatkan hasil yang bagus.
<style name=“AppTheme” parent=“AppTheme.Base”> <item name=“android:windowTitleSize”>0dp</item> <item name=“android:colorPrimary”>@color/colorPrimary</item> <item name=“android:colorPrimaryDark”>@color/colorPrimaryDark</item> <item name=“android:colorAccent”>@color/colorAccent</item> <item name=“android:textColorPrimary”>#fff</item> <item name=“android:textColor”>#727272</item> <item name=“android:navigationBarColor”>#303F9F</item> </style>