Menangani perubahan input

Chromebook menawarkan banyak opsi input yang berbeda kepada pengguna: keyboard, mouse, trackpad, layar sentuh, stilus, MIDI, dan gamepad/pengontrol biru. Artinya, perangkat yang sama dapat menjadi stasiun DJ, kanvas artis, atau platform pilihan gamer untuk game streaming AAA.

Sebagai developer, Anda memiliki peluang untuk menciptakan pengalaman aplikasi yang serbaguna dan menarik bagi pengguna yang memanfaatkan perangkat input yang sudah mereka miliki - mulai dari keyboard yang terpasang hingga stylus hingga pengontrol game Stadia. Namun, semua kemungkinan ini juga mengharuskan Anda memikirkan UI untuk membuat pengalaman aplikasi yang lancar dan logis. Hal ini terutama berlaku jika aplikasi atau game Anda didesain dengan mempertimbangkan ponsel. Misalnya, jika game Anda memiliki joystick yang dikontrol sentuhan di layar untuk ponsel, Anda mungkin ingin menyembunyikannya saat pengguna bermain dengan keyboard.

Di halaman ini, Anda akan menemukan masalah utama yang perlu diingat saat memikirkan beberapa sumber input dan strategi untuk mengatasinya.

Penemuan metode input yang didukung oleh pengguna

Idealnya, aplikasi Anda akan merespons dengan lancar input apa pun yang dipilih pengguna. Biasanya hal ini sederhana dan tidak mengharuskan Anda memberikan informasi tambahan kepada pengguna. Misalnya, tombol harus berfungsi jika pengguna mengkliknya dengan mouse, trackpad, layar sentuh, stilus, dll. dan Anda tidak perlu memberi tahu pengguna bahwa mereka dapat menggunakan perangkat yang berbeda ini untuk mengaktifkan tombol.

Namun, ada situasi ketika metode input dapat meningkatkan pengalaman pengguna dan sebaiknya beri tahu mereka bahwa aplikasi Anda mendukungnya. Beberapa contohnya:

  • Aplikasi pemutar media dapat mendukung banyak pintasan keyboard praktis yang mungkin tidak dapat ditebak dengan mudah oleh pengguna.
  • Jika Anda telah membuat aplikasi DJ, pengguna mungkin menggunakan layar sentuh pada awalnya dan mungkin tidak menyadari bahwa Anda telah mengizinkannya menggunakan keyboard/trackpad untuk memberikan akses sentuhan ke beberapa fitur. Demikian pula, mereka mungkin tidak menyadari bahwa Anda mendukung sejumlah pengontrol DJ MIDI dan mendorong mereka untuk memeriksa hardware yang didukung dapat memberikan pengalaman DJ yang lebih autentik.
  • Game Anda mungkin bagus dengan layar sentuh dan keyboard/mouse, tetapi pengguna mungkin tidak menyadari bahwa game tersebut juga mendukung sejumlah pengontrol game bluetooth. Menghubungkan salah satu perangkat ini dapat meningkatkan kepuasan dan engagement pengguna.

Anda dapat membantu pengguna menemukan opsi input dengan pesan pada waktu yang tepat. Implementasinya akan terlihat berbeda untuk setiap aplikasi. Beberapa contohnya meliputi:

  • Pop-up tip harian atau yang dijalankan pertama kali
  • Opsi konfigurasi di panel setelan dapat secara pasif menunjukkan kepada pengguna bahwa dukungan tersedia. Misalnya, tab “pengontrol game” di panel setelan game menunjukkan bahwa pengontrol didukung.
  • Pesan kontekstual. Misalnya, jika Anda mendeteksi keyboard fisik dan mendapati pengguna mengklik tindakan menggunakan mouse atau layar sentuh, Anda dapat menampilkan petunjuk bermanfaat yang menyarankan pintasan keyboard
  • Daftar pintasan keyboard. Saat keyboard fisik terdeteksi, memberikan indikasi di UI tentang cara menampilkan daftar pintasan keyboard yang tersedia memiliki tujuan ganda, yaitu mengiklankan bahwa dukungan keyboard tersedia, dan memberikan cara mudah bagi pengguna untuk melihat dan mengingat pintasan yang didukung

Pelabelan/tata letak UI untuk variasi input

Idealnya, antarmuka visual Anda tidak perlu banyak berubah jika perangkat input yang berbeda digunakan, semua kemungkinan input harus “berfungsi”. Namun, ada pengecualian penting. Dua di antaranya adalah UI khusus sentuhan dan perintah di layar.

UI khusus sentuhan

Setiap kali aplikasi Anda memiliki elemen UI khusus sentuhan, misalnya joystick di layar dalam game, Anda harus mempertimbangkan pengalaman pengguna saat sentuhan tidak digunakan. Di beberapa game seluler, sebagian besar layar digunakan oleh kontrol yang diperlukan untuk permainan berbasis sentuhan, tetapi tidak diperlukan jika pengguna menggunakan gamepad atau keyboard untuk bermain. Aplikasi atau game Anda harus menyediakan logika untuk mendeteksi metode input mana yang sedang aktif digunakan dan menyesuaikan UI yang sesuai. Lihat bagian Penerapan di bawah untuk mengetahui beberapa contoh cara melakukannya.

UI game balap mobil - satu dengan kontrol di layar dan satu dengan keyboard

Perintah di layar

Aplikasi Anda mungkin memberikan perintah di layar yang berguna bagi pengguna. Berhati-hatilah agar tidak bergantung pada perangkat input. Contoh:

  • Geser untuk…
  • Ketuk di mana saja untuk menutup
  • Cubit untuk zoom
  • Tekan ‘X’ untuk…
  • Tekan lama untuk mengaktifkan

Beberapa aplikasi mungkin dapat menyesuaikan kata-katanya agar tidak bergantung pada input. Hal ini lebih disukai jika masuk akal, tetapi dalam banyak kasus, spesifisitas penting dan Anda mungkin perlu menampilkan pesan yang berbeda bergantung pada metode input yang digunakan, terutama dalam mode seperti tutorial pada saat aplikasi dijalankan pertama kali.

Pertimbangan multibahasa

Jika aplikasi Anda mendukung beberapa bahasa, Anda harus memikirkan arsitektur string. Misalnya, jika Anda mendukung 3 mode input dan 5 bahasa, itu berarti ada 15 versi berbeda dari setiap pesan UI. Hal ini akan meningkatkan jumlah pekerjaan yang diperlukan untuk menambahkan fitur baru dan memperbesar kemungkinan kesalahan ejaan.

Salah satu pendekatan adalah menganggap tindakan antarmuka sebagai kumpulan string yang terpisah. Misalnya, jika Anda menentukan tindakan “tutup” sebagai variabel stringnya sendiri dengan varian khusus input seperti: “Ketuk di mana saja untuk menutup”, “Tekan ‘Esc’ untuk menutup”, “Klik di mana saja untuk menutup”, “Tekan tombol apa saja untuk menutup”, maka semua pesan UI yang perlu memberi tahu pengguna cara menutup sesuatu dapat menggunakan satu variabel string “tutup” ini. Saat metode input berubah, cukup ubah nilai satu variabel ini.

Input keyboard virtual / IME

Ingatlah bahwa jika pengguna menggunakan aplikasi tanpa keyboard fisik, input teks dapat dilakukan melalui keyboard virtual. Pastikan untuk menguji bahwa elemen UI yang diperlukan tidak tertutup saat keyboard virtual muncul. Lihat dokumentasi visibilitas IME Android untuk mengetahui informasi selengkapnya.

Penerapan

Dalam sebagian besar kasus, aplikasi atau game harus merespons dengan benar semua input yang valid, terlepas dari apa yang ditampilkan di layar. Jika pengguna menggunakan layar sentuh selama 10 menit, tetapi kemudian tiba-tiba beralih menggunakan keyboard, input keyboard harus berfungsi, terlepas dari perintah atau kontrol visual di layar. Dengan kata lain, fungsi harus diprioritaskan daripada visual/teks.Hal ini membantu menjamin aplikasi/game Anda dapat digunakan meskipun logika deteksi input Anda mengalami error atau situasi yang tidak terduga muncul.

Langkah selanjutnya adalah menampilkan UI yang benar untuk metode input yang sedang digunakan. Bagaimana cara mendeteksinya secara akurat? Apa yang terjadi jika pengguna beralih di antara berbagai metode input saat menggunakan aplikasi Anda? Bagaimana jika mereka menggunakan beberapa metode secara bersamaan?

Mesin status yang diprioritaskan berdasarkan peristiwa yang diterima

Salah satu pendekatan adalah melacak “status input aktif” saat ini - yang merepresentasikan perintah input yang saat ini ditampilkan di layar kepada pengguna - dengan melacak peristiwa input sebenarnya yang diterima oleh aplikasi dan melakukan transisi antar-status menggunakan logika yang diprioritaskan.

Prioritaskan

Mengapa status input harus diprioritaskan? Pengguna berinteraksi dengan perangkat mereka dengan berbagai cara dan aplikasi Anda harus mendukung pilihan mereka. Misalnya, jika pengguna memilih untuk menggunakan layar sentuh dan mouse Bluetooth secara bersamaan, hal itu harus didukung. Namun, perintah dan kontrol input di layar mana yang harus Anda tampilkan? Mouse atau sentuh?

Menentukan setiap kumpulan perintah sebagai “status input”, lalu memprioritaskannya dapat membantu memutuskan hal ini secara konsisten.

Peristiwa input yang diterima menentukan status

Mengapa hanya bertindak pada peristiwa input yang diterima? Anda mungkin berpikir bahwa Anda dapat melacak koneksi bluetooth untuk menunjukkan apakah pengontrol bluetooth terpasang atau memantau koneksi USB untuk perangkat USB. Pendekatan ini tidak direkomendasikan karena dua alasan utama.

Pertama-tama, informasi yang dapat Anda tebak tentang perangkat terhubung berdasarkan variabel API tidak konsisten dan jumlah perangkat bluetooth/hardware/stylus terus bertambah.

Alasan kedua untuk menggunakan peristiwa yang diterima, bukan status koneksi, adalah karena pengguna mungkin memiliki mouse, pengontrol Bluetooth, pengontrol MIDI, dll. yang terhubung, tetapi tidak menggunakannya secara aktif untuk berinteraksi dengan aplikasi atau game Anda.

Dengan merespons peristiwa input yang telah diterima secara aktif oleh aplikasi Anda, Anda memastikan bahwa Anda merespons tindakan nyata pengguna secara real-time, dan tidak mencoba menebak niat mereka dengan informasi yang tidak lengkap.

Contoh: game dengan dukungan sentuh, keyboard/mouse, dan pengontrol

Bayangkan Anda telah mengembangkan game balap mobil untuk ponsel berbasis sentuhan. Pemain dapat berakselerasi, melambat, berbelok ke kanan, berbelok ke kiri, atau menggunakan nitro untuk meningkatkan kecepatan.

Antarmuka layar sentuh saat ini terdiri dari joystick di layar di kiri bawah layar untuk kontrol kecepatan dan arah, serta tombol di kanan bawah untuk nitro. Pengguna dapat mengumpulkan tabung nitro di lintasan dan saat batang nitro di bagian bawah layar penuh, pesan “Tekan untuk Nitro!” akan muncul di atas tombol. Jika ini adalah game pertama pengguna atau tidak ada input yang diterima selama beberapa waktu, teks “tutorial” akan muncul di atas joystick yang menunjukkan cara menggerakkan mobil kepada pengguna.

Anda ingin menambahkan dukungan keyboard dan pengontrol game bluetooth. Dari mana Anda memulai?

Game balap mobil dengan kontrol sentuh

Status input

Mulailah dengan mengidentifikasi semua status input yang mungkin dijalankan game Anda, lalu mencantumkan semua parameter yang ingin Anda ubah di setiap status.

                                                                                                                                                                        
SentuhKeyboard/MousePengontrol Game
       

Bereaksi terhadap

     
       

Semua input

     
       

Semua input

     
       

Semua input

     
       

Kontrol di layar

     
       

- Joystick virtual
- Tombol Nitro

     
       

- Tanpa joystick
- Tanpa tombol nitro

     
       

- Tanpa joystick
- Tanpa tombol nitro

     
       

Teks

     
       

Ketuk untuk Nitro!

     
       

Tekan "N" untuk Nitro!

     
       

Tekan "A" untuk Nitro!

     
       

Tutorial

     
       

Gambar joystick untuk kecepatan/arah

     
       

Gambar tombol panah dan WASD untuk kecepatan/arah

     
       

Gambar gamepad untuk kecepatan/arah

     

Pantau status “input aktif”, lalu perbarui UI sesuai kebutuhan, berdasarkan status tersebut.

Catatan: Ingat: game/aplikasi Anda harus merespons semua metode input, apa pun statusnya. Misalnya, jika pengguna mengemudikan mobil dengan layar sentuh, tetapi menekan N di keyboard - tindakan nitro harus dipicu.

Perubahan status yang diprioritaskan

Beberapa pengguna dapat menggunakan input keyboard dan layar sentuh secara bersamaan. Beberapa orang mungkin mulai menggunakan game/aplikasi Anda di sofa dalam mode tablet, lalu beralih menggunakan keyboard di meja. Pemain lain dapat menghubungkan atau memutuskan koneksi pengontrol game di tengah permainan.

Idealnya, Anda tidak ingin memiliki elemen UI yang salah - seperti memberi tahu pengguna untuk menekan tombol n saat mereka menggunakan layar sentuh. Pada saat yang sama, jika pengguna menggunakan beberapa perangkat input secara bersamaan, seperti layar sentuh dan keyboard, Anda tidak ingin UI terus-menerus beralih di antara kedua status tersebut.

Salah satu cara untuk menanganinya adalah dengan menetapkan prioritas jenis input, dan membuat penundaan di antara perubahan status. Untuk game mobil di atas, Anda harus selalu memastikan joystick di layar terlihat setiap kali peristiwa sentuhan layar diterima, jika tidak, game mungkin tampak tidak dapat digunakan oleh pengguna. Tindakan ini akan menjadikan layar sentuh sebagai perangkat dengan prioritas tertinggi.

Jika peristiwa keyboard dan layar sentuh diterima secara bersamaan, game harus tetap dalam mode layar sentuh - meskipun masih bereaksi terhadap input keyboard. Jika tidak ada input layar sentuh yang diterima setelah 5 detik dan peristiwa keyboard masih diterima, mungkin kontrol di layar akan memudar dan game akan beralih ke status keyboard.

Input pengontrol game akan mengikuti pola yang serupa: status UI pengontrol akan diberi prioritas yang lebih rendah daripada keyboard/mouse dan sentuhan, serta hanya muncul jika input pengontrol game, bukan bentuk input lainnya, diterima. Game akan selalu merespons input pengontrol.

Berikut adalah diagram status dan tabel transisi untuk contoh. Sesuaikan ide dengan aplikasi atau game Anda.

Mesin status yang diprioritaskan - layar sentuh, keyboard/mouse, pengontrol game

                                                                                                                                        
#1 Layar sentuh#2 Keyboard#3 Gamepad
       

Pindahkan ke #1

     
       

T/A

     
       

- Input sentuh diterima
- Segera beralih ke status Input sentuh

     
       

- Input sentuh diterima
- Segera beralih ke status Input sentuh

     
       

Pindah ke #2

     
       

- Tidak ada sentuhan selama 5 detik
- Input keyboard diterima
- Pindah ke status Input keyboard

     
       

T/A

     
       

- Input keyboard diterima
(segera beralih ke status Input keyboard)

     
       

Pindah ke #3

     
       

- Tidak ada sentuhan selama 5 detik
- Tidak ada input keyboard selama 5 detik
- Input gamepad diterima
- Pindah ke status input Gamepad

     
       

- Tidak ada keyboard selama 5 detik
- Input gamepad diterima
- Pindah ke status input gamepad

     
       

T/A

     

Catatan: Perhatikan bagaimana prioritas membantu memperjelas jenis input mana yang harus dominan. Status input langsung bergerak “naik” dalam prioritas:

3. Gamepad -> 2. Keyboard -> 1. Sentuh

segera setelah perangkat dengan prioritas lebih tinggi digunakan, tetapi prioritasnya perlahan bergerak “turun”, hanya setelah periode penundaan dan hanya jika perangkat dengan prioritas lebih rendah sedang aktif digunakan.

Peristiwa input

Berikut adalah beberapa contoh kode cara mendeteksi peristiwa input dari berbagai jenis perangkat input menggunakan Android API standar. Gunakan peristiwa ini untuk menggerakkan mesin status Anda, seperti di atas. Anda harus menyesuaikan konsep umum dengan jenis peristiwa input yang Anda harapkan dan dengan aplikasi atau game Anda.

Tombol keyboard dan pengontrol

// Drive the state machine based on the received input type
// onKeyDown drives the state machine, but does not trigger game actions
// Both keyboard and game controller events come through as key events
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    if (event != null) {
        // Check input source
        val outputMessage = when (event.source) {
            SOURCE_KEYBOARD -> {
                MyStateMachine.KeyboardEventReceived()
                "Keyboard event"
            }
            SOURCE_GAMEPAD -> {
                MyStateMachine.ControllerEventReceived()
                "Game controller event"
            }
            else -> "Other key event: ${event.source}"
        }
        // Do something based on source type
        findViewById(R.id.text_message).text = outputMessage
    }
    // Pass event up to system
    return super.onKeyDown(keyCode, event)
}
// Trigger game events based on key release
// Both keyboard and game controller events come through as key events
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
   when(keyCode) {
       KeyEvent.KEYCODE_N -> {
           MyStateMachine.keyboardEventReceived()
           engageNitro()
           return true // event handled here, return true
       }
   }
   // If event not handled, pass up to system
   return super.onKeyUp(keyCode, event)
}

Catatan: untuk KeyEvents, Anda dapat memilih untuk menggunakan onKeyDown() atau onKeyUp(). Di sini, onKeyDown() digunakan untuk mengontrol mesin status, sedangkan onKeyUp() digunakan untuk memicu peristiwa game.

Jika pengguna menekan dan menahan tombol, onKeyUp() hanya akan dipicu satu kali per penekanan tombol, sedangkan onKeyDown() akan dipanggil beberapa kali. Jika ingin bereaksi terhadap penekanan ke bawah, Anda harus menangani peristiwa game di onKeyDown() dan menerapkan logika untuk mengatasi peristiwa berulang. Lihat dokumentasi Menangani Tindakan Keyboard untuk mengetahui info selengkapnya.

Sentuhan dan Stilus

// Touch and stylus events come through as touch events
override fun onTouchEvent(event: MotionEvent?): Boolean {
   if (event != null) {
       // Get tool type
       val pointerIndex = event.action and ACTION_POINTER_INDEX_MASK shr ACTION_POINTER_INDEX_SHIFT
       val toolType = event.getToolType(pointerIndex)

       // Check tool type
       val outputMessage = when (toolType) {
           TOOL_TYPE_FINGER -> {
               MyStateMachine.TouchEventReceived()
               "Touch event"
           }
           TOOL_TYPE_STYLUS -> {
                MyStateMachine.StylusEventReceived()
               "Stylus event"
           }
           else -> "Other touch event: ${toolType}"
       }

       // Do something based on tool type, return true if event handled
       findViewById(R.id.text_message).text = outputMessage
   }
   // If event not handled, pass up to system
   return super.onGenericMotionEvent(event)
}

Mouse dan joystick

// Mouse and joystick events come through as generic events
override fun onGenericMotionEvent(event: MotionEvent?): Boolean {
   if (event != null) {
       // Check input source
       val outputMessage = when (event.source) {
           SOURCE_JOYSTICK -> {
                MyStateMachine.ControllerEventReceived()
               "Controller event"
           }
           SOURCE_MOUSE -> {
                MyStateMachine.MouseEventReceived()
               "Mouse event"
           }
           else -> "Other generic event: ${event.source}"
       }
       // Do something based on source type, return true if event handled
       findViewById(R.id.text_message).text = outputMessage
   }
   // If event not handled, pass up to system
   return super.onGenericMotionEvent(event)
}