Membuat tampilan kustom menjadi interaktif

Coba cara Compose
Jetpack Compose adalah toolkit UI yang direkomendasikan untuk Android. Pelajari cara menggunakan tata letak di Compose.

Menggambar UI hanyalah satu bagian dari membuat tampilan kustom. Anda juga harus membuat tampilan merespons input pengguna sedemikian rupa sehingga sangat mirip dengan tindakan sebenarnya yang dapat Anda tiru.

Buat objek dalam aplikasi Anda berfungsi seperti objek yang sebenarnya. Misalnya, jangan biarkan gambar di aplikasi Anda muncul dan muncul kembali di tempat lain, karena objek di dunia nyata tidak akan seperti itu. Sebagai gantinya, pindahkan gambar dari satu tempat ke tempat lain.

Pengguna merasakan perilaku atau nuansa yang halus dalam antarmuka dan bereaksi paling baik terhadap hal-hal kecil yang meniru dunia nyata. Misalnya, saat pengguna melemparkan objek UI, beri mereka rasa inersia di awal yang menunda gerakan. Di akhir gerakan, beri mereka momentum yang membawa objek melampaui ayunan tersebut.

Halaman ini menunjukkan cara menggunakan fitur framework Android untuk menambahkan perilaku nyata ini ke tampilan kustom Anda.

Anda dapat menemukan informasi terkait tambahan di Ringkasan peristiwa input dan Ringkasan animasi properti.

Menangani gestur input

Seperti banyak framework UI lainnya, Android mendukung model peristiwa input. Tindakan pengguna berubah menjadi peristiwa yang memicu callback, dan Anda dapat mengganti callback untuk menyesuaikan cara aplikasi merespons pengguna. Peristiwa input yang paling umum dalam sistem Android adalah sentuh, yang memicu onTouchEvent(android.view.MotionEvent). Ganti metode ini untuk menangani peristiwa, seperti 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 tidak terlalu berguna. UI sentuh modern menentukan interaksi dalam hal gestur seperti mengetuk, menarik, mendorong, melempar, dan memperbesar/memperkecil. Untuk mengonversi peristiwa sentuh mentah 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 berikut 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());

Baik 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 GestureDetector.SimpleOnGestureListener, sistem akan menganggap Anda ingin mengabaikan seluruh gestur, dan metode lain dari GestureDetector.OnGestureListener 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 menafsirkan 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 onTouchEvent() peristiwa sentuh yang tidak dikenali sebagai bagian dari gestur, peristiwa tersebut akan menampilkan false. Selanjutnya, Anda dapat menjalankan kode deteksi gestur kustom Anda sendiri.

Buat gerakan yang masuk akal secara fisik

Gestur adalah cara efektif untuk mengontrol perangkat layar sentuh, tetapi kontra-intuitif dan sulit diingat kecuali jika memberikan hasil yang masuk akal secara fisik.

Misalnya, Anda ingin mengimplementasikan gestur ayunkan jari horizontal yang menyetel item yang digambar dalam tampilan yang berputar di sekitar sumbu vertikalnya. Gestur ini akan masuk akal jika UI merespons dengan bergerak cepat ke arah lemparan, lalu melambat, seolah-olah pengguna mendorong roda inovasi dan membuatnya berputar.

Dokumentasi tentang cara menganimasikan gestur scroll memberikan penjelasan mendetail tentang cara menerapkan perilaku scoll Anda sendiri. Namun, menyimulasikan nuansa roda inovasi bukanlah hal yang mudah. Banyak fisika dan matematika diperlukan untuk membuat model roda inovasi berfungsi dengan benar. Untungnya, Android menyediakan class helper untuk menyimulasikan hal ini dan perilaku lainnya. Class Scroller adalah dasar untuk menangani gestur ayunkan jari bergaya roda inovasi.

Untuk memulai fling, panggil fling() dengan kecepatan awal serta nilai x dan y minimum dan maksimum dari fling 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 menyiapkan model fisika untuk gestur ayunkan jari. Setelah itu, update 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 tersebut. 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 rotasi 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 cukup sering agar animasi scroll terlihat halus. Ada dua cara untuk melakukannya:

  • Paksa penggambaran ulang dengan memanggil postInvalidate() setelah memanggil fling(). Teknik ini mengharuskan Anda menghitung offset scroll di onDraw() dan memanggil postInvalidate() setiap kali offset scroll berubah.
  • Siapkan ValueAnimator untuk menganimasikan selama ayunan jari dan tambahkan pemroses untuk memproses update animasi dengan memanggil addUpdateListener(). Teknik ini memungkinkan Anda menganimasikan properti View.

Buat transisi yang lancar

Pengguna mengharapkan UI modern bertransisi dengan lancar antar-status: elemen UI memudar dan keluar, bukan muncul dan menghilang, serta gerakan dimulai dan berakhir dengan lancar, bukan memulai dan berhenti tiba-tiba. Framework animasi properti Android membuat transisi yang lancar lebih mudah.

Untuk menggunakan sistem animasi, setiap kali properti berubah yang memengaruhi tampilan tampilan Anda, jangan mengubah properti secara langsung. Sebagai gantinya, gunakan ValueAnimator untuk melakukan perubahan. Pada contoh berikut, mengubah komponen turunan yang dipilih dalam tampilan akan membuat seluruh tampilan yang dirender berputar sehingga pointer pemilihan dipusatkan. ValueAnimator mengubah rotasi selama periode beberapa ratus milidetik, bukan langsung menetapkan nilai rotasi 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, melakukan animasi akan menjadi 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();