พัฒนาแอป

เนื้อหาต่อไปนี้มีไว้สำหรับนักพัฒนาแอป

หากต้องการให้แอปรองรับปุ่มหมุน คุณต้องทำดังนี้

  1. วาง FocusParkingView ในเลย์เอาต์กิจกรรมที่เกี่ยวข้อง
  2. อย่าลืมตรวจสอบมุมมองที่โฟกัสได้ (หรือไม่ได้)
  3. ใช้ FocusArea เพื่อเลื่อนมุมมองที่โฟกัสได้ทั้งหมด ยกเว้น FocusParkingView

งานแต่ละงานด้านล่างนี้มีรายละเอียดหลังจากที่คุณตั้งค่าสภาพแวดล้อมเพื่อ พัฒนาแอปที่เปิดใช้ด้วยการหมุน

ตั้งค่าตัวควบคุมแบบหมุน

คุณต้องมีตัวควบคุมแบบหมุนก่อนที่จะเริ่มพัฒนาแอปที่เปิดใช้ด้วยปุ่มหมุนได้ หรือแค่การทำงานด้วยตนเอง คุณมีตัวเลือกตามที่อธิบายไว้ด้านล่าง

โปรแกรมจำลอง

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

หรือคุณจะใช้ aosp_car_x86_64-userdebug ก็ได้

วิธีเข้าถึงตัวควบคุมแบบหมุนจำลอง

  1. แตะจุด 3 จุดที่ด้านล่างของแถบเครื่องมือ

    เข้าถึงตัวควบคุมแบบหมุนที่จำลอง
    รูปที่ 1 เข้าถึงตัวควบคุมแบบหมุนจำลอง
  2. เลือกปุ่มหมุนรถยนต์ในหน้าต่างควบคุมแบบขยาย ซึ่งมีดังนี้

    เลือกปุ่มหมุนรถยนต์
    รูปที่ 2 เลือก "ปุ่มหมุนรถยนต์"

แป้นพิมพ์ USB

  • เสียบแป้นพิมพ์ USB เข้ากับอุปกรณ์ที่ใช้ Android Automotive OS (AAOS) ในบางกรณี วิธีนี้จะป้องกันไม่ให้แป้นพิมพ์บนหน้าจอปรากฏขึ้น
  • ใช้บิลด์ userdebug หรือ eng
  • วิธีเปิดใช้การกรองเหตุการณ์สำคัญ
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • ดูตารางด้านล่างเพื่อหาคีย์ที่สอดคล้องกันสำหรับการดำเนินการแต่ละรายการ
    คีย์ การทำงานของปุ่มหมุน
    Q หมุนทวนเข็มนาฬิกา
    E หมุนตามเข็มนาฬิกา
    กดไปทางซ้าย
    D กดไปทางขวา
    W กดขึ้น
    อา กดลง
    F หรือคอมมา ปุ่มกลาง
    R หรือ Esc ปุ่มย้อนกลับ

คำสั่ง ADB

คุณใช้คําสั่ง car_service เพื่อแทรกเหตุการณ์การป้อนข้อมูลแบบหมุนได้ คำสั่งเหล่านี้ สามารถเรียกใช้ในอุปกรณ์ที่ใช้ Android Automotive OS (AAOS) หรือในโปรแกรมจําลองได้

คำสั่งเกี่ยวกับบริการรถยนต์ การป้อนข้อมูลด้วยปุ่มหมุน
adb shell cmd car_service inject-rotary หมุนทวนเข็มนาฬิกา
adb shell cmd car_service inject-rotary -c true หมุนตามเข็มนาฬิกา
adb shell cmd car_service inject-rotary -dt 100 50 หมุนทวนเข็มนาฬิกาหลายครั้ง (100 มิลลิวินาทีที่ผ่านมาและ 50 มิลลิวินาทีที่ผ่านมา)
adb shell cmd car_service inject-key 282 กดไปทางซ้าย
adb shell cmd car_service inject-key 283 กดไปทางขวา
adb shell cmd car_service inject-key 280 กดขึ้น
adb shell cmd car_service inject-key 281 กดลง
adb shell cmd car_service inject-key 23 คลิกปุ่มกลาง
adb shell input keyevent inject-key 4 คลิกปุ่มย้อนกลับ

ตัวควบคุมปุ่มหมุนของ OEM

เมื่อฮาร์ดแวร์ตัวควบคุมแบบหมุนเริ่มทำงานแล้ว นี่เป็นการดำเนินการ ที่สมจริง ซึ่งมีประโยชน์อย่างยิ่งสำหรับการทดสอบการหมุนเร็ว

มุมมองที่จอดรถแบบโฟกัส

FocusParkingView เป็นมุมมองโปร่งใสใน Car UI Library (car-ui-library) RotaryService จะใช้ตำแหน่งนี้เพื่อรองรับการนำทางด้วยตัวควบคุมแบบหมุน FocusParkingView ต้องเป็นมุมมองที่โฟกัสได้รายการแรก ในเลย์เอาต์ ต้องวางนอก FocusArea ทั้งหมด แต่ละหน้าต่างต้องมี 1 รายการ FocusParkingView ถ้าคุณใช้เค้าโครงฐานของ Car-UI-library อยู่แล้ว ที่มี FocusParkingView อยู่ คุณไม่จำเป็นต้องเพิ่มอีก FocusParkingView ด้านล่างนี้เป็นตัวอย่างของ FocusParkingView ใน 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>

เหตุผลที่คุณต้องมี FocusParkingView มีดังนี้

  1. Android จะไม่ล้างโฟกัสโดยอัตโนมัติเมื่อตั้งค่าโฟกัสในหน้าต่างอื่น หากคุณ ให้พยายามล้างโฟกัสในหน้าต่างก่อนหน้า Android จะปรับมุมมองในหน้าต่างนั้นใหม่ จะทำให้หน้าต่าง 2 หน้าต่างโฟกัส พร้อมกัน การเพิ่ม FocusParkingView แต่ละหน้าต่างสามารถแก้ปัญหานี้ได้ มุมมองนี้โปร่งใสและมีการไฮไลต์โฟกัสเริ่มต้น ถูกปิดใช้ ดังนั้นผู้ใช้จึงมองไม่เห็น ไม่ว่าจะโฟกัสอยู่หรือไม่ก็ตาม ช่วยปรับโฟกัสเพื่อให้ RotaryService ช่วยกำหนดจุดโฟกัส เพื่อลบไฮไลต์โฟกัส
  2. หากมี FocusArea เพียง 1 รายการในหน้าต่างปัจจุบัน กำลังหมุนตัวควบคุม ใน FocusArea จะทำให้ RotaryService ย้ายโฟกัส จากมุมมองด้านขวาไปเป็นมุมมองทางซ้าย (และกลับกัน) กำลังเพิ่มมุมมองนี้ ลงในแต่ละหน้าต่างสามารถช่วยแก้ไขปัญหาได้ เมื่อ RotaryService กําหนดโฟกัส เป้าหมายคือ FocusParkingView ซึ่งสามารถระบุได้ว่าผลสรุปกำลังจะ ซึ่งจะหลีกเลี่ยงการตัดภาพรอบทิศทางโดยไม่ย้ายโฟกัส
  3. เมื่อการควบคุมด้วยปุ่มหมุนเปิดแอป Android จะโฟกัสที่มุมมองที่โฟกัสได้เป็นอันดับแรก ซึ่งจะเป็น FocusParkingView เสมอ FocusParkingView กำหนดมุมมองที่เหมาะสมที่จะมุ่งเน้น แล้วใช้การโฟกัส

มุมมองที่โฟกัสได้

RotaryService ต่อยอดมาจากเฟรมเวิร์ก ที่มีอยู่ แนวคิดของการมองภาพย้อนกลับไปตั้งแต่ตอนที่โทรศัพท์มีแป้นพิมพ์จริงและ D-pad เปลี่ยนแอตทริบิวต์ android:nextFocusForward ที่มีอยู่ให้เป็นโรตารีแล้ว (ดูการปรับแต่ง FocusArea) แต่ android:nextFocusLeft android:nextFocusRight android:nextFocusUp และ android:nextFocusDown ไม่ใช่

RotaryService จะมุ่งเน้นเฉพาะยอดดูที่โฟกัสได้ ยอดดูบางส่วน เช่น Button มักจะโฟกัสได้ อื่นๆ เช่น TextView และ ViewGroup มักจะไม่ใช่อย่างนั้น มุมมองที่คลิกได้จะโฟกัสได้โดยอัตโนมัติ และการดูจะ คลิกได้เมื่อมี Listener การคลิก ถ้าตรรกะอัตโนมัตินี้ให้ผลลัพธ์ ความสามารถในการโฟกัส คุณไม่จำเป็นต้องตั้งค่าความสามารถในการโฟกัสของมุมมองอย่างชัดเจน หากตรรกะอัตโนมัติไม่ ส่งผลให้ความสามารถในการโฟกัสที่ต้องการ ให้ตั้งค่าแอตทริบิวต์ android:focusable เป็น true หรือ false หรือตั้งค่าความสามารถในการโฟกัสของมุมมองแบบเป็นโปรแกรมด้วย View.setFocusable(boolean) RotaryService ต้องมีการแสดงผลจึงจะเห็นได้ มีคุณสมบัติตรงตามข้อกำหนดต่อไปนี้

  • โฟกัสได้
  • เปิดใช้
  • แสดง
  • มีค่าความกว้างและความสูงที่ไม่ใช่ 0

หากข้อมูลพร็อพเพอร์ตี้ไม่เป็นไปตามข้อกำหนดเหล่านี้ทั้งหมด เช่น ปุ่มที่โฟกัสได้แต่ปิดใช้ ผู้ใช้ไม่สามารถใช้ปุ่มหมุนควบคุมเพื่อโฟกัสได้ หากคุณต้องการโฟกัส การดูที่ปิดไปแล้ว ลองใช้สถานะที่กำหนดเองแทน android:state_enabled เพื่อควบคุมวิธีการ มุมมองปรากฏขึ้นโดยไม่ระบุว่า Android ควรพิจารณาปิดใช้มุมมองนี้ แอปของคุณให้ข้อมูล ผู้ใช้ว่าเหตุใดการดูจึงถูกปิดใช้เมื่อแตะ ส่วนถัดไปจะอธิบายถึงวิธีการดังกล่าว

สถานะที่กำหนดเอง

วิธีเพิ่มสถานะที่กำหนดเอง

  1. วิธีเพิ่มแอตทริบิวต์ที่กำหนดเอง ในมุมมองของคุณ เช่น หากต้องการเพิ่มสถานะที่กำหนดเอง state_rotary_enabled ลงใน ดูชั้นเรียนของ CustomView ใช้:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. หากต้องการติดตามสถานะนี้ ให้เพิ่มตัวแปรอินสแตนซ์ลงในข้อมูลพร็อพเพอร์ตี้พร้อมกับเมธอดตัวเข้าถึง ดังนี้
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. หากต้องการอ่านค่าของแอตทริบิวต์เมื่อสร้างมุมมองของคุณ ให้ทำดังนี้
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. ในคลาสการแสดงผล ให้ลบล้างเมธอด onCreateDrawableState() แล้ว เพิ่มสถานะที่กำหนดเองได้ตามความเหมาะสม เช่น
    @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. ทำให้เครื่องจัดการการคลิกของมุมมองทำงานแตกต่างออกไปตามสถานะ ตัวอย่างเช่น พารามิเตอร์ เครื่องจัดการการคลิกอาจไม่ดำเนินการใดๆ หรืออาจแสดงข้อความโทสต์เมื่อ mRotaryEnabled คือfalse
  6. หากต้องการทำให้ปุ่มถูกปิดใช้งาน ให้ใช้ที่ถอนออกได้ในพื้นหลังของมุมมอง app:state_rotary_enabled จากราคาเต็ม android:state_enabled หากยังไม่มี ให้เพิ่มรายการต่อไปนี้
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. หากปิดใช้มุมมองในเลย์เอาต์ใดก็ตาม ให้แทนที่ android:enabled="false" ด้วย app:state_rotary_enabled="false" จากนั้นเพิ่มเนมสเปซ app เหมือนดังข้างต้น
  8. หากมุมมองของคุณถูกปิดใช้งานแบบเป็นโปรแกรม ให้แทนที่การเรียกไปยัง setEnabled() ที่มีการโทรไปยัง setRotaryEnabled()

พื้นที่โฟกัส

ใช้ FocusAreas เพื่อแบ่งพาร์ติชันมุมมองที่โฟกัสได้เป็นบล็อกเพื่อทำการนำทาง ง่ายขึ้นและสอดคล้องกับแอปอื่นๆ ตัวอย่างเช่น หากแอปของคุณมีแถบเครื่องมือ แถบเครื่องมือ ควรอยู่ใน FocusArea แยกต่างหากจากแอปอื่นๆ ของคุณ แถบแท็บและ ควรแยกองค์ประกอบการนำทางอื่นๆ ออกจากส่วนอื่นๆ ของแอปด้วย รายการขนาดใหญ่ โดยทั่วไปควรมี FocusArea เป็นของตัวเอง หากไม่เป็นเช่นนั้น ผู้ใช้ต้องหมุนเวียนไปเรื่อยๆ รายการทั้งหมดเพื่อเข้าถึงมุมมองบางรายการ

FocusArea เป็นคลาสย่อยของ LinearLayout ใน car-ui-library เมื่อเปิดใช้ฟีเจอร์นี้ FocusArea จะวาดไฮไลต์เมื่อ องค์ประกอบสืบทอดจะเน้น ดูข้อมูลเพิ่มเติมได้ที่ โฟกัสการปรับแต่งไฮไลต์

เมื่อสร้างบล็อกการนำทางในไฟล์การออกแบบ หากคุณต้องการใช้ LinearLayout เป็นคอนเทนเนอร์สำหรับบล็อกนั้นๆ ให้ใช้ FocusArea แทน หรือรวมบล็อกไว้ใน FocusArea

อย่าฝัง FocusArea ไว้ใน FocusArea อื่น เพราะจะนำไปสู่ลักษณะการไปยังส่วนต่างๆ ที่ไม่ได้กำหนด ตรวจสอบว่ามุมมองที่โฟกัสได้ทั้งหมด ฝังอยู่ใน FocusArea

ตัวอย่างของ FocusArea ใน RotaryPlayground แสดงอยู่ด้านล่าง

<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 ทำงานดังต่อไปนี้

  1. เมื่อจัดการการทำงานด้วยการหมุนและการกระตุ้น RotaryService จะค้นหาอินสแตนซ์ ของ FocusArea ในลำดับชั้นการแสดงผล
  2. เมื่อได้รับเหตุการณ์การหมุน RotaryService จะย้ายโฟกัสไปยังอื่น มุมมองที่โฟกัสได้ใน FocusArea เดียวกัน
  3. เมื่อได้รับเหตุการณ์การกระตุ้นเตือน RotaryService จะย้ายโฟกัสไปยังมุมมองอื่น ที่สามารถโฟกัสในอีก FocusArea (โดยทั่วไปจะอยู่ติดกัน)

หากคุณไม่รวม FocusAreas ใดๆ ไว้ในเลย์เอาต์ ระบบจะถือว่ามุมมองรูท เป็นส่วนที่เน้นโดยนัย ผู้ใช้ไม่สามารถกระตุ้นการนำทางในแอปได้ แต่พวกเขาจะ เปลี่ยนมุมมองที่โฟกัสได้ทั้งหมด ซึ่งอาจเพียงพอสำหรับกล่องโต้ตอบ

การปรับแต่ง FocusArea

คุณสามารถใช้แอตทริบิวต์มุมมองมาตรฐาน 2 รายการเพื่อปรับแต่งการนำทางด้วยปุ่มหมุนได้ ดังนี้

  • android:nextFocusForward อนุญาตให้นักพัฒนาแอประบุการหมุนเวียน ตามลำดับที่สำคัญ นี่เป็นแอตทริบิวต์เดียวกับที่ใช้ควบคุมลำดับแท็บสำหรับ การไปยังส่วนต่างๆ ด้วยแป้นพิมพ์ อย่าใช้แอตทริบิวต์นี้ในการสร้างการวนซ้ำ แต่ให้ใช้ app:wrapAround (ดูด้านล่าง) เพื่อสร้างวนซ้ำแทน
  • android:focusedByDefault ช่วยให้นักพัฒนาแอปสามารถระบุ มุมมองโฟกัสเริ่มต้นในหน้าต่าง อย่าใช้แอตทริบิวต์นี้และ app:defaultFocus (ดูด้านล่าง) ใน FocusArea เดียวกัน

FocusArea ยังกำหนดแอตทริบิวต์บางรายการเพื่อปรับแต่งการนำทางด้วยปุ่มหมุนด้วย ไม่สามารถปรับแต่งพื้นที่โฟกัสโดยนัยด้วยแอตทริบิวต์เหล่านี้ได้

  1. (Android 11 QPR3, รถยนต์ Android 11 Android 12)
    app:defaultFocus สามารถใช้เพื่อ ระบุรหัสของมุมมององค์ประกอบสืบทอดที่โฟกัสได้ ซึ่งควรมุ่งเน้นเมื่อผู้ใช้ กระตุ้นไปที่ FocusArea นี้
  2. (Android 11 QPR3, รถยนต์ Android 11 Android 12)
    app:defaultFocusOverridesHistory ตั้งค่าเป็น true เพื่อให้มุมมองที่ระบุด้านบนเป็นจุดโฟกัสได้ ประวัติเพื่อระบุว่ามีการเน้นที่มุมมองอื่นใน FocusArea นี้
  3. (Android 12)
    ใช้ app:nudgeLeftShortcut, app:nudgeRightShortcut app:nudgeUpShortcut และ app:nudgeDownShortcut เพื่อระบุรหัสของมุมมององค์ประกอบสืบทอดที่โฟกัสได้ ซึ่งควรโฟกัสเมื่อ สะกิดผู้ใช้ไปในทิศทางที่กำหนด หากต้องการเรียนรู้เพิ่มเติม โปรดดูเนื้อหาของ ปุ่มลัดด้านล่าง

    (Android 11 QPR3, รถยนต์ Android 11 เลิกใช้งานใน Android 12) app:nudgeShortcut และ app:nudgeShortcutDirection รองรับทางลัดการกระตุ้นเตือนเพียง 1 รายการเท่านั้น

  4. (Android 11 QPR3, รถยนต์ Android 11 Android 12)
    เมื่อต้องการเปิดใช้การหมุนเพื่อวนรอบใน FocusArea นี้ app:wrapAround ตั้งค่าเป็น true ได้ ซึ่งมักใช้เมื่อจัดเรียงมุมมองในลักษณะ เป็นวงกลมหรือวงรี
  5. (Android 11 QPR3, รถยนต์ Android 11 Android 12)
    วิธีปรับระยะห่างจากขอบของไฮไลต์ FocusAreaนี้ ใช้ app:highlightPaddingStart app:highlightPaddingEnd app:highlightPaddingTop app:highlightPaddingBottom, app:highlightPaddingHorizontal และ app:highlightPaddingVertical
  6. (Android 11 QPR3, รถยนต์ Android 11 Android 12)
    หากต้องการปรับขอบเขตที่รับรู้ของ FocusArea นี้เพื่อค้นหาเป้าหมายการกระตุ้นเตือน ใช้ app:startBoundOffset, app:endBoundOffset app:topBoundOffset app:bottomBoundOffset app:horizontalBoundOffset และ app:verticalBoundOffset
  7. (Android 11 QPR3, รถยนต์ Android 11 Android 12)
    หากต้องการระบุรหัสของ FocusArea (หรือพื้นที่) ที่อยู่ติดกันในเส้นทางที่กำหนด app:nudgeLeft, app:nudgeRight, app:nudgeUp และ app:nudgeDown ใช้ตัวเลือกนี้เมื่อมีการใช้การค้นหาทางเรขาคณิตเป็นค่าเริ่มต้น ไม่พบเป้าหมายที่ต้องการ

การกระตุ้นเตือนมักเป็นเส้นทางระหว่าง FocusAreas แต่ด้วยทางลัดการกระตุ้นเตือน บางครั้งการกระตุ้นเตือนจะไปยังส่วนต่างๆ ภายใน FocusArea ก่อนเพื่อให้ผู้ใช้อาจจําเป็น เพื่อเลื่อน 2 ครั้งเพื่อไปยัง FocusArea ถัดไป แป้นพิมพ์ลัดของการกระตุ้นเตือนมีประโยชน์ เมื่อ FocusArea มีรายการที่ยาวตามด้วย ปุ่มการทำงานแบบลอย ตามตัวอย่างด้านล่าง

วันที่ ทางลัดการกระตุ้นเตือน
รูปที่ 3 เลื่อนทางลัด

หากไม่มีทางลัดการกระตุ้นเตือน ผู้ใช้จะต้องหมุนเวียนไปเรื่อยๆ จากรายการทั้งหมดเพื่อเข้าถึง FAB

โฟกัสการปรับแต่งไฮไลต์

ดังที่ระบุไว้ข้างต้น RotaryService สร้างขึ้นจากแนวคิดที่มีอยู่ของเฟรมเวิร์ก Android ดูโฟกัส เมื่อผู้ใช้หมุนและเลื่อน RotaryService จะย้ายโฟกัสไปรอบๆ โดยโฟกัสมุมมองหนึ่งและไม่โฟกัสอีกมุมมอง ใน Android เมื่อโฟกัสที่มุมมอง สิ่งที่จะเกิดขึ้นมีดังนี้

  • ได้ระบุการไฮไลต์จุดโฟกัสของตัวเอง Android จะวาดการไฮไลต์การโฟกัสของมุมมอง
  • ไม่ได้ระบุการไฮไลต์จุดโฟกัส และไม่มีการปิดใช้ไฮไลต์โฟกัสเริ่มต้น จะวาดไฮไลต์โฟกัสเริ่มต้นสำหรับมุมมอง

แอปที่ออกแบบมาสำหรับการสัมผัสมักจะไม่ได้ระบุการโฟกัสที่เหมาะสม

เฟรมเวิร์กของ Android จะไฮไลต์โฟกัสเริ่มต้นและลบล้างได้ โดย OEM นักพัฒนาแอปจะได้รับการแจ้งเตือนเมื่อธีมที่ใช้อยู่มาจากธีม Theme.DeviceDefault

ใช้ไฮไลต์โฟกัสเริ่มต้นเมื่อเป็นไปได้เพื่อให้ผู้ใช้ได้รับประสบการณ์ที่สอดคล้องกัน ถ้าต้องการไฮไลต์ที่มีรูปทรงที่กำหนดเอง (เช่น ทรงกลมหรือทรงยา) หรือหาก โดยใช้ธีมที่ไม่ได้มาจาก Theme.DeviceDefault ให้ใช้ Car-ui-library เพื่อกำหนดจุดมุ่งเน้นของคุณเองสำหรับแต่ละมุมมอง

หากต้องการระบุไฮไลต์การโฟกัสแบบกำหนดเองสำหรับมุมมอง ให้เปลี่ยนพื้นหลังหรือพื้นหน้าที่ถอนออกได้ ของมุมมอง เป็นโฆษณาที่ถอนออกได้ ซึ่งจะแตกต่างออกไปเมื่อมีการโฟกัสที่มุมมองนั้น โดยปกติแล้วคุณจะต้องเปลี่ยน พื้นหลัง รายการต่อไปนี้สามารถใช้เป็นพื้นหลังสำหรับมุมมองสี่เหลี่ยมจัตุรัส จะสร้างไฮไลต์ที่เน้นวงกลม

<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 Android 12) การอ้างอิงทรัพยากรที่เป็นตัวหนาในตัวอย่าง ด้านบนจะระบุทรัพยากรที่กำหนดโดย car-ui-library OEM จะลบล้างค่านี้เพื่อให้สอดคล้องกัน พร้อมไฮไลต์การโฟกัสที่เป็นค่าเริ่มต้นที่กำหนดไว้ ซึ่งช่วยให้แน่ใจว่าสีไฮไลต์ที่โฟกัส ความกว้างของเส้นโครงร่าง และอื่นๆ จะไม่เปลี่ยนแปลงเมื่อผู้ใช้ไปยังมุมมองต่างๆ ที่มีโฟกัสที่กำหนดเอง ไฮไลต์และมุมมองที่มีการไฮไลต์โฟกัสเริ่มต้น รายการสุดท้ายเป็นระลอกคลื่นที่ใช้สำหรับการแตะ ค่าเริ่มต้นที่ใช้สำหรับทรัพยากรที่เป็นตัวหนาจะแสดงดังนี้

วันที่ ค่าเริ่มต้นสำหรับทรัพยากรที่เป็นตัวหนา
รูปที่ 4 ค่าเริ่มต้นสำหรับทรัพยากรที่เป็นตัวหนา

นอกจากนี้ จะมีการไฮไลต์การโฟกัสแบบกำหนดเองเมื่อแสดงปุ่มที่ทึบ เพื่อดึงดูดความสนใจของผู้ใช้ ดังเช่นในตัวอย่างด้านล่าง สิ่งนี้สามารถทำให้ ไฮไลต์ที่มองไม่เห็นยาก ในกรณีนี้ ให้ระบุการไฮไลต์จุดโฟกัสที่กำหนดเองโดยใช้ สีรอง:

สีพื้นหลังแบบทึบ
  • (Android 11 QPR3, รถยนต์ Android 11 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

เช่น

โฟกัสอยู่ ไม่ได้กด โฟกัสอยู่ กด
โฟกัส ไม่ได้กด โฟกัส กด

การเลื่อนแบบหมุน

หากแอปใช้ RecyclerView คุณควรใช้ CarUiRecyclerViewแทน ซึ่งจะทำให้ UI ของคุณสอดคล้องกับ อื่นๆ เนื่องจากการปรับแต่งของ OEM มีผลกับ CarUiRecyclerView ทั้งหมด

หากองค์ประกอบในลิสต์สามารถโฟกัสได้ทั้งหมด คุณก็ไม่ต้องดำเนินการใดๆ การนำทางด้วยปุ่มหมุนจะย้ายโฟกัสผ่านองค์ประกอบในรายการ แล้วรายการจะเลื่อน เพื่อแสดงองค์ประกอบที่โฟกัสใหม่

(Android 11 QPR3, รถยนต์ Android 11 Android 12)
ถ้ามีทั้งการโฟกัสและโฟกัสไม่ได้ หรือหากองค์ประกอบทั้งหมดไม่สามารถโฟกัสได้ คุณสามารถเปิดใช้การเลื่อนแบบหมุน ซึ่งช่วยให้ ให้ใช้ปุ่มหมุนควบคุมเพื่อค่อยๆ เลื่อนดูรายการโดยไม่ข้าม รายการที่ไม่สามารถโฟกัสได้ หากต้องการเปิดใช้การเลื่อนแบบหมุน ให้ตั้งค่า app:rotaryScrollEnabled เป็น true

(Android 11 QPR3, รถยนต์ Android 11 Android 12)
คุณสามารถเปิดใช้งานการเลื่อนแบบหมุนได้ มุมมองแบบเลื่อนได้ ซึ่งรวมถึง AvCarUiRecyclerView ที่มีองค์ประกอบ setRotaryScrollEnabled() เมธอดใน CarUiUtils หากคุณทำเช่นนั้น คุณต้องทำดังนี้

  • ทำให้มุมมองที่เลื่อนได้โฟกัสได้ เพื่อที่จะสามารถโฟกัสได้เมื่อไม่มีการโฟกัส มุมมององค์ประกอบสืบทอดที่โฟกัสได้จะปรากฏ
  • ปิดใช้การไฮไลต์โฟกัสเริ่มต้นในมุมมองที่เลื่อนได้ด้วยการเรียกใช้ setDefaultFocusHighlightEnabled(false) เพื่อให้มุมมองที่เลื่อนได้ ดูเหมือนจะไม่มีสมาธิ
  • ตรวจสอบว่าได้โฟกัสที่มุมมองที่เลื่อนได้ก่อนรายการระดับล่างด้วยการเรียก setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
  • ฟังเหตุการณ์การเคลื่อนไหวด้วย SOURCE_ROTARY_ENCODER และ AXIS_VSCROLL หรือ AXIS_HSCROLL เพื่อระบุระยะทางที่จะเลื่อนและ ทิศทาง (ผ่านป้าย)

เมื่อเปิดใช้การเลื่อนแบบหมุนใน CarUiRecyclerView และผู้ใช้หมุน ไปยังบริเวณที่ไม่มีมุมมองที่โฟกัสได้ แถบเลื่อนจะเปลี่ยนจากสีเทาเป็นสีน้ำเงิน เหมือนกับว่า เพื่อบอกสถานะว่าแถบเลื่อนมีโฟกัสอยู่ คุณสามารถนำเอฟเฟกต์ที่คล้ายกันนี้ไปใช้ได้หากต้องการ

MotionEvent จะเหมือนกับเหตุการณ์ที่สร้างโดยล้อเลื่อนของเมาส์ ยกเว้นแหล่งที่มา

โหมดการจัดการโดยตรง

โดยปกติแล้ว การกระตุ้นเตือนและการหมุนจะไปยังส่วนต่างๆ ของอินเทอร์เฟซผู้ใช้ ในขณะที่การกดปุ่มตรงกลาง ดำเนินการบางอย่าง แต่อาจไม่เป็นเช่นนี้เสมอไป เช่น หากผู้ใช้ต้องการปรับ ระดับเสียงปลุก ก็อาจใช้ตัวควบคุมแบบหมุนเพื่อไปยังแถบเลื่อนระดับเสียง แล้วกด ปุ่มตรงกลาง หมุนตัวควบคุมเพื่อปรับระดับเสียงปลุก แล้วกดปุ่มย้อนกลับ เพื่อกลับไปยังการนำทาง วิธีนี้เรียกว่าโหมดการจัดการโดยตรง (DM) ด้วยวิธีนี้ ตัวควบคุมปุ่มหมุนจะใช้ในการโต้ตอบกับมุมมองโดยตรงแทนการนำทาง

ใช้ DM ด้วย 1 ใน 2 วิธีต่อไปนี้ ถ้าคุณแค่ต้องจัดการกับการหมุนและมุมมองที่ต้องการเท่านั้น เพื่อตอบสนอง ACTION_SCROLL_FORWARD และ ACTION_SCROLL_BACKWARD AccessibilityEvent อย่างเหมาะสม ให้ใช้ กลไกที่เรียบง่าย หรือใช้กลไกขั้นสูง

กลไกแบบง่ายเป็นเพียงตัวเลือกเดียวในหน้าต่างระบบ จะใช้กลไกใดก็ได้

กลไกที่ใช้ง่าย

(Android 11 QPR3, รถยนต์ Android 11 Android 12)
แอปของคุณควรเรียกใช้ DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) RotaryService จะจดจำเมื่อผู้ใช้อยู่ในโหมด DM และเข้าสู่โหมด DM เมื่อผู้ใช้อยู่ในโหมด DM กดปุ่มกลางขณะที่มุมมองถูกโฟกัส เมื่ออยู่ในโหมด DM การหมุนจะทํางาน ACTION_SCROLL_FORWARD หรือ ACTION_SCROLL_BACKWARD และออกจากโหมด DM เมื่อผู้ใช้กดปุ่มย้อนกลับ กลไกแบบง่ายจะเปิด/ปิดสถานะที่เลือก มุมมองเมื่อเข้าและออกจากโหมด DM

หากต้องการให้ภาพที่ผู้ใช้อยู่ในโหมด DM คุณต้องทำให้มุมมองของคุณปรากฏแตกต่างออกไป เมื่อเลือกไว้ เช่น เปลี่ยนพื้นหลังเมื่อ android:state_selected คือtrue

กลไกขั้นสูง

แอปจะระบุเวลาที่ RotaryService จะเข้าและออกจากโหมด DM เพื่อความสม่ำเสมอ ประสบการณ์ของผู้ใช้ การกดปุ่มตรงกลางที่โฟกัสด้วยมุมมอง DM ควรเข้าสู่โหมด DM และปุ่มย้อนกลับควรออกจากโหมด DM หากไม่ได้ใช้ปุ่มกลางและ/หรือการกระตุ้นเตือน สามารถใช้เป็นทางเลือกในการออกจากโหมด DM สำหรับแอป เช่น Maps ปุ่มที่แสดง DM ใช้เพื่อเข้าสู่โหมด DM ได้

หากต้องการรองรับโหมด DM ขั้นสูง ให้ใช้มุมมองดังนี้

  1. (Android 11 QPR3, รถยนต์ Android 11 Android 12) ต้องฟัง KEYCODE_DPAD_CENTER เหตุการณ์ที่จะเข้าสู่โหมด DM และรอเหตุการณ์ KEYCODE_BACK เพื่อออกจากโหมด DM จะเรียก DirectManipulationHelper.enableDirectManipulationMode() ในแต่ละกรณี หากต้องการฟังเหตุการณ์เหล่านี้ ให้ทําอย่างใดอย่างหนึ่งต่อไปนี้
    • ลงทะเบียน OnKeyListener
    • หรือ
    • ขยายมุมมองแล้วลบล้างเมธอด dispatchKeyEvent() ของมุมมองนั้น
  2. ควรฟังเหตุการณ์การกระตุ้นเตือน (KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN KEYCODE_DPAD_LEFT หรือ KEYCODE_DPAD_RIGHT) หากมุมมองควร ที่กระตุ้นเตือน
  3. ควรฟัง MotionEvent วินาทีและรับการนับรอบใน AXIS_SCROLL หากมุมมองต้องการจัดการการหมุน ซึ่งทำได้หลายวิธี ดังนี้
    1. ลงทะเบียน OnGenericMotionListener
    2. ขยายมุมมองและลบล้างเมธอด dispatchTouchEvent()
  4. เพื่อหลีกเลี่ยงการค้างอยู่ในโหมด DM ให้ออกจากโหมด DM เมื่อ Fragment หรือกิจกรรมในมุมมอง เป็นของ ไม่มีการโต้ตอบ
  5. ควรมีสัญลักษณ์ภาพเพื่อระบุว่ามุมมองอยู่ในโหมด DM

ดูตัวอย่างมุมมองที่กำหนดเองซึ่งใช้โหมด DM เพื่อเลื่อนและซูมแผนที่ได้ที่ด้านล่าง

/** 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(); }

ดูตัวอย่างเพิ่มเติมได้ใน RotaryPlayground โปรเจ็กต์

มุมมองกิจกรรม

เมื่อใช้มุมมองกิจกรรม ให้ทำดังนี้

  • ActivityView ไม่ควรโฟกัสได้
  • (Android 11 QPR3, รถยนต์ Android 11 เลิกใช้งานแล้วใน Android 11)
    เนื้อหาของ ActivityView ต้องมี FocusParkingView เป็นมุมมองที่โฟกัสได้ครั้งแรก และapp:shouldRestoreFocus ต้องเป็น false
  • เนื้อหาของ ActivityView ไม่ควรมี ยอดดู android:focusByDefault ครั้ง

สำหรับผู้ใช้นั้น ActivityViews ไม่ควรส่งผลใดๆ ต่อการนำทาง ยกเว้นการโฟกัสนั้น พื้นที่ไม่สามารถขยาย ActivityViews ได้ กล่าวคือ คุณไม่สามารถมี ด้านที่มุ่งเน้นเฉพาะ มีเนื้อหาภายในและภายนอก ActivityView หากไม่เพิ่ม FocusAreas ใดๆ ก็ตามไปยัง ActivityView ซึ่งเป็นรูทของลำดับชั้นการแสดงผลใน ActivityView ถือเป็นด้านที่มุ่งเน้นโดยนัย

ปุ่มที่ทำงานเมื่อกดค้างไว้

ปุ่มส่วนใหญ่ทำให้มีการทำงานบางอย่างเมื่อคลิก ปุ่มบางปุ่มจะทำงานเมื่อกดค้างไว้แทน ตัวอย่างเช่น โดยปกติปุ่มกรอไปข้างหน้าและกรอกลับจะทำงานเมื่อกดค้างไว้ เพื่อให้ข้อมูลดังกล่าว ปุ่มหมุนรองรับปุ่มหมุน ฟัง KEYCODE_DPAD_CENTER KeyEvents ดังนี้

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;
});

ซึ่ง mRunnable จะดำเนินการบางอย่าง (เช่น กรอกลับ) และตั้งเวลาให้ เรียกใช้หลังจากความล่าช้า

โหมดสัมผัส

ผู้ใช้สามารถใช้ตัวควบคุมแบบหมุนเพื่อโต้ตอบกับเครื่องเล่นวิทยุในรถยนต์ได้ 2 วิธีดังนี้ จะใช้ตัวควบคุมแบบหมุนหรือแตะหน้าจอก็ได้ เมื่อใช้ตัวควบคุมแบบหมุน มุมมองที่โฟกัสได้มุมมองหนึ่งคือมุมมองที่มีการไฮไลต์ ไม่มีการไฮไลต์โฟกัสเมื่อแตะหน้าจอ จะปรากฏขึ้น ผู้ใช้สามารถสลับระหว่างโหมดป้อนข้อมูลเหล่านี้ได้ทุกเมื่อ

  • หมุน → แตะ เมื่อผู้ใช้แตะหน้าจอ ไฮไลต์โฟกัสจะหายไป
  • แตะ → ปุ่มหมุน เมื่อผู้ใช้เลื่อน หมุน หรือกดปุ่มกลาง ไฮไลต์ที่โฟกัสจะปรากฏขึ้น

ปุ่มย้อนกลับและปุ่มหน้าแรกจะไม่มีผลกับโหมดป้อนข้อมูล

การผูกโยงกับแต่ละแบบในความคิดของ Android โหมดสัมผัส คุณสามารถใช้ View.isInTouchMode() เพื่อระบุว่าผู้ใช้กำลังใช้โหมดป้อนข้อมูลใด คุณสามารถใช้ OnTouchModeChangeListener เพื่อฟังการเปลี่ยนแปลง ขณะที่การตั้งค่านี้สามารถใช้ปรับแต่งอินเทอร์เฟซผู้ใช้ของคุณสำหรับ โหมดป้อนข้อมูล โปรดหลีกเลี่ยงการเปลี่ยนแปลงที่สำคัญ เพราะอาจทำให้เกิดความน่าตกใจ

การแก้ปัญหา

ในแอปที่ออกแบบมาเพื่อการแตะ เป็นเรื่องปกติที่จะมีมุมมองที่โฟกัสได้ที่ซ้อนกันอยู่ ตัวอย่างเช่น อาจมี FrameLayout รอบๆ ImageButton ซึ่งสามารถโฟกัสทั้ง 2 แบบได้ ซึ่งไม่อันตรายต่อการสัมผัส แต่อาจส่งผลทางลบ ประสบการณ์ของผู้ใช้สำหรับปุ่มหมุนเพราะผู้ใช้ต้องหมุนตัวควบคุม 2 ครั้งเพื่อเคลื่อนที่ ก็จะมีมุมมองแบบอินเทอร์แอกทีฟถัดไป เพื่อให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ดี Google ขอแนะนำให้คุณทำอย่างใดอย่างหนึ่งต่อไปนี้ มุมมองด้านนอกหรือด้านในที่โฟกัสได้ แต่ทำทั้ง 2 อย่างไม่ได้

หากปุ่มหรือสวิตช์สูญเสียโฟกัสเมื่อกดผ่านตัวควบคุมแบบหมุน หนึ่งใน อาจมีเงื่อนไขเหล่านี้

  • ปุ่มหรือสวิตช์ถูกปิดใช้งาน (เป็นเวลาสั้นๆ หรือไม่มีกำหนด) เนื่องจาก มีการกดปุ่มอยู่ ไม่ว่าจะเป็นกรณีใดก็ตาม คุณสามารถจัดการกับเรื่องนี้ได้ 2 วิธี ดังนี้
    • คงสถานะ android:enabled ไว้เป็น true และใช้ เพื่อทำให้ปุ่มหรือสวิตช์เป็นสีเทาตามที่อธิบายไว้ใน สถานะที่กำหนดเอง
    • ใช้คอนเทนเนอร์เพื่อล้อมรอบปุ่มหรือสวิตช์และทำให้โฟกัสของคอนเทนเนอร์ได้ แทนปุ่มหรือสวิตช์ (Listener การคลิกต้องอยู่บนคอนเทนเนอร์)
  • มีการเปลี่ยนปุ่มหรือสวิตช์ ตัวอย่างเช่น การดำเนินการที่เกิดขึ้นเมื่อปุ่ม มีการกดหรือสวิตช์ถูกเปิดไว้อาจทริกเกอร์การรีเฟรชของการทำงานที่ใช้ได้ ทำให้ปุ่มใหม่มาแทนที่ปุ่มที่มีอยู่ มี 2 วิธีในการแก้ไขปัญหานี้ ได้แก่
    • แทนที่จะสร้างปุ่มหรือสวิตช์ใหม่ ให้ตั้งค่าไอคอนและ/หรือข้อความของ ปุ่มหรือสวิตช์ที่มีอยู่
    • เพิ่มคอนเทนเนอร์ที่โฟกัสได้รอบปุ่มหรือสวิตช์ดังข้างต้น

สนามเด็กเล่นแบบหมุน

RotaryPlayground เป็นแอปอ้างอิงสำหรับปุ่มหมุน ใช้รายงานนี้เพื่อดูวิธีผสานรวม คุณลักษณะแบบหมุนลงในแอปของคุณ RotaryPlayground รวมอยู่ในบิลด์ของโปรแกรมจำลองและใน บิลด์สําหรับอุปกรณ์ที่ใช้ Android Automotive OS (AAOS)

  • ที่เก็บ RotaryPlayground รายการ: packages/apps/Car/tests/RotaryPlayground/
  • เวอร์ชัน: Android 11 QPR3, Android 11 Car, และ Android 12

แอป RotaryPlayground จะแสดงแท็บต่อไปนี้ทางด้านซ้าย

  • การ์ด ทดสอบการนำทางรอบๆ พื้นที่โฟกัสโดยข้ามองค์ประกอบที่โฟกัสไม่ได้ และการป้อนข้อความ
  • การชักจูงทางตรง วิดเจ็ตทดสอบที่รองรับทั้งแบบง่ายและขั้นสูง โหมดการจัดการโดยตรง แท็บนี้มีไว้สำหรับการควบคุมโดยตรงภายใน หน้าต่างแอป
  • การจัดการ UI ของ Sys วิดเจ็ตทดสอบที่รองรับการควบคุมโดยตรง ในหน้าต่างระบบที่รองรับเฉพาะโหมดการจัดการโดยตรงอย่างง่ายเท่านั้น
  • ตารางกริด ทดสอบการนำทางด้วยปุ่มหมุนแบบ Z ด้วยการเลื่อน
  • การแจ้งเตือน ทดสอบการสะกิดการแจ้งเตือนล่วงหน้าเข้าและออก
  • เลื่อน ทดสอบการเลื่อนผ่านการผสมผสานระหว่างโฟกัสได้และโฟกัสไม่ได้ เนื้อหา
  • WebView ทดสอบการนำทางผ่านลิงก์ใน WebView
  • กำหนดเองFocusArea ทดสอบการปรับแต่ง FocusArea:
    • ใส่ได้ครบถ้วน
    • android:focusedByDefault และ app:defaultFocus
    • เป้าหมายการกระตุ้นเตือนที่ชัดเจน
    • เลื่อนทางลัด
    • FocusArea โดยไม่มีมุมมองที่โฟกัสได้