Membuat tampilan kustom menjadi interaktif

Mencoba 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 dengan cara yang sangat mirip dengan tindakan nyata yang Anda tiru.

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

Pengguna merasakan bahkan perilaku atau perasaan halus di antarmuka dan bereaksi paling baik terhadap kehalusan yang meniru dunia nyata. Misalnya, saat pengguna melemparkan objek UI, berikan kesan inersia di awal yang menunda gerakan. Di akhir gerakan, beri pengguna kesan momentum yang membawa objek melampaui lemparan tersebut.

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

Anda dapat menemukan informasi terkait lainnya 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 Anda merespons pengguna. Peristiwa input yang paling umum dalam 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 itu sendiri tidak terlalu berguna. UI sentuh modern menentukan interaksi dalam hal gestur seperti mengetuk, menarik, mendorong, mengayunkan, dan zoom. 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 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());

Baik Anda menggunakan GestureDetector.SimpleOnGestureListener atau tidak, selalu terapkan metode onDown() yang menampilkan true. 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 GestureDetector.OnGestureListener tidak akan dipanggil. Hanya tampilkan false dari onDown() jika Anda ingin mengabaikan seluruh gestur.

Setelah menerapkan GestureDetector.OnGestureListener dan membuat instance GestureDetector, Anda dapat menggunakan GestureDetector untuk menafsirkan peristiwa sentuh yang diterima 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;
}

Saat Anda meneruskan onTouchEvent() peristiwa sentuh yang tidak dikenali sebagai bagian dari gestur, false akan ditampilkan. Anda kemudian dapat menjalankan kode deteksi gestur kustom Anda sendiri.

Membuat gerakan yang masuk akal secara fisik

Gestur adalah cara ampuh untuk mengontrol perangkat layar sentuh, tetapi mungkin 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 digambar dalam tampilan yang berputar di sekitar sumbu vertikalnya. Gestur ini masuk akal jika UI merespons dengan bergerak cepat ke arah ayunan, lalu melambat, seolah-olah pengguna mendorong flywheel dan memutarnya.

Dokumentasi tentang cara menganimasikan gestur scroll memberikan penjelasan mendetail tentang cara menerapkan perilaku scoll Anda sendiri. Namun, menyimulasikan nuansa roda inovasi bukanlah hal yang sepele. 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 untuk 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 saat 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: mode ini menggunakan posisi scroll x saat ini untuk menyetel 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. Menerapkan koordinat baru cukup sering agar animasi scroll terlihat lancar. Ada dua cara untuk melakukannya:

  • Paksa gambar 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 durasi ayunan dan tambahkan pemroses untuk memproses pembaruan animasi dengan memanggil addUpdateListener(). Teknik ini memungkinkan Anda menganimasikan properti View.

Buat transisi Anda lancar

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

Untuk menggunakan sistem animasi, setiap kali properti mengubah hal yang memengaruhi tampilan tampilan, jangan ubah properti secara langsung. Sebagai gantinya, gunakan ValueAnimator untuk melakukan perubahan. Dalam contoh berikut, mengubah komponen turunan yang dipilih dalam tampilan akan membuat seluruh tampilan yang dirender diputar sehingga pointer pemilihan berada di tengah. ValueAnimator mengubah rotasi selama 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 jauh lebih mudah, karena tampilan memiliki ViewPropertyAnimator bawaan yang dioptimalkan untuk animasi simultan beberapa properti, seperti pada contoh berikut:

Kotlin

animate()
    .rotation(targetAngle)
    .duration = ANIM_DURATION
    .start()

Java

animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();