Menggambar UI hanyalah satu bagian dari membuat tampilan kustom. Anda juga perlu menjadikan tampilan Anda responsif terhadap input pengguna dengan cara yang sangat mirip dengan tindakan sebenarnya yang Anda tirukan.
Buat objek di aplikasi Anda bertindak seperti objek sebenarnya. Misalnya, jangan biarkan gambar di aplikasi Anda menghilang dan muncul kembali di tempat lain, karena objek di dunia nyata tidak melakukan hal tersebut. Sebagai gantinya, pindahkan gambar Anda dari satu tempat ke tempat lain.
Pengguna merasakan perilaku atau perasaan halus dalam suatu antarmuka dan bereaksi paling baik terhadap kehalusan yang menyerupai penggunaan sebenarnya. Misalnya, saat pengguna melempar objek UI, berikan mereka rasa inersia di awal yang menunda gerakan. Di akhir gerakan, berikan mereka rasa momentum yang membawa objek melampaui lemparan tersebut.
Halaman ini menjelaskan cara menggunakan fitur-fitur framework Android untuk menambahkan perilaku dalam pemakaian sebenarnya ini ke tampilan kustom Anda.
Anda dapat menemukan informasi terkait lainnya di Ringkasan peristiwa input dan Ringkasan animasi properti overview.
Menangani gestur input
Seperti banyak framework UI lainnya, Android mendukung model peristiwa input. Tindakan pengguna diubah menjadi peristiwa yang memicu callback, dan Anda dapat mengganti callback untuk menyesuaikan respons aplikasi terhadap pengguna. Peristiwa input yang paling umum di sistem Android adalah sentuh, yang memicu
onTouchEvent(android.view.MotionEvent).
Ganti metode ini untuk menangani peristiwa, sebagai berikut:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
Peristiwa sentuh sendiri sebenarnya tidak terlalu berguna. UI sentuh modern menentukan interaksi dalam bentuk gestur seperti mengetuk, menarik, mendorong, melempar, dan memperbesar/memperkecil. Untuk mengonversi peristiwa sentuh dasar menjadi gestur, Android menyediakan GestureDetector.
Buat GestureDetector dengan meneruskan instance class
yang mengimplementasikan
GestureDetector.OnGestureListener.
Jika hanya ingin memproses beberapa gestur, Anda dapat memperluas GestureDetector.SimpleOnGestureListener, bukan mengimplementasikan antarmuka GestureDetector.OnGestureListener. Misalnya, kode ini membuat class yang memperluas GestureDetector.SimpleOnGestureListener dan mengganti onDown(MotionEvent).
Kotlin
private val myListener = object : GestureDetector.SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } } private val detector: GestureDetector = GestureDetector(context, myListener)
Java
class MyListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } detector = new GestureDetector(getContext(), new MyListener());
Terlepas dari apakah Anda menggunakan GestureDetector.SimpleOnGestureListener atau tidak, selalu implementasikan metode onDown() yang menampilkan true. Hal ini diperlukan karena semua gestur dimulai dengan pesan onDown(). Jika Anda menampilkan false dari onDown(), seperti yang dilakukan GestureDetector.SimpleOnGestureListener, sistem akan menganggap Anda ingin mengabaikan gestur lainnya, dan metode GestureDetector.OnGestureListener lainnya tidak akan dipanggil. Hanya tampilkan false dari onDown() jika Anda ingin mengabaikan seluruh gestur.
Setelah mengimplementasikan GestureDetector.OnGestureListener dan membuat
instance GestureDetector, Anda dapat menggunakan
GestureDetector untuk menginterpretasikan peristiwa sentuh yang Anda terima di
onTouchEvent().
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return detector.onTouchEvent(event).let { result -> if (!result) { if (event.action == MotionEvent.ACTION_UP) { stopScrolling() true } else false } else true } }
Java
@Override public boolean onTouchEvent(MotionEvent event) { boolean result = detector.onTouchEvent(event); if (!result) { if (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = true; } } return result; }
Jika Anda meneruskan peristiwa sentuh yang tidak dikenali sebagai bagian dari gestur ke onTouchEvent(), tindakan itu akan menampilkan false. Selanjutnya Anda dapat menjalankan kode deteksi gestur kustom Anda sendiri.
Membuat gerakan yang masuk akal secara fisika
Meskipun efektif untuk mengontrol perangkat layar sentuh, gestur dapat kontra-intuitif dan sulit diingat kecuali jika gestur tersebut memberikan hasil yang masuk akal secara fisika.
Misalnya, Anda ingin mengimplementasikan gestur lempar horizontal yang membuat item yang digambar dalam tampilan berputar di sekitar sumbu vertikalnya. Gerakan ini terasa natural jika UI merespons dengan bergerak cepat searah lemparan, lalu melambat, seolah-olah pengguna mendorong flywheel dan membuatnya berputar.
Dokumentasi tentang cara
menganimasikan gestur scroll memberikan penjelasan mendetail tentang cara mengimplementasikan perilaku scroll Anda sendiri. Namun, menyimulasikan nuansa putaran flywheel bukanlah tugas sepele. Perlu banyak kalkulasi fisika dan matematika untuk membuat model flywheel berfungsi dengan benar. Untungnya, Android menyediakan class helper untuk menyimulasikan hal ini dan perilaku lainnya. Class Scroller adalah dasar untuk menangani gestur lempar gaya flywheel.
Untuk memulai gestur lempar, panggil fling() dengan menetapkan nilai kecepatan awal serta nilai x - y minimum dan maksimum untuk lemparan tersebut. Untuk nilai kecepatan, Anda dapat menggunakan nilai yang dihitung oleh GestureDetector.
Kotlin
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { scroller.fling( currentX, currentY, (velocityX / SCALE).toInt(), (velocityY / SCALE).toInt(), minX, minY, maxX, maxY ) postInvalidate() return true }
Java
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); return true; }
Panggilan ke fling() akan mengatur model fisika untuk gestur lempar tersebut. Setelah itu, perbarui Scroller dengan memanggil
Scroller.computeScrollOffset()
secara berkala. computeScrollOffset() memperbarui status internal objek Scroller dengan membaca waktu saat ini dan menggunakan model fisika untuk menghitung posisi x dan y pada waktu itu. Panggil getCurrX() dan getCurrY() untuk mengambil nilai ini.
Sebagian besar tampilan meneruskan posisi x dan y objek Scroller langsung ke scrollTo().
Contoh ini sedikit berbeda: contoh ini menggunakan posisi x scroll saat ini untuk menetapkan sudut putaran tampilan.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
Class Scroller menghitung posisi scroll secara otomatis, tetapi tidak otomatis menerapkan posisi tersebut ke tampilan Anda. Terapkan koordinat baru sesering mungkin agar animasi scrolling terlihat halus. Ada dua cara untuk melakukannya:
- Paksa penarikan ulang dengan memanggil
postInvalidate()setelah memanggilfling(). Teknik ini mengharuskan Anda menghitung offset scroll dionDraw()dan memanggilpostInvalidate()setiap kali offset scroll berubah. - Siapkan
ValueAnimatoruntuk menganimasikan durasi lemparan dan tambahkan pemroses untuk memproses update animasi dengan memanggiladdUpdateListener(). Teknik ini memungkinkan Anda menganimasikan properti aView.
Menciptakan transisi yang lancar
Pengguna mengharapkan UI modern bertransisi dengan lancar dari satu keadaan ke keadaan yang lain: elemen UI melakukan fade-in dan fade-out, bukannya muncul dan menghilang, serta gerakan dimulai dan diakhiri dengan lancar, bukannya dimulai dan berhenti secara tiba-tiba. Framework animasi properti Android memudahkan transisi yang lancar.
Untuk menggunakan sistem animasi ini, setiap kali properti berubah yang memengaruhi gaya tampilan, jangan ubah properti secara langsung. Sebagai gantinya, gunakan ValueAnimator untuk melakukan perubahan. Dalam contoh berikut, memodifikasi komponen turunan yang dipilih dalam tampilan akan membuat seluruh tampilan yang dirender berputar sehingga penunjuk pilihan berada di tengah.
ValueAnimator mengubah putaran dalam rentang waktu beberapa ratus milidetik, bukan langsung menetapkan nilai putaran baru.
Kotlin
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply { setIntValues(targetAngle) duration = AUTOCENTER_ANIM_DURATION start() }
Java
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0); autoCenterAnimator.setIntValues(targetAngle); autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); autoCenterAnimator.start();
Jika nilai yang ingin Anda ubah adalah salah satu properti View
dasar, animasi dapat dijalankan lebih mudah lagi, karena tampilan memiliki
ViewPropertyAnimator
bawaan yang dioptimalkan untuk animasi simultan beberapa properti, seperti dalam
contoh berikut:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();