Mengembangkan aplikasi

Materi berikut ditujukan untuk developer aplikasi.

Agar aplikasi mendukung rotary, Anda HARUS:

  1. Tempatkan FocusParkingView di tata letak aktivitas masing-masing.
  2. Pastikan tampilan yang dapat (atau tidak dapat) difokuskan.
  3. Gunakan FocusArea untuk menggabungkan semua tampilan yang dapat difokuskan, kecuali FocusParkingView.

Setiap tugas ini dijelaskan di bawah, setelah Anda menyiapkan lingkungan untuk mengembangkan aplikasi yang mendukung rotary.

Menyiapkan pengontrol rotasi

Sebelum dapat mulai mengembangkan aplikasi yang mendukung rotary, Anda memerlukan pengontrol rotary atau pengganti. Anda memiliki opsi yang dijelaskan di bawah.

Emulator

source build/envsetup.sh && lunch car_x86_64-userdebug
m -j
emulator -wipe-data -no-snapshot -writable-system

Anda juga dapat menggunakan aosp_car_x86_64-userdebug.

Untuk mengakses pengontrol putar yang diemulasi:

  1. Ketuk tiga titik di bagian bawah toolbar:

    Mengakses pengontrol rotasi yang diemulasi
    Gambar 1. Mengakses pengontrol rotasi yang diemulasi
  2. Pilih Car rotary di jendela extended controls:

    Pilih Putar mobil
    Gambar 2. Pilih Putar mobil

Keyboard USB

  • Colokkan keyboard USB ke perangkat Anda yang menjalankan Android Automotive OS (AAOS). Dalam beberapa kasus, hal ini mencegah keyboard virtual muncul.
  • Gunakan build userdebug atau eng.
  • Mengaktifkan pemfilteran peristiwa utama:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • Lihat tabel di bawah untuk menemukan kunci yang sesuai untuk setiap tindakan:
    Kunci Tindakan dari tombol putar
    T Putar berlawanan arah jarum jam
    E Putar searah jarum jam
    A Geser ke Kiri
    D Geser ke Kanan
    W Geser ke Atas
    S Geser ke Bawah
    F atau Koma Tombol tengah
    R atau Esc Tombol kembali

Perintah ADB

Anda dapat menggunakan perintah car_service untuk memasukkan peristiwa input rotasi. Perintah ini dapat dijalankan di perangkat yang menjalankan Android Automotive OS (AAOS) atau di emulator.

perintah car_service Input dari alat rotasi
adb shell cmd car_service inject-rotary Putar berlawanan arah jarum jam
adb shell cmd car_service inject-rotary -c true Putar searah jarum jam
adb shell cmd car_service inject-rotary -dt 100 50 Memutar berlawanan arah jarum jam beberapa kali (100 md yang lalu dan 50 md yang lalu)
adb shell cmd car_service inject-key 282 Geser ke Kiri
adb shell cmd car_service inject-key 283 Geser ke Kanan
adb shell cmd car_service inject-key 280 Geser ke Atas
adb shell cmd car_service inject-key 281 Geser ke Bawah
adb shell cmd car_service inject-key 23 Klik tombol tengah
adb shell input keyevent inject-key 4 Klik tombol Kembali

Rotary controller OEM

Saat hardware pengontrol rotari Anda sudah aktif dan berjalan, ini adalah opsi yang paling realistis. Hal ini sangat berguna untuk menguji rotasi cepat.

FocusParkingView

FocusParkingView adalah tampilan transparan di Library UI Mobil (car-ui-library). RotaryService menggunakannya untuk mendukung navigasi pengontrol rotasi. FocusParkingView harus menjadi tampilan pertama yang dapat difokuskan dalam tata letak. Elemen ini harus ditempatkan di luar semua FocusArea. Setiap jendela harus memiliki satu FocusParkingView. Jika sudah menggunakan tata letak dasar car-ui-library, yang berisi FocusParkingView, Anda tidak perlu menambahkan FocusParkingView lain. Berikut adalah contoh FocusParkingView di RotaryPlayground.

<FrameLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
   <com.android.car.ui.FocusParkingView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
</FrameLayout>

Berikut adalah alasan Anda memerlukan FocusParkingView:

  1. Android tidak menghapus fokus secara otomatis saat fokus ditetapkan di jendela lain. Jika Anda mencoba menghapus fokus di jendela sebelumnya, Android akan memfokuskan ulang tampilan di jendela tersebut, yang mengakibatkan dua jendela difokuskan secara bersamaan. Menambahkan FocusParkingView ke setiap jendela dapat memperbaiki masalah ini. Tampilan ini bersifat transparan dan sorotan fokus defaultnya dinonaktifkan, sehingga tidak terlihat oleh pengguna, terlepas dari apakah difokuskan atau tidak. Tindakan ini dapat mengambil fokus sehingga RotaryService dapat memarkir fokus di dalamnya untuk menghapus sorotan fokus.
  2. Jika hanya ada satu FocusArea di jendela saat ini, memutar pengontrol di FocusArea akan menyebabkan RotaryService memindahkan fokus dari tampilan di sebelah kanan ke tampilan di sebelah kiri (dan sebaliknya). Menambahkan tampilan ini ke setiap jendela dapat memperbaiki masalah. Saat RotaryService menentukan target fokus adalah FocusParkingView, RotaryService dapat menentukan bahwa penggabungan akan terjadi, dan pada titik tersebut, RotaryService akan menghindari penggabungan dengan tidak memindahkan fokus.
  3. Saat kontrol putar meluncurkan aplikasi, Android akan memfokuskan tampilan pertama yang dapat difokuskan, yang selalu berupa FocusParkingView. FocusParkingView menentukan tampilan optimal yang akan difokuskan, lalu menerapkan fokus.

Tampilan yang dapat difokuskan

RotaryService dibuat berdasarkan konsep fokus tampilan yang ada pada framework Android, yang berasal dari saat ponsel memiliki keyboard fisik dan D-pad. Atribut android:nextFocusForward yang ada digunakan kembali untuk perangkat putar (lihat Penyesuaian FocusArea), tetapi android:nextFocusLeft, android:nextFocusRight, android:nextFocusUp, dan android:nextFocusDown tidak.

RotaryService hanya berfokus pada tampilan yang dapat difokuskan. Beberapa tampilan, seperti Button, biasanya dapat difokuskan. Yang lainnya, seperti TextView dan ViewGroup, biasanya tidak. Tampilan yang dapat diklik secara otomatis dapat difokuskan dan tampilan secara otomatis dapat diklik jika memiliki pemroses klik. Jika logika otomatis ini menghasilkan fokus yang diinginkan, Anda tidak perlu menetapkan fokus tampilan secara eksplisit. Jika logika otomatis tidak menghasilkan kemampuan fokus yang diinginkan, tetapkan atribut android:focusable ke true atau false, atau tetapkan kemampuan fokus tampilan secara terprogram dengan View.setFocusable(boolean). Agar RotaryService berfokus pada tampilan, tampilan HARUS memenuhi persyaratan berikut:

  • Dapat difokuskan
  • Aktif
  • Terlihat
  • Memiliki nilai non-nol untuk lebar dan tinggi

Jika tampilan tidak memenuhi semua persyaratan ini, misalnya tombol yang dapat difokuskan tetapi dinonaktifkan, pengguna tidak dapat menggunakan kontrol putar untuk berfokus pada tombol tersebut. Jika Anda ingin berfokus pada tampilan yang dinonaktifkan, pertimbangkan untuk menggunakan status kustom, bukan android:state_enabled, untuk mengontrol cara tampilan muncul tanpa menunjukkan bahwa Android harus menganggapnya dinonaktifkan. Aplikasi Anda dapat memberi tahu pengguna alasan tampilan dinonaktifkan saat diketuk. Bagian berikutnya menjelaskan cara melakukannya.

Status kustom

Untuk menambahkan status kustom:

  1. Untuk menambahkan atribut kustom ke tampilan Anda. Misalnya, untuk menambahkan status kustom state_rotary_enabled ke class tampilan CustomView, gunakan:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Untuk melacak status ini, tambahkan variabel instance ke tampilan Anda beserta metode pengakses:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. Untuk membaca nilai atribut saat tampilan dibuat:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. Di class tampilan, ganti metode onCreateDrawableState(), lalu tambahkan status kustom, jika sesuai. Contoh:
    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        if (mRotaryEnabled) extraSpace++;
        int[] drawableState = super.onCreateDrawableState(extraSpace);
        if (mRotaryEnabled) {
            mergeDrawableStates(drawableState, { R.attr.state_rotary_enabled });
        }
        return drawableState;
    }
    
  5. Buat pengendali klik tampilan Anda berperforma berbeda-beda, bergantung pada statusnya. Misalnya, pengendali klik mungkin tidak melakukan apa pun atau mungkin memunculkan toast saat mRotaryEnabled adalah false.
  6. Agar tombol tampak dinonaktifkan, di drawable latar belakang tampilan, gunakan app:state_rotary_enabled, bukan android:state_enabled. Jika belum memilikinya, Anda harus menambahkan:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Jika tampilan Anda dinonaktifkan di tata letak apa pun, ganti android:enabled="false" dengan app:state_rotary_enabled="false", lalu tambahkan namespace app, seperti di atas.
  8. Jika tampilan Anda dinonaktifkan secara terprogram, ganti panggilan ke setEnabled() dengan panggilan ke setRotaryEnabled().

FocusArea

Gunakan FocusAreas untuk mempartisi tampilan yang dapat difokuskan menjadi beberapa blok untuk mempermudah navigasi dan agar konsisten dengan aplikasi lain. Misalnya, jika aplikasi Anda memiliki toolbar, toolbar harus berada di FocusArea terpisah dari aplikasi lainnya. Panel tab dan elemen navigasi lainnya juga harus dipisahkan dari aplikasi lainnya. Daftar besar umumnya harus memiliki FocusArea-nya sendiri. Jika tidak, pengguna harus memutar seluruh daftar untuk mengakses beberapa tampilan.

FocusArea adalah subclass dari LinearLayout di library car-ui. Jika fitur ini diaktifkan, FocusArea akan menggambar sorotan saat salah satu turunan difokuskan. Untuk mempelajari lebih lanjut, lihat Penyesuaian sorotan fokus.

Saat membuat blok navigasi dalam file tata letak, jika Anda ingin menggunakan LinearLayout sebagai penampung untuk blok tersebut, gunakan FocusArea. Jika tidak, gabungkan blok dalam FocusArea.

JANGAN bertingkat FocusArea di FocusArea lain. Tindakan ini akan menyebabkan perilaku navigasi yang tidak ditentukan. Pastikan semua tampilan yang dapat difokuskan bertingkat dalam FocusArea.

Contoh FocusArea di RotaryPlayground ditampilkan di bawah ini:

<com.android.car.ui.FocusArea
       android:layout_margin="16dp"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">
       <EditText
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:singleLine="true">
       </EditText>
   </com.android.car.ui.FocusArea>

FocusArea berfungsi sebagai berikut:

  1. Saat menangani tindakan putar dan dorong, RotaryService akan mencari instance FocusArea dalam hierarki tampilan.
  2. Saat menerima peristiwa rotasi, RotaryService akan memindahkan fokus ke View lain yang dapat mengambil fokus di FocusArea yang sama.
  3. Saat menerima peristiwa dorongan, RotaryService akan memindahkan fokus ke tampilan lain yang dapat mengambil fokus di FocusArea lain (biasanya berdekatan).

Jika Anda tidak menyertakan FocusAreas dalam tata letak, tampilan root akan diperlakukan sebagai area fokus implisit. Pengguna tidak dapat mendorong untuk menavigasi di aplikasi. Sebagai gantinya, mereka akan memutar semua tampilan yang dapat difokuskan, yang mungkin memadai untuk dialog.

Penyesuaian FocusArea

Dua atribut View standar dapat digunakan untuk menyesuaikan navigasi putar:

  • android:nextFocusForward memungkinkan developer aplikasi menentukan urutan rotasi di area fokus. Ini adalah atribut yang sama dengan yang digunakan untuk mengontrol urutan Tab untuk navigasi keyboard. JANGAN gunakan atribut ini untuk membuat loop. Sebagai gantinya, gunakan app:wrapAround (lihat di bawah) untuk membuat loop.
  • android:focusedByDefault memungkinkan developer aplikasi menentukan tampilan fokus default di jendela. JANGAN gunakan atribut ini dan app:defaultFocus (lihat di bawah) dalam FocusArea yang sama.

FocusArea juga menentukan beberapa atribut untuk menyesuaikan navigasi putar. Area fokus implisit tidak dapat disesuaikan dengan atribut ini.

  1. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocus dapat digunakan untuk menentukan ID tampilan turunan yang dapat difokuskan, yang harus difokuskan saat pengguna mendorong ke FocusArea ini.
  2. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocusOverridesHistory dapat disetel ke true untuk membuat tampilan yang ditentukan di atas mengambil fokus meskipun dengan histori untuk menunjukkan tampilan lain di FocusArea ini telah difokuskan.
  3. (Android 12)
    Gunakan app:nudgeLeftShortcut, app:nudgeRightShortcut, app:nudgeUpShortcut, dan app:nudgeDownShortcut untuk menentukan ID tampilan turunan yang dapat difokuskan, yang harus difokuskan saat pengguna mendorong ke arah tertentu. Untuk mempelajari lebih lanjut, lihat konten untuk pintasan dorong di bawah.

    (Android 11 QPR3, Android 11 Car, tidak digunakan lagi di Android 12) app:nudgeShortcut dan app:nudgeShortcutDirection hanya mendukung satu pintasan dorong.

  4. (Android 11 QPR3, Android 11 Car, Android 12)
    Untuk mengaktifkan rotasi agar dibungkus di FocusArea ini, app:wrapAround dapat disetel ke true. Ini biasanya digunakan saat tampilan disusun dalam lingkaran atau oval.
  5. (Android 11 QPR3, Android 11 Car, Android 12)
    Untuk menyesuaikan padding sorotan di FocusArea ini, gunakan app:highlightPaddingStart, app:highlightPaddingEnd, app:highlightPaddingTop, app:highlightPaddingBottom, app:highlightPaddingHorizontal, dan app:highlightPaddingVertical.
  6. (Android 11 QPR3, Android 11 Car, Android 12)
    Untuk menyesuaikan batas yang dirasakan dari FocusArea ini untuk menemukan target dorongan, gunakan app:startBoundOffset, app:endBoundOffset, app:topBoundOffset, app:bottomBoundOffset, app:horizontalBoundOffset, dan app:verticalBoundOffset.
  7. (Android 11 QPR3, Android 11 Car, Android 12)
    Untuk menentukan ID FocusArea (atau area) yang berdekatan secara eksplisit dalam rute yang diberikan, gunakan app:nudgeLeft, app:nudgeRight, app:nudgeUp, dan app:nudgeDown. Gunakan ini jika penelusuran geometris yang digunakan secara default tidak menemukan target yang diinginkan.

Dorongan biasanya menavigasi di antara FocusAreas. Namun, dengan pintasan dorongan, dorongan terkadang pertama-tama menavigasi dalam FocusArea sehingga pengguna mungkin perlu mendorong dua kali untuk menavigasi ke FocusArea berikutnya. Pintasan nudge berguna saat FocusArea berisi daftar panjang yang diikuti dengan Floating Action Button, seperti pada contoh di bawah:

Pintasan dorong
Gambar 3. Pintasan dorong

Tanpa pintasan dorong, pengguna harus memutar seluruh daftar untuk mencapai FAB.

Penyesuaian sorotan fokus

Seperti yang disebutkan di atas, RotaryService dibuat berdasarkan konsep fokus tampilan yang ada di framework Android. Saat pengguna memutar dan mendorong, RotaryService akan memindahkan fokus, memfokuskan satu tampilan dan membatalkan fokus tampilan lainnya. Di Android, saat tampilan difokuskan, jika tampilan:

  • Telah menentukan sorotan fokusnya sendiri, Android menggambar sorotan fokus tampilan.
  • Tidak menentukan sorotan fokus, dan sorotan fokus default tidak dinonaktifkan, Android akan menggambar sorotan fokus default untuk tampilan.

Aplikasi yang dirancang untuk sentuh biasanya tidak menentukan sorotan fokus yang sesuai.

Sorotan fokus default disediakan oleh framework Android dan dapat diganti oleh OEM. Developer aplikasi menerimanya saat tema yang mereka gunakan berasal dari Theme.DeviceDefault.

Untuk pengalaman pengguna yang konsisten, gunakan sorotan fokus default jika memungkinkan. Jika Anda memerlukan sorotan fokus berbentuk kustom (misalnya, bulat atau berbentuk pil), atau jika Anda menggunakan tema yang tidak berasal dari Theme.DeviceDefault, gunakan resource car-ui-library untuk menentukan sorotan fokus Anda sendiri untuk setiap tampilan.

Untuk menentukan sorotan fokus kustom untuk tampilan, ubah drawable latar belakang atau latar depan tampilan menjadi drawable yang berbeda saat tampilan difokuskan. Biasanya, Anda akan mengubah latar belakang. Drawable berikut, jika digunakan sebagai latar belakang untuk tampilan persegi, akan menghasilkan sorotan fokus bulat:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_focused="true" android:state_pressed="true">
      <shape android:shape="oval">
         <solid android:color="@color/car_ui_rotary_focus_pressed_fill_color"/>
         <stroke
            android:width="@dimen/car_ui_rotary_focus_pressed_stroke_width"
            android:color="@color/car_ui_rotary_focus_pressed_stroke_color"/>
      </shape>
   </item>
   <item android:state_focused="true">
      <shape android:shape="oval">
         <solid android:color="@color/car_ui_rotary_focus_fill_color"/>
         <stroke
            android:width="@dimen/car_ui_rotary_focus_stroke_width"
            android:color="@color/car_ui_rotary_focus_stroke_color"/>
      </shape>
   </item>
   <item>
      <ripple...>
         ...
      </ripple>
   </item>
</selector>

(Android 11 QPR3, Android 11 Car, Android 12) Referensi resource Tebal dalam contoh di atas mengidentifikasi resource yang ditentukan oleh car-ui-library. OEM menggantinya agar konsisten dengan sorotan fokus default yang ditentukan. Hal ini memastikan bahwa warna sorotan fokus, lebar goresan, dan sebagainya tidak berubah saat pengguna beralih antara tampilan dengan sorotan fokus kustom dan tampilan dengan sorotan fokus default. Item terakhir adalah ripple yang digunakan untuk sentuhan. Nilai default yang digunakan untuk resource tebal akan muncul sebagai berikut:

Nilai default untuk resource tebal
Gambar 4. Nilai default untuk resource tebal

Selain itu, sorotan fokus kustom dipanggil saat tombol diberi warna latar solid untuk menarik perhatian pengguna, seperti pada contoh di bawah. Hal ini dapat membuat sorotan fokus sulit dilihat. Dalam situasi ini, tentukan sorotan fokus kustom menggunakan warna sekunder:

Warna latar belakang solid
  • (Android 11 QPR3, Android 11 Car, Android 12)
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • (Android 12)
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

Contoh:

Difokuskan, tidak ditekan Fokus, ditekan
Difokuskan, tidak ditekan Fokus, ditekan

Scroll putar

Jika aplikasi Anda menggunakan RecyclerView, Anda HARUS menggunakan CarUiRecyclerView sebagai gantinya. Hal ini memastikan bahwa UI Anda konsisten dengan UI lainnya karena penyesuaian OEM berlaku untuk semua CarUiRecyclerView.

Jika semua elemen dalam daftar dapat difokuskan, Anda tidak perlu melakukan apa pun. Navigasi rotasi memindahkan fokus melalui elemen dalam daftar dan daftar akan di-scroll agar elemen yang baru difokuskan terlihat.

(Android 11 QPR3, Android 11 Car, Android 12)
Jika ada campuran elemen yang dapat difokuskan dan tidak dapat difokuskan, atau jika semua elemen tidak dapat difokuskan, Anda dapat mengaktifkan scroll putar, yang memungkinkan pengguna menggunakan pengontrol putar untuk men-scroll daftar secara bertahap tanpa melewati item yang tidak dapat difokuskan. Untuk mengaktifkan scroll putar, tetapkan atribut app:rotaryScrollEnabled ke true.

(Android 11 QPR3, Android 11 Car, Android 12)
Anda dapat mengaktifkan scroll putar di tampilan yang dapat di-scroll, termasuk avCarUiRecyclerView, dengan metode setRotaryScrollEnabled() di CarUiUtils. Jika melakukannya, Anda harus:

  • Buat tampilan yang dapat di-scroll dapat difokuskan sehingga dapat difokuskan saat tidak ada tampilan turunan yang dapat difokuskan yang terlihat,
  • Nonaktifkan sorotan fokus default pada tampilan yang dapat di-scroll dengan memanggil setDefaultFocusHighlightEnabled(false) sehingga tampilan yang dapat di-scroll tidak tampak difokuskan,
  • Pastikan tampilan yang dapat di-scroll difokuskan sebelum turunannya dengan memanggil setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS).
  • Proses MotionEvents dengan SOURCE_ROTARY_ENCODER dan AXIS_VSCROLL atau AXIS_HSCROLL untuk menunjukkan jarak yang akan di-scroll dan arah (melalui tanda).

Saat scroll putar diaktifkan di CarUiRecyclerView dan pengguna memutar ke area yang tidak memiliki tampilan yang dapat difokuskan, scrollbar akan berubah dari abu-abu menjadi biru, seolah-olah menunjukkan bahwa scrollbar difokuskan. Anda dapat menerapkan efek serupa jika mau.

MotionEvents sama dengan yang dihasilkan oleh roda scroll pada mouse, kecuali sumbernya.

Mode manipulasi langsung

Biasanya, dorongan dan rotasi menavigasi antarmuka pengguna, sedangkan penekanan tombol Tengah akan melakukan tindakan, meskipun tidak selalu demikian. Misalnya, jika pengguna ingin menyesuaikan volume alarm, mereka dapat menggunakan pengontrol putar untuk membuka penggeser volume, menekan tombol Tengah, memutar pengontrol untuk menyesuaikan volume alarm, lalu menekan tombol Kembali untuk kembali ke navigasi. Hal ini disebut sebagai mode manipulasi langsung (DM). Dalam mode ini, pengontrol rotasi digunakan untuk berinteraksi dengan tampilan secara langsung, bukan untuk menavigasi.

Terapkan DM dengan salah satu dari dua cara. Jika Anda hanya perlu menangani rotasi dan tampilan yang ingin dimanipulasi merespons ACTION_SCROLL_FORWARD dan ACTION_SCROLL_BACKWARD AccessibilityEvent dengan tepat, gunakan mekanisme sederhana. Jika tidak, gunakan mekanisme lanjutan.

Mekanisme sederhana adalah satu-satunya opsi di jendela sistem; aplikasi dapat menggunakan salah satu mekanisme.

Mekanisme sederhana

(Android 11 QPR3, Android 11 Car, Android 12)
Aplikasi Anda harus memanggil DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable). RotaryService mengenali saat pengguna berada dalam mode DM dan memasuki mode DM saat pengguna menekan tombol Tengah saat tampilan difokuskan. Saat dalam mode DM, rotasi akan menjalankan ACTION_SCROLL_FORWARD atau ACTION_SCROLL_BACKWARD dan keluar dari mode DM saat pengguna menekan tombol Kembali. Mekanisme sederhana ini mengalihkan status tampilan yang dipilih saat memasuki dan keluar dari mode DM.

Untuk memberikan isyarat visual bahwa pengguna berada dalam mode DM, buat tampilan Anda terlihat berbeda saat dipilih. Misalnya, ubah latar belakang saat android:state_selected adalah true.

Mekanisme lanjutan

Aplikasi menentukan kapan RotaryService memasuki dan keluar dari mode DM. Untuk pengalaman pengguna yang konsisten, menekan tombol Tengah dengan tampilan DM yang difokuskan akan memasuki mode DM dan tombol Kembali akan keluar dari mode DM. Jika tidak digunakan, tombol Tengah dan/atau dorongan dapat menjadi cara alternatif untuk keluar dari mode DM. Untuk aplikasi seperti Maps, tombol untuk mewakili DM dapat digunakan untuk memasuki mode DM.

Untuk mendukung mode DM lanjutan, tampilan:

  1. (Android 11 QPR3, Android 11 Car, Android 12) HARUS memproses peristiwa KEYCODE_DPAD_CENTER untuk memasuki mode DM dan memproses peristiwa KEYCODE_BACK untuk keluar dari mode DM, memanggil DirectManipulationHelper.enableDirectManipulationMode() dalam setiap kasus. Untuk memproses peristiwa ini, lakukan salah satu tindakan berikut:
    • Daftarkan OnKeyListener.
    • atau,
    • Luaskan tampilan, lalu ganti metode dispatchKeyEvent()-nya.
  2. HARUS memproses peristiwa nudge (KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, atau KEYCODE_DPAD_RIGHT) jika tampilan harus menangani nudge.
  3. HARUS memproses MotionEvent dan mendapatkan jumlah rotasi di AXIS_SCROLL jika tampilan ingin menangani rotasi. Ada beberapa cara untuk melakukannya:
    1. Daftarkan OnGenericMotionListener.
    2. Luaskan tampilan dan ganti metode dispatchTouchEvent()-nya.
  4. Untuk menghindari terjebak dalam mode DM, HARUS keluar dari mode DM saat Fragmen atau Aktivitas yang menjadi milik tampilan tidak interaktif.
  5. HARUS memberikan isyarat visual untuk menunjukkan bahwa tampilan berada dalam mode DM.

Contoh tampilan kustom yang menggunakan mode DM untuk menggeser dan memperbesar peta diberikan di bawah ini:

/** Whether this view is in DM mode. */
private boolean mInDirectManipulationMode;

/** Initializes the view. Called by the constructors. */ private void init() { setOnKeyListener((view, keyCode, keyEvent) -> { boolean isActionUp = keyEvent.getAction() == KeyEvent.ACTION_UP; switch (keyCode) { // Always consume KEYCODE_DPAD_CENTER and KEYCODE_BACK events. case KeyEvent.KEYCODE_DPAD_CENTER: if (!mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = true; DirectManipulationHelper.enableDirectManipulationMode(this, true); setSelected(true); // visually indicate DM mode } return true; case KeyEvent.KEYCODE_BACK: if (mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); setSelected(false); } return true; // Consume controller nudge events only when in DM mode. // When in DM mode, nudges pan the map. case KeyEvent.KEYCODE_DPAD_UP: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, -10f); return true; case KeyEvent.KEYCODE_DPAD_DOWN: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, 10f); return true; case KeyEvent.KEYCODE_DPAD_LEFT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(-10f, 0f); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(10f, 0f); return true; // Don't consume other key events. default: return false; } });
// When in DM mode, rotation zooms the map. setOnGenericMotionListener(((view, motionEvent) -> { if (!mInDirectManipulationMode) return false; float scroll = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL); zoom(10 * scroll); return true; })); }
@Override public void onPause() { if (mInDirectManipulationMode) { // To ensure that the user doesn't get stuck in DM mode, disable DM mode // when the fragment is not interactive (e.g., a dialog shows up). mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); } super.onPause(); }

Contoh lainnya dapat ditemukan di project RotaryPlayground.

ActivityView

Saat menggunakan ActivityView:

  • ActivityView tidak boleh difokuskan.
  • (Android 11 QPR3, Android 11 Car, tidak digunakan lagi di Android 11)
    Konten ActivityView HARUS berisi FocusParkingView sebagai tampilan pertama yang dapat difokuskan, dan atribut app:shouldRestoreFocus HARUS berupa false.
  • Konten ActivityView tidak boleh memiliki tampilan android:focusByDefault.

Bagi pengguna, ActivityViews seharusnya tidak memengaruhi navigasi, kecuali area fokus tidak dapat menjangkau ActivityViews. Dengan kata lain, Anda tidak dapat memiliki satu area fokus yang memiliki konten di dalam dan di luar ActivityView. Jika Anda tidak menambahkan FocusAreas ke ActivityView, root hierarki tampilan di ActivityView dianggap sebagai area fokus implisit.

Tombol yang beroperasi saat ditahan

Sebagian besar tombol menyebabkan beberapa tindakan saat diklik. Beberapa tombol beroperasi saat ditekan. Misalnya, tombol Maju Cepat dan Putar Balik biasanya beroperasi saat ditekan. Agar tombol tersebut mendukung tombol putar, dengarkan KEYCODE_DPAD_CENTER KeyEvents sebagai berikut:

mButton.setOnKeyListener((v, keyCode, event) ->
{
    if (keyCode != KEYCODE_DPAD_CENTER) {
        return false;
    }
    if (event.getAction() == ACTION_DOWN) {
        mButton.setPressed(true);
        mHandler.post(mRunnable);
    } else {
        mButton.setPressed(false);
        mHandler.removeCallbacks(mRunnable);
    }
    return true;
});

Di mana mRunnable mengambil tindakan (seperti memundurkan) dan menjadwalkan dirinya sendiri untuk berjalan setelah penundaan.

Mode sentuh

Pengguna dapat menggunakan pengontrol putar untuk berinteraksi dengan head unit di mobil dengan dua cara, baik menggunakan pengontrol putar maupun dengan menyentuh layar. Saat menggunakan pengontrol putar, salah satu tampilan yang dapat difokuskan akan ditandai. Saat menyentuh layar, tidak ada sorotan fokus yang muncul. Pengguna dapat beralih di antara mode input ini kapan saja:

  • Putar → sentuh. Saat pengguna menyentuh layar, sorotan fokus akan menghilang.
  • Sentuh → putar. Saat pengguna mendorong, memutar, atau menekan tombol Tengah, sorotan fokus akan muncul.

Tombol Kembali dan Layar Utama tidak memengaruhi mode input.

Rotary piggybacks pada konsep mode sentuh yang ada di Android. Anda dapat menggunakan View.isInTouchMode() untuk menentukan mode input yang digunakan pengguna. Anda dapat menggunakan OnTouchModeChangeListener untuk memproses perubahan. Meskipun dapat digunakan untuk menyesuaikan antarmuka pengguna untuk mode input saat ini, hindari perubahan besar karena dapat mengganggu.

Pemecahan masalah

Dalam aplikasi yang dirancang untuk sentuhan, tampilan bertingkat yang dapat difokuskan biasanya digunakan. Misalnya, mungkin ada FrameLayout di sekitar ImageButton, yang keduanya dapat difokuskan. Hal ini tidak berbahaya untuk sentuh, tetapi dapat mengakibatkan pengalaman pengguna yang buruk untuk tombol putar karena pengguna harus memutar pengontrol dua kali untuk berpindah ke tampilan interaktif berikutnya. Untuk pengalaman pengguna yang baik, Google merekomendasikan agar Anda membuat tampilan luar atau tampilan dalam dapat difokuskan, tetapi tidak keduanya.

Jika tombol atau tombol beralih kehilangan fokus saat ditekan melalui pengontrol putar, salah satu kondisi berikut mungkin berlaku:

  • Tombol atau tombol akses dinonaktifkan (secara singkat atau tanpa batas waktu) karena tombol ditekan. Dalam kedua kasus tersebut, ada dua cara untuk mengatasinya:
    • Biarkan status android:enabled sebagai true dan gunakan status kustom untuk membuat tombol atau tombol beralih berwarna abu-abu seperti yang dijelaskan dalam Status Kustom.
    • Gunakan penampung untuk mengelilingi tombol atau tombol akses dan membuat penampung dapat difokuskan, bukan tombol atau tombol akses. (Pemroses klik harus berada di penampung.)
  • Tombol atau tombol akses sedang diganti. Misalnya, tindakan yang dilakukan saat tombol ditekan atau tombol diaktifkan dapat memicu pembaruan tindakan yang tersedia sehingga tombol baru akan menggantikan tombol yang ada. Ada dua cara untuk mengatasinya:
    • Daripada membuat tombol atau tombol akses baru, tetapkan ikon dan/atau teks tombol atau tombol akses yang ada.
    • Seperti di atas, tambahkan penampung yang dapat difokuskan di sekitar tombol atau tombol akses.

RotaryPlayground

RotaryPlayground adalah aplikasi referensi untuk alat rotasi. Gunakan untuk mempelajari cara mengintegrasikan fitur rotasi ke dalam aplikasi Anda. RotaryPlayground disertakan dalam build emulator dan dalam build untuk perangkat yang menjalankan Android Automotive OS (AAOS).

  • Repositori RotaryPlayground: packages/apps/Car/tests/RotaryPlayground/
  • Versi: Android 11 QPR3, Android 11 Car, dan Android 12

Aplikasi RotaryPlayground menampilkan tab berikut di sebelah kiri:

  • Kartu. Uji navigasi di sekitar area fokus, lewati elemen yang tidak dapat difokuskan dan input teks.
  • Manipulasi Langsung. Uji widget yang mendukung mode manipulasi langsung sederhana dan lanjutan. Tab ini khusus untuk manipulasi langsung dalam jendela aplikasi.
  • Manipulasi UI Sys. Uji widget yang mendukung manipulasi langsung di jendela sistem yang hanya mendukung mode manipulasi langsung sederhana.
  • Petak. Menguji navigasi dari alat rotasi pola z dengan scroll.
  • Notifikasi. Menguji notifikasi pendahuluan yang masuk dan keluar.
  • Scroll. Uji scroll melalui campuran konten yang dapat difokuskan dan tidak dapat difokuskan.
  • WebView. Uji navigasi melalui link di WebView.
  • FocusArea kustom. Uji penyesuaian FocusArea:
    • Wrap-around.
    • android:focusedByDefault dan app:defaultFocus
    • .
    • Target dorongan eksplisit.
    • Pintasan dorong.
    • FocusArea tanpa tampilan yang dapat difokuskan.