Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Melacak sentuhan dan gerakan pointer

Tutorial ini menjelaskan cara melacak gerakan dalam peristiwa sentuhan.

onTouchEvent() baru akan dipicu oleh peristiwa ACTION_MOVE setiap kali posisi, tekanan, atau ukuran kontak sentuh saat ini berubah. Semua peristiwa tersebut dicatat dalam parameter MotionEvent onTouchEvent(), seperti yang dijelaskan dalam Mendeteksi Gestur Umum.

Karena sentuhan berbasis jari tidak selalu menjadi bentuk interaksi yang paling tepat, deteksi peristiwa sentuhan seringkali lebih didasarkan pada gerakan daripada kontak sederhana. Untuk membantu aplikasi membedakan gestur berbasis gerakan (seperti geser) dengan gestur non-gerakan (seperti satu ketukan), Android menyertakan gagasan "touch slop". Touch slop merujuk pada jarak dalam piksel yang dapat ditelusuri dengan sentuhan pengguna sebelum gestur diartikan sebagai gestur berbasis gerakan. Untuk diskusi lebih lanjut tentang topik ini, lihat Mengelola Peristiwa Sentuhan dalam ViewGroup.

Ada beberapa cara untuk melacak gerakan dalam gestur, bergantung pada kebutuhan aplikasi Anda. Contoh:

  • Posisi awal dan akhir pointer (misalnya, memindahkan objek di layar dari titik A ke titik B).
  • Arah pointer berjalan, seperti yang ditentukan oleh koordinat x dan y.
  • Histori. Anda dapat menemukan ukuran histori gestur dengan memanggil MotionEvent metode getHistorySize(). Kemudian Anda dapat memperoleh posisi, ukuran, waktu, dan tekanan dari masing-masing peristiwa historis menggunakan metode getHistorical<Value> peristiwa motion. Histori akan berguna saat melakukan proses rendering pada jejak jari pengguna, seperti untuk menggambar dengan sentuhan. Lihat referensi MotionEvent untuk detailnya.
  • Kecepatan pointer saat bergerak di atas layar sentuh.

Lihat referensi terkait berikut:

Melacak kecepatan

Anda dapat membuat gestur berbasis gerakan yang hany didasarkan pada jarak dan/atau arah yang dilalui pointer. Tetapi, kecepatan sering menjadi faktor penentu dalam melacak karakteristik gestur atau bahkan memutuskan apakah gestur itu terjadi. Untuk mempermudah penghitungan kecepatan, Android menyediakan class VelocityTracker. VelocityTracker membantu Anda melacak kecepatan peristiwa sentuhan. Hal ini berguna untuk gestur di mana kecepatan menjadi bagian dari kriteria gestur tersebut, seperti melempar.

Berikut adalah contoh sederhana yang menggambarkan tujuan metode dalam API VelocityTracker:

Kotlin

    private const val DEBUG_TAG = "Velocity"

    class MainActivity : Activity() {
        private var mVelocityTracker: VelocityTracker? = null

        override fun onTouchEvent(event: MotionEvent): Boolean {

            when (event.actionMasked) {
                MotionEvent.ACTION_DOWN -> {
                    // Reset the velocity tracker back to its initial state.
                    mVelocityTracker?.clear()
                    // If necessary retrieve a new VelocityTracker object to watch the
                    // velocity of a motion.
                    mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain()
                    // Add a user's movement to the tracker.
                    mVelocityTracker?.addMovement(event)
                }
                MotionEvent.ACTION_MOVE -> {
                    mVelocityTracker?.apply {
                        val pointerId: Int = event.getPointerId(event.actionIndex)
                        addMovement(event)
                        // When you want to determine the velocity, call
                        // computeCurrentVelocity(). Then call getXVelocity()
                        // and getYVelocity() to retrieve the velocity for each pointer ID.
                        computeCurrentVelocity(1000)
                        // Log velocity of pixels per second
                        // Best practice to use VelocityTrackerCompat where possible.
                        Log.d("", "X velocity: ${getXVelocity(pointerId)}")
                        Log.d("", "Y velocity: ${getYVelocity(pointerId)}")
                    }
                }
                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                    // Return a VelocityTracker object back to be re-used by others.
                    mVelocityTracker?.recycle()
                    mVelocityTracker = null
                }
            }
            return true
        }
    }
    

Java

    public class MainActivity extends Activity {
        private static final String DEBUG_TAG = "Velocity";
            ...
        private VelocityTracker mVelocityTracker = null;
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int index = event.getActionIndex();
            int action = event.getActionMasked();
            int pointerId = event.getPointerId(index);

            switch(action) {
                case MotionEvent.ACTION_DOWN:
                    if(mVelocityTracker == null) {
                        // Retrieve a new VelocityTracker object to watch the
                        // velocity of a motion.
                        mVelocityTracker = VelocityTracker.obtain();
                    }
                    else {
                        // Reset the velocity tracker back to its initial state.
                        mVelocityTracker.clear();
                    }
                    // Add a user's movement to the tracker.
                    mVelocityTracker.addMovement(event);
                    break;
                case MotionEvent.ACTION_MOVE:
                    mVelocityTracker.addMovement(event);
                    // When you want to determine the velocity, call
                    // computeCurrentVelocity(). Then call getXVelocity()
                    // and getYVelocity() to retrieve the velocity for each pointer ID.
                    mVelocityTracker.computeCurrentVelocity(1000);
                    // Log velocity of pixels per second
                    // Best practice to use VelocityTrackerCompat where possible.
                    Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId));
                    Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId));
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    // Return a VelocityTracker object back to be re-used by others.
                    mVelocityTracker.recycle();
                    break;
            }
            return true;
        }
    }
    

Menggunakan rekaman pointer

Beberapa aplikasi, seperti game, desktop jarak jauh, dan klien virtualisasi, sangat diuntungkan karena dapat dikontrol melalui pointer mouse. Rekaman pointer adalah fitur yang tersedia di Android 8.0 (API level 26) dan versi lebih baru yang menyediakan kontrol tersebut dengan mengirimkan semua peristiwa mouse ke tampilan fokus di aplikasi Anda.

Meminta rekaman pointer

Tampilan di aplikasi Anda dapat meminta rekaman pointer hanya saat hierarki tampilan yang memuatnya memiliki fokus. Karena alasan ini, Anda harus meminta rekaman pointer saat ada tindakan pengguna tertentu yang sedang ditampilkan, seperti selama peristiwa onClick(), atau pada pengendali peristiwa onWindowFocusChanged() aktivitas Anda.

Untuk meminta rekaman pointer, panggil metode requestPointerCapture() yang sedang ditampilkan. Contoh kode berikut menunjukkan cara meminta rekaman pointer saat pengguna mengklik tampilan:

Kotlin

    fun onClick(view: View) {
        view.requestPointerCapture()
    }
    

Java

    @Override
    public void onClick(View view) {
        view.requestPointerCapture();
    }
    

Jika permintaan untuk merekam pointer berhasil, Android akan memanggil onPointerCaptureChange(true). Sistem akan mengirimkan peristiwa mouse ke tampilan fokus di aplikasi Anda selama berada dalam hierarki tampilan yang sama dengan tampilan yang meminta rekaman. Aplikasi lain akan berhenti menerima peristiwa mouse hingga rekaman dirilis, termasuk peristiwa ACTION_OUTSIDE. Android akan mengirimkan peristiwa pointer dari sumber selain mouse secara normal, tetapi pointer mouse tidak akan terlihat lagi.

Menangani peristiwa pointer yang direkam

Setelah tampilan berhasil memperoleh rekaman pointer, Android akan mulai mengirimkan peristiwa mouse. Tampilan fokus Anda dapat menangani peristiwa dengan melakukan salah satu tugas berikut:

  1. Jika Anda menggunakan tampilan kustom, ganti onCapturedPointerEvent(MotionEvent).
  2. Jika tidak, daftarkan OnCapturedPointerListener.

Contoh kode berikut menunjukkan cara menerapkan onCapturedPointerEvent(MotionEvent):

Kotlin

    override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean {
        // Get the coordinates required by your app
        val verticalOffset: Float = motionEvent.y
        // Use the coordinates to update your view and return true if the event was
        // successfully processed
        return true
    }
    

Java

    @Override
    public boolean onCapturedPointerEvent(MotionEvent motionEvent) {
      // Get the coordinates required by your app
      float verticalOffset = motionEvent.getY();
      // Use the coordinates to update your view and return true if the event was
      // successfully processed
      return true;
    }
    

Contoh kode berikut menunjukkan cara mendaftarkan OnCapturedPointerListener:

Kotlin

    myView.setOnCapturedPointerListener { view, motionEvent ->
        // Get the coordinates required by your app
        val horizontalOffset: Float = motionEvent.x
        // Use the coordinates to update your view and return true if the event was
        // successfully processed
        true
    }
    

Java

    myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
      @Override
      public boolean onCapturedPointer (View view, MotionEvent motionEvent) {
        // Get the coordinates required by your app
        float horizontalOffset = motionEvent.getX();
        // Use the coordinates to update your view and return true if the event was
        // successfully processed
        return true;
      }
    });
    

Baik menggunakan tampilan kustom maupun mendaftarkan pemroses, tampilan Anda akan menerima MotionEvent dengan koordinat pointer yang menentukan gerakan relatif seperti delta X/Y, mirip dengan koordinat yang dikirimkan oleh perangkat trackball. Anda dapat mengambil koordinat menggunakan getX() dan getY().

Merilis rekaman pointer

Tampilan di aplikasi Anda dapat merilis rekaman pointer dengan memanggil releasePointerCapture(), seperti yang ditunjukkan dalam contoh kode berikut:

Kotlin

    override fun onClick(view: View) {
        view.releasePointerCapture()
    }
    

Java

    @Override
    public void onClick(View view) {
        view.releasePointerCapture();
    }
    

Sistem dapat menghapus rekaman dari tampilan tanpa Anda harus secara eksplisit memanggil releasePointerCapture(), yang umumnya disebabkan karena hierarki tampilan yang berisi tampilan yang diminta rekamannya telah kehilangan fokus.