เนื้อหาต่อไปนี้มีไว้สำหรับนักพัฒนาแอป
เพื่อให้แอปของคุณรองรับแบบหมุน คุณต้อง:
- วาง
FocusParkingView
ในเค้าโครงกิจกรรมตามลำดับ - ตรวจสอบให้แน่ใจว่ามุมมองที่สามารถโฟกัสได้ (หรือไม่ได้)
- ใช้
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
ในการเข้าถึงตัวควบคุมโรตารีจำลอง:
- แตะที่จุดสามจุดที่ด้านล่างของแถบเครื่องมือ:
- เลือก Car Rotary ในหน้าต่างควบคุมแบบขยาย:
แป้นพิมพ์ยูเอสบี
- เสียบแป้นพิมพ์ USB เข้ากับอุปกรณ์ของคุณที่ใช้ Android Automotive OS (AAOS) ในบางกรณี การดำเนินการนี้จะป้องกันไม่ให้แป้นพิมพ์บนหน้าจอปรากฏขึ้น
- ใช้
userdebug
หรือeng
build - เปิดใช้งานการกรองเหตุการณ์สำคัญ:
adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
- ดูตารางด้านล่างเพื่อค้นหาคีย์ที่เกี่ยวข้องสำหรับแต่ละการกระทำ:
สำคัญ การกระทำแบบโรตารี ถาม หมุนทวนเข็มนาฬิกา อี หมุนตามเข็มนาฬิกา ก เขยิบไปทางซ้าย ดี เขยิบไปทางขวา ว เขยิบขึ้น ส เขยิบลง F หรือจุลภาค ปุ่มตรงกลาง R หรือ Esc ปุ่มย้อนกลับ
คำสั่งเอดีบี
คุณสามารถใช้คำสั่ง car_service
เพื่อแทรกเหตุการณ์อินพุตแบบหมุนได้ คำสั่งเหล่านี้สามารถเรียกใช้บนอุปกรณ์ที่ใช้ Android Automotive OS (AAOS) หรือบนโปรแกรมจำลอง
คำสั่ง car_service | อินพุตแบบหมุน |
---|---|
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 ms ที่แล้วและ 50 ms ที่แล้ว) |
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 (car-ui-library) RotaryService
ใช้เพื่อรองรับการนำทางของตัวควบคุมแบบหมุน FocusParkingView
ต้องเป็นมุมมองที่สามารถโฟกัสได้แรกในเค้าโครง ต้องวางไว้นอก FocusArea
ทั้งหมด แต่ละหน้าต่างจะต้องมี 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
:
- Android ไม่ล้างโฟกัสโดยอัตโนมัติเมื่อตั้งค่าโฟกัสในหน้าต่างอื่น หากคุณพยายามล้างโฟกัสในหน้าต่างก่อนหน้า Android จะปรับโฟกัสมุมมองในหน้าต่างนั้นใหม่ ซึ่งส่งผลให้หน้าต่างสองบานถูกโฟกัสพร้อมกัน การเพิ่ม
FocusParkingView
ลงในแต่ละหน้าต่างสามารถแก้ไขปัญหานี้ได้ มุมมองนี้มีความโปร่งใส และไฮไลต์โฟกัสเริ่มต้นจะถูกปิดใช้งาน เพื่อให้ผู้ใช้มองไม่เห็นไม่ว่าจะโฟกัสหรือไม่ก็ตาม สามารถจับโฟกัสเพื่อให้RotaryService
สามารถ จอด โฟกัสไว้เพื่อลบไฮไลท์โฟกัสออก - หากมี
FocusArea
เพียงอันเดียวในหน้าต่างปัจจุบัน การหมุนตัวควบคุมในFocusArea
จะทำให้RotaryService
ย้ายโฟกัสจากมุมมองด้านขวาไปยังมุมมองด้านซ้าย (และในทางกลับกัน) การเพิ่มมุมมองนี้ลงในแต่ละหน้าต่างสามารถแก้ไขปัญหาได้ เมื่อRotaryService
กำหนดเป้าหมายโฟกัสคือFocusParkingView
ก็สามารถตรวจสอบการวนรอบที่จะเกิดขึ้น ณ จุดที่มันจะหลีกเลี่ยงการวนรอบโดยไม่ย้ายโฟกัส - เมื่อตัวควบคุมแบบหมุนเปิดแอพ Android จะโฟกัสมุมมองแรกที่โฟกัสได้ ซึ่งก็คือ
FocusParkingView
เสมอFocusParkingView
จะกำหนดมุมมองที่เหมาะสมที่สุดในการโฟกัส จากนั้นจึงโฟกัส
มุมมองที่สามารถโฟกัสได้
RotaryService
สร้างขึ้นจากแนวคิด ที่มีอยู่ ของกรอบงาน Android เกี่ยวกับการโฟกัสมุมมอง ย้อนกลับไปเมื่อโทรศัพท์มีแป้นพิมพ์กายภาพและ D-pad แอตทริบิวต์ android:nextFocusForward
ที่มีอยู่ถูกนำมาใช้ใหม่สำหรับการหมุน (ดู การปรับแต่ง FocusArea ) แต่ android:nextFocusLeft
, android:nextFocusRight
, android:nextFocusUp
และ android:nextFocusDown
ไม่ใช่
RotaryService
มุ่งเน้นเฉพาะมุมมองที่สามารถโฟกัสได้เท่านั้น มุมมองบางอย่าง เช่น Button
มักจะสามารถโฟกัสได้ ส่วนอื่นๆ เช่น TextView
s และ ViewGroup
s มักไม่เป็นเช่นนั้น มุมมองที่คลิกได้นั้นสามารถโฟกัสได้โดยอัตโนมัติ และมุมมองนั้นสามารถคลิกได้โดยอัตโนมัติเมื่อมีตัวฟังการคลิก หากตรรกะอัตโนมัตินี้ส่งผลให้ได้ความสามารถในการโฟกัสที่ต้องการ คุณไม่จำเป็นต้องตั้งค่าความสามารถในการโฟกัสของมุมมองอย่างชัดเจน หากตรรกะอัตโนมัติไม่ส่งผลให้สามารถโฟกัสได้ตามต้องการ ให้ตั้งค่าแอตทริบิวต์ android:focusable
เป็น true
หรือ false
หรือตั้งค่าความสามารถในการโฟกัสของมุมมองโดยทางโปรแกรมด้วย View.setFocusable(boolean)
เพื่อให้ RotaryService
มุ่งเน้นไปที่มุมมองนั้นจะต้องเป็นไปตามข้อกำหนดต่อไปนี้:
- สามารถโฟกัสได้
- เปิดใช้งานแล้ว
- มองเห็นได้
- มีค่าความกว้างและความสูงที่ไม่เป็นศูนย์
หากมุมมองไม่ตรงตามข้อกำหนดเหล่านี้ทั้งหมด เช่น ปุ่มที่สามารถโฟกัสได้แต่ปิดใช้งานอยู่ ผู้ใช้จะไม่สามารถใช้ตัวควบคุมแบบหมุนเพื่อโฟกัสไปที่มุมมองนั้นได้ หากคุณต้องการมุ่งเน้นไปที่มุมมองที่ปิดใช้งาน ให้พิจารณาใช้สถานะที่กำหนดเองแทน android:state_enabled
เพื่อควบคุมวิธีที่มุมมองปรากฏโดยไม่ระบุว่า Android ควรพิจารณาว่าถูกปิดใช้งาน แอปของคุณสามารถแจ้งให้ผู้ใช้ทราบว่าเหตุใดมุมมองจึงถูกปิดใช้งานเมื่อแตะ ส่วนถัดไปจะอธิบายวิธีการทำเช่นนี้
สถานะที่กำหนดเอง
หากต้องการเพิ่มสถานะที่กำหนดเอง:
- หากต้องการเพิ่ม แอตทริบิวต์ที่กำหนดเอง ให้กับมุมมองของคุณ ตัวอย่างเช่น หากต้องการเพิ่มสถานะแบบกำหนดเอง
state_rotary_enabled
ให้กับคลาสมุมมองCustomView
ให้ใช้:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- หากต้องการติดตามสถานะนี้ ให้เพิ่มตัวแปรอินสแตนซ์ลงในมุมมองของคุณพร้อมกับวิธีการเข้าถึง:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- หากต้องการอ่านค่าแอตทริบิวต์ของคุณเมื่อสร้างมุมมองของคุณ:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- ในคลาสมุมมองของคุณ ให้แทนที่เมธอด
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; }
- ทำให้ตัวจัดการการคลิกของข้อมูลพร็อพเพอร์ตี้ของคุณทำงานแตกต่างออกไปโดยขึ้นอยู่กับสถานะ ตัวอย่างเช่น ตัวจัดการการคลิกอาจไม่ทำอะไรเลยหรืออาจแสดงข้อความแจ้งเตือนเมื่อ
mRotaryEnabled
เป็นfalse
- หากต้องการให้ปุ่มปรากฏว่าปิดใช้งาน ในพื้นหลังของมุมมองที่วาดได้ ให้ใช้
app:state_rotary_enabled
แทนandroid:state_enabled
หากคุณยังไม่มี คุณจะต้องเพิ่ม:xmlns:app="http://schemas.android.com/apk/res-auto"
- หากมุมมองของคุณถูกปิดใช้งานในรูปแบบใด ๆ ให้แทนที่
android:enabled="false"
ด้วยapp:state_rotary_enabled="false"
จากนั้นเพิ่มเนมสเปซของapp
ดังที่กล่าวข้างต้น - หากมุมมองของคุณถูกปิดใช้งานโดยทางโปรแกรม ให้แทนที่การเรียก
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
ทำงานดังนี้:
- เมื่อจัดการการดำเนินการหมุนและดัน
RotaryService
จะค้นหาอินสแตนซ์ของFocusArea
ในลำดับชั้นการดู - เมื่อได้รับเหตุการณ์การหมุน
RotaryService
จะย้ายโฟกัสไปยังมุมมองอื่นที่สามารถโฟกัสในFocusArea
เดียวกันได้ - เมื่อได้รับเหตุการณ์เขยิบ
RotaryService
จะย้ายโฟกัสไปยังมุมมองอื่นที่สามารถโฟกัสในอีกจุดหนึ่ง (โดยทั่วไปจะอยู่ติดกัน)FocusArea
หากคุณไม่ได้รวม FocusAreas
ใดๆ ไว้ในโครงร่างของคุณ มุมมองรูทจะถือเป็นพื้นที่โฟกัสโดยนัย ผู้ใช้ไม่สามารถสะกิดเพื่อนำทางในแอปได้ แต่จะหมุนเวียนไปตามมุมมองที่สามารถโฟกัสได้ทั้งหมด ซึ่งอาจเพียงพอสำหรับกล่องโต้ตอบ
การปรับแต่งพื้นที่โฟกัส
คุณลักษณะ View มาตรฐานสองรายการสามารถใช้เพื่อปรับแต่งการนำทางแบบหมุนได้:
-
android:nextFocusForward
ช่วยให้นักพัฒนาแอประบุลำดับการหมุนในพื้นที่โฟกัสได้ นี่เป็นแอตทริบิวต์เดียวกับที่ใช้ควบคุมลำดับแท็บสำหรับการนำทางด้วยแป้นพิมพ์ อย่า ใช้แอตทริบิวต์นี้เพื่อสร้างการวนซ้ำ ให้ใช้app:wrapAround
(ดูด้านล่าง) เพื่อสร้างลูปแทน -
android:focusedByDefault
ช่วยให้นักพัฒนาแอประบุมุมมองโฟกัสเริ่มต้นในหน้าต่างได้ อย่า ใช้แอตทริบิวต์นี้และapp:defaultFocus
(ดูด้านล่าง) ในFocusArea
เดียวกัน
FocusArea
ยังกำหนดคุณลักษณะบางอย่างเพื่อปรับแต่งการนำทางแบบหมุน พื้นที่โฟกัสโดยนัยไม่สามารถปรับแต่งด้วยคุณลักษณะเหล่านี้ได้
- ( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
app:defaultFocus
สามารถใช้เพื่อระบุ ID ของมุมมองสืบทอดที่สามารถโฟกัสได้ ซึ่งควรเน้นไปที่เมื่อผู้ใช้สะกิดไปที่FocusArea
นี้ - ( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
app:defaultFocusOverridesHistory
สามารถตั้งค่าเป็นtrue
เพื่อให้มุมมองที่ระบุข้างต้นได้รับการโฟกัส แม้ว่าจะมีประวัติบ่งชี้มุมมองอื่นในFocusArea
นี้ที่ได้รับการโฟกัสอยู่ก็ตาม - ( แอนดรอยด์ 12 )
ใช้app:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
และapp:nudgeDownShortcut
เพื่อระบุ ID ของมุมมองสืบทอดที่สามารถโฟกัสได้ ซึ่งควรเน้นเมื่อผู้ใช้เขยิบไปในทิศทางที่กำหนด หากต้องการเรียนรู้เพิ่มเติม โปรดดูเนื้อหาสำหรับ ทางลัดสะกิด ด้านล่าง( Android 11 QPR3, Android 11 Car เลิกใช้งานใน Android 12 )
app:nudgeShortcut
และapp:nudgeShortcutDirection
รองรับทางลัดสะกิดเดียวเท่านั้น - ( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
หากต้องการเปิดใช้งานการหมุนเพื่อพันรอบในFocusArea
นี้app:wrapAround
สามารถตั้งค่าเป็นtrue
ได้ โดยทั่วไปจะใช้เมื่อจัดเรียงมุมมองเป็นวงกลมหรือวงรี - ( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
หากต้องการปรับช่องว่างภายในของไฮไลต์ในFocusArea
นี้ ให้ใช้app:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
และapp:highlightPaddingVertical
- ( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
หากต้องการปรับขอบเขตการรับรู้ของFocusArea
นี้เพื่อค้นหาเป้าหมายที่เขยิบ ให้ใช้app:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
,app:horizontalBoundOffset
และapp:verticalBoundOffset
- ( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
หากต้องการระบุ ID ของFocusArea
(หรือพื้นที่) ที่อยู่ติดกันอย่างชัดเจนในทิศทางที่กำหนด ให้ใช้app:nudgeLeft
,app:nudgeRight
,app:nudgeUp
และapp:nudgeDown
ใช้สิ่งนี้เมื่อการค้นหาทางเรขาคณิตที่ใช้เป็นค่าเริ่มต้นไม่พบเป้าหมายที่ต้องการ
โดยปกติการเขยิบจะนำทางไปมาระหว่าง FocusAreas แต่ด้วยทางลัดการสะกิด บางครั้งการสะกิดจะเป็นการนำทางภายใน FocusArea
ก่อน ดังนั้นผู้ใช้อาจต้องสะกิดสองครั้งเพื่อนำทางไปยัง FocusArea
ถัดไป ทางลัดเขยิบมีประโยชน์เมื่อ FocusArea
มีรายการแบบยาวตามด้วย ปุ่มการดำเนินการแบบลอยตัว ดังตัวอย่างด้านล่าง:
หากไม่มีทางลัดสะกิด ผู้ใช้จะต้องหมุนดูรายการทั้งหมดเพื่อไปที่ FAB
การปรับแต่งโฟกัสเน้น
ตามที่ระบุไว้ข้างต้น RotaryService
สร้างขึ้นจากแนวคิดที่มีอยู่ของกรอบงาน Android เกี่ยวกับการโฟกัสมุมมอง เมื่อผู้ใช้หมุนและเขยิบ RotaryService
จะย้ายโฟกัสไปรอบๆ โดยโฟกัสไปที่มุมมองหนึ่งและไม่โฟกัสอีกมุมมองหนึ่ง ใน Android เมื่อมุมมองถูกโฟกัส หากมุมมอง:
- ได้ระบุไฮไลท์โฟกัสของตัวเองแล้ว 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 Car, Android 12 ) การอ้างอิงทรัพยากร ตัวหนา ในตัวอย่างด้านบนระบุทรัพยากรที่กำหนดโดย car-ui-library OEM จะแทนที่สิ่งเหล่านี้เพื่อให้สอดคล้องกับไฮไลต์โฟกัสเริ่มต้นที่พวกเขาระบุ เพื่อให้แน่ใจว่าสีไฮไลต์โฟกัส ความกว้างของเส้นขีด และอื่นๆ จะไม่เปลี่ยนแปลงเมื่อผู้ใช้นำทางระหว่างมุมมองที่มีการเน้นโฟกัสแบบกำหนดเองและมุมมองที่มีการเน้นโฟกัสเริ่มต้น สุดท้ายเป็นระลอกคลื่นที่ใช้สัมผัส ค่าเริ่มต้นที่ใช้สำหรับทรัพยากรตัวหนาปรากฏดังนี้:
นอกจากนี้ จะมีการเรียกไฮไลต์โฟกัสแบบกำหนดเองเมื่อปุ่มได้รับสีพื้นหลังทึบเพื่อให้ผู้ใช้สนใจ ดังตัวอย่างด้านล่าง ซึ่งจะทำให้มองเห็นจุดเน้นโฟกัสได้ยาก ในสถานการณ์นี้ ให้ระบุการเน้นโฟกัสแบบกำหนดเองโดยใช้สี รอง :
- ( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
car_ui_rotary_focus_fill_secondary_color
car_ui_rotary_focus_stroke_secondary_color
- ( แอนดรอยด์ 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 )
คุณสามารถเปิดใช้งานการเลื่อนแบบหมุนในมุมมองแบบเลื่อนได้ รวมถึง av CarUiRecyclerView
ด้วยเมธอด setRotaryScrollEnabled()
ใน CarUiUtils
หากคุณทำเช่นนั้น คุณจะต้อง:
- ทำให้มุมมองแบบเลื่อนสามารถโฟกัสได้เพื่อให้สามารถโฟกัสได้เมื่อไม่เห็นมุมมองสืบทอดที่สามารถโฟกัสได้
- ปิดใช้งานการเน้นโฟกัสเริ่มต้นในมุมมองที่เลื่อนได้โดยการเรียก
setDefaultFocusHighlightEnabled(false)
เพื่อให้มุมมองที่เลื่อนได้ไม่ปรากฏว่ามีโฟกัส - ตรวจสอบให้แน่ใจว่ามุมมองที่เลื่อนได้นั้นได้รับการโฟกัสก่อนที่จะสืบทอดโดยการเรียก
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
- ฟัง MotionEvents ด้วย
SOURCE_ROTARY_ENCODER
และAXIS_VSCROLL
หรือAXIS_HSCROLL
เพื่อระบุระยะทางในการเลื่อนและทิศทาง (ผ่านป้าย)
เมื่อเปิดใช้งานการเลื่อนแบบหมุนบน CarUiRecyclerView
และผู้ใช้หมุนไปยังพื้นที่ที่ไม่มีมุมมองที่สามารถโฟกัสได้ แถบเลื่อนจะเปลี่ยนจากสีเทาเป็นสีน้ำเงิน ราวกับว่าเป็นการระบุว่าแถบเลื่อนถูกโฟกัสอยู่ คุณสามารถใช้เอฟเฟกต์ที่คล้ายกันได้หากต้องการ
MotionEvents จะเหมือนกับที่สร้างโดยล้อเลื่อนบนเมาส์ ยกเว้นแหล่งที่มา
โหมดการจัดการโดยตรง
โดยปกติ การสะกิดและการหมุนจะนำทางผ่านอินเทอร์เฟซผู้ใช้ ในขณะที่กดปุ่มกลางเพื่อดำเนินการ แม้ว่าจะไม่ได้เป็นเช่นนั้นเสมอไปก็ตาม ตัวอย่างเช่น หากผู้ใช้ต้องการปรับระดับเสียงปลุก พวกเขาอาจใช้ตัวควบคุมแบบหมุนเพื่อนำทางไปยังแถบเลื่อนระดับเสียง กดปุ่มกลาง หมุนตัวควบคุมเพื่อปรับระดับเสียงปลุก จากนั้นกดปุ่มย้อนกลับเพื่อกลับสู่การนำทาง . สิ่งนี้เรียกว่าโหมด การจัดการโดยตรง (DM) ในโหมดนี้ ตัวควบคุมแบบหมุนจะใช้เพื่อโต้ตอบกับมุมมองโดยตรง แทนที่จะใช้เพื่อนำทาง
ใช้งาน DM ด้วยวิธีใดวิธีหนึ่งจากสองวิธี หากคุณต้องการจัดการการหมุนและมุมมองที่คุณต้องการจัดการตอบสนองต่อ ACTION_SCROLL_FORWARD
และ ACTION_SCROLL_BACKWARD
AccessibilityEvent
อย่างเหมาะสม ให้ใช้กลไก ง่ายๆ มิฉะนั้นให้ใช้กลไก ขั้นสูง
กลไกง่ายๆ เป็นเพียงตัวเลือกเดียวในหน้าต่างระบบ แอพสามารถใช้กลไกใดกลไกหนึ่งได้
กลไกง่ายๆ
( Android 11 QPR3, Android 11 รถยนต์, Android 12 )
แอปของคุณควรเรียก DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
RotaryService
รับรู้เมื่อผู้ใช้อยู่ในโหมด 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 ขั้นสูง มุมมอง:
- ( Android 11 QPR3, Android 11 Car, Android 12 ) ต้องฟังเหตุการณ์
KEYCODE_DPAD_CENTER
เพื่อเข้าสู่โหมด DM และฟังเหตุการณ์KEYCODE_BACK
เพื่อออกจากโหมด DM โดยเรียกDirectManipulationHelper.enableDirectManipulationMode()
ในแต่ละกรณี หากต้องการฟังเหตุการณ์เหล่านี้ ให้ปฏิบัติตามวิธีใดวิธีหนึ่งต่อไปนี้:- ลงทะเบียน
OnKeyListener
หรือ, - ขยายมุมมองแล้วแทนที่วิธี
dispatchKeyEvent()
- ลงทะเบียน
- ควรรับฟังเหตุการณ์เขยิบ (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
หรือKEYCODE_DPAD_RIGHT
) หากมุมมองควรจัดการการเขยิบ - ควรฟัง
MotionEvent
และรับจำนวนการหมุนในAXIS_SCROLL
หากมุมมองต้องการจัดการการหมุน มีหลายวิธีในการทำเช่นนี้:- ลงทะเบียน
OnGenericMotionListener
- ขยายมุมมองและแทนที่วิธี
dispatchTouchEvent()
- ลงทะเบียน
- เพื่อหลีกเลี่ยงการติดค้างอยู่ในโหมด DM ต้องออกจากโหมด DM เมื่อแฟรกเมนต์หรือกิจกรรมที่อยู่ในมุมมองนั้นไม่โต้ตอบ
- ควรจัดเตรียมสัญญาณภาพเพื่อระบุว่ามุมมองอยู่ในโหมด 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:
-
ActivityView
ไม่ควรโฟกัสได้ - ( Android 11 QPR3, Android 11 Car, เลิกใช้งานแล้วใน 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
จะดำเนินการ (เช่น การกรอกลับ) และกำหนดเวลาให้ตัวเองทำงานหลังจากการหน่วงเวลา
โหมดสัมผัส
ผู้ใช้สามารถใช้ตัวควบคุมแบบหมุนเพื่อโต้ตอบกับชุดหูฟังในรถยนต์ได้สองวิธี โดยใช้ตัวควบคุมแบบหมุนหรือโดยการสัมผัสหน้าจอ เมื่อใช้ตัวควบคุมแบบหมุน มุมมองใดมุมมองหนึ่งที่สามารถโฟกัสได้จะถูกไฮไลท์ เมื่อสัมผัสหน้าจอ จะไม่มีไฮไลท์โฟกัสปรากฏขึ้น ผู้ใช้สามารถสลับระหว่างโหมดอินพุตเหล่านี้ได้ตลอดเวลา:
- โรตารี → สัมผัส เมื่อผู้ใช้สัมผัสหน้าจอ ไฮไลท์โฟกัสจะหายไป
- สัมผัส → หมุน เมื่อผู้ใช้ขยับ หมุน หรือกดปุ่มกลาง ไฮไลท์โฟกัสจะปรากฏขึ้น
ปุ่มย้อนกลับและปุ่มโฮมไม่มีผลกับโหมดอินพุต
หมูหลังแบบหมุนบนแนวคิด โหมดสัมผัส ที่มีอยู่ของ Android คุณสามารถใช้ View.isInTouchMode()
เพื่อกำหนดโหมดอินพุตที่ผู้ใช้ใช้อยู่ คุณสามารถใช้ OnTouchModeChangeListener
เพื่อฟังการเปลี่ยนแปลง แม้ว่าสิ่งนี้สามารถใช้เพื่อปรับแต่งอินเทอร์เฟซผู้ใช้ของคุณสำหรับโหมดอินพุตปัจจุบันได้ แต่ควรหลีกเลี่ยงการเปลี่ยนแปลงที่สำคัญใด ๆ เนื่องจากอาจทำให้สับสนได้
การแก้ไขปัญหา
ในแอปที่ออกแบบมาเพื่อระบบสัมผัส เป็นเรื่องปกติที่จะมีมุมมองที่สามารถโฟกัสซ้อนกันได้ ตัวอย่างเช่น อาจมี FrameLayout
รอบๆ ImageButton
ซึ่งทั้งสองอย่างนี้สามารถโฟกัสได้ การดำเนินการนี้ไม่เป็นอันตรายต่อการสัมผัส แต่อาจส่งผลให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดีในการหมุน เนื่องจากผู้ใช้ต้องหมุนตัวควบคุมสองครั้งเพื่อย้ายไปยังมุมมองโต้ตอบถัดไป เพื่อประสบการณ์ผู้ใช้ที่ดี Google ขอแนะนำให้คุณทำให้มุมมองด้านนอกหรือด้านในสามารถโฟกัสได้ แต่ไม่ใช่ทั้งสองอย่าง
หากปุ่มหรือสวิตช์สูญเสียโฟกัสเมื่อกดผ่านตัวควบคุมแบบหมุน อาจเกิดเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้:
- ปุ่มหรือสวิตช์ถูกปิดใช้งาน (ชั่วขณะหรือไม่มีกำหนด) เนื่องจากการกดปุ่ม ไม่ว่าในกรณีใด มีสองวิธีในการแก้ไขปัญหานี้:
- ปล่อยให้สถานะ
android:enabled
เป็นtrue
และใช้สถานะแบบกำหนดเองเพื่อทำให้ปุ่มหรือสวิตช์เป็นสีเทาตามที่อธิบายไว้ใน Custom State - ใช้คอนเทนเนอร์ล้อมรอบปุ่มหรือสวิตช์ และทำให้คอนเทนเนอร์สามารถโฟกัสได้แทนปุ่มหรือสวิตช์ (ตัวฟังการคลิกจะต้องอยู่บนคอนเทนเนอร์)
- ปล่อยให้สถานะ
- กำลังเปลี่ยนปุ่มหรือสวิตช์ ตัวอย่างเช่น การดำเนินการที่เกิดขึ้นเมื่อกดปุ่มหรือสวิตช์อาจกระตุ้นให้มีการรีเฟรชการดำเนินการที่มีอยู่ ส่งผลให้ปุ่มใหม่เข้ามาแทนที่ปุ่มที่มีอยู่ มีสองวิธีในการแก้ไขปัญหานี้:
- แทนที่จะสร้างปุ่มหรือสวิตช์ใหม่ ให้ตั้งค่าไอคอนและ/หรือข้อความของปุ่มหรือสวิตช์ที่มีอยู่
- ดังที่กล่าวข้างต้น ให้เพิ่มคอนเทนเนอร์ที่สามารถโฟกัสได้รอบๆ ปุ่มหรือสวิตช์
สนามเด็กเล่นโรตารี
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
-
FocusArea
แบบกำหนดเอง ทดสอบการปรับแต่งFocusArea
:- ห่อรอบ ๆ.
-
android:focusedByDefault
และapp:defaultFocus
. - สะกิดเป้าหมายที่ชัดเจน
- เขยิบทางลัด
-
FocusArea
ที่ไม่มีมุมมองที่สามารถโฟกัสได้
เนื้อหาต่อไปนี้มีไว้สำหรับนักพัฒนาแอป
เพื่อให้แอปของคุณรองรับแบบหมุน คุณต้อง:
- วาง
FocusParkingView
ในเค้าโครงกิจกรรมที่เกี่ยวข้อง - ตรวจสอบให้แน่ใจว่ามุมมองที่สามารถโฟกัสได้ (หรือไม่ได้)
- ใช้
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
ในการเข้าถึงตัวควบคุมโรตารีจำลอง:
- แตะที่จุดสามจุดที่ด้านล่างของแถบเครื่องมือ:
- เลือก Car Rotary ในหน้าต่างควบคุมแบบขยาย:
แป้นพิมพ์ยูเอสบี
- เสียบแป้นพิมพ์ USB เข้ากับอุปกรณ์ของคุณที่ใช้ Android Automotive OS (AAOS) ในบางกรณี การดำเนินการนี้จะป้องกันไม่ให้แป้นพิมพ์บนหน้าจอปรากฏขึ้น
- ใช้
userdebug
หรือeng
build - เปิดใช้งานการกรองเหตุการณ์สำคัญ:
adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
- ดูตารางด้านล่างเพื่อค้นหาคีย์ที่เกี่ยวข้องสำหรับแต่ละการกระทำ:
สำคัญ การกระทำแบบโรตารี ถาม หมุนทวนเข็มนาฬิกา อี หมุนตามเข็มนาฬิกา ก เขยิบไปทางซ้าย ดี เขยิบไปทางขวา ว เขยิบขึ้น ส เขยิบลง F หรือจุลภาค ปุ่มตรงกลาง R หรือ Esc ปุ่มย้อนกลับ
คำสั่งเอดีบี
คุณสามารถใช้คำสั่ง car_service
เพื่อแทรกเหตุการณ์อินพุตแบบหมุนได้ คำสั่งเหล่านี้สามารถเรียกใช้บนอุปกรณ์ที่ใช้ Android Automotive OS (AAOS) หรือบนโปรแกรมจำลอง
คำสั่ง car_service | อินพุตแบบหมุน |
---|---|
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 ms ที่แล้วและ 50 ms ที่แล้ว) |
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 (car-ui-library) RotaryService
ใช้เพื่อรองรับการนำทางของตัวควบคุมแบบหมุน FocusParkingView
ต้องเป็นมุมมองที่สามารถโฟกัสได้แรกในเค้าโครง ต้องวางไว้นอก FocusArea
ทั้งหมด แต่ละหน้าต่างจะต้องมี 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
:
- Android ไม่ล้างโฟกัสโดยอัตโนมัติเมื่อตั้งค่าโฟกัสในหน้าต่างอื่น หากคุณพยายามล้างโฟกัสในหน้าต่างก่อนหน้า Android จะปรับโฟกัสมุมมองในหน้าต่างนั้นใหม่ ซึ่งส่งผลให้หน้าต่างสองบานถูกโฟกัสพร้อมกัน การเพิ่ม
FocusParkingView
ลงในแต่ละหน้าต่างสามารถแก้ไขปัญหานี้ได้ มุมมองนี้มีความโปร่งใส และไฮไลต์โฟกัสเริ่มต้นจะถูกปิดใช้งาน เพื่อให้ผู้ใช้มองไม่เห็นไม่ว่าจะโฟกัสหรือไม่ก็ตาม สามารถจับโฟกัสเพื่อให้RotaryService
สามารถ จอด โฟกัสไว้เพื่อลบไฮไลท์โฟกัสออก - หากมี
FocusArea
เพียงอันเดียวในหน้าต่างปัจจุบัน การหมุนตัวควบคุมในFocusArea
จะทำให้RotaryService
ย้ายโฟกัสจากมุมมองด้านขวาไปยังมุมมองด้านซ้าย (และในทางกลับกัน) การเพิ่มมุมมองนี้ลงในแต่ละหน้าต่างสามารถแก้ไขปัญหาได้ เมื่อRotaryService
กำหนดเป้าหมายโฟกัสคือFocusParkingView
ก็สามารถตรวจสอบการวนรอบที่จะเกิดขึ้น ณ จุดที่มันจะหลีกเลี่ยงการวนรอบโดยไม่ย้ายโฟกัส - เมื่อตัวควบคุมแบบหมุนเปิดแอพ Android จะโฟกัสมุมมองแรกที่โฟกัสได้ ซึ่งก็คือ
FocusParkingView
เสมอFocusParkingView
จะกำหนดมุมมองที่เหมาะสมที่สุดในการโฟกัส จากนั้นจึงโฟกัส
มุมมองที่สามารถโฟกัสได้
RotaryService
สร้างขึ้นจากแนวคิด ที่มีอยู่ ของกรอบงาน Android เกี่ยวกับการโฟกัสมุมมอง ย้อนกลับไปเมื่อโทรศัพท์มีแป้นพิมพ์กายภาพและ D-pad แอตทริบิวต์ android:nextFocusForward
ที่มีอยู่ถูกนำมาใช้ใหม่สำหรับการหมุน (ดู การปรับแต่ง FocusArea ) แต่ android:nextFocusLeft
, android:nextFocusRight
, android:nextFocusUp
และ android:nextFocusDown
ไม่ใช่
RotaryService
มุ่งเน้นเฉพาะมุมมองที่สามารถโฟกัสได้เท่านั้น มุมมองบางอย่าง เช่น Button
มักจะสามารถโฟกัสได้ ส่วนอื่นๆ เช่น TextView
s และ ViewGroup
s มักไม่เป็นเช่นนั้น มุมมองที่คลิกได้นั้นสามารถโฟกัสได้โดยอัตโนมัติ และมุมมองนั้นสามารถคลิกได้โดยอัตโนมัติเมื่อมีตัวฟังการคลิก หากตรรกะอัตโนมัตินี้ส่งผลให้ได้ความสามารถในการโฟกัสที่ต้องการ คุณไม่จำเป็นต้องตั้งค่าความสามารถในการโฟกัสของมุมมองอย่างชัดเจน หากตรรกะอัตโนมัติไม่ส่งผลให้สามารถโฟกัสได้ตามต้องการ ให้ตั้งค่าแอตทริบิวต์ android:focusable
เป็น true
หรือ false
หรือตั้งค่าความสามารถในการโฟกัสของมุมมองโดยทางโปรแกรมด้วย View.setFocusable(boolean)
เพื่อให้ RotaryService
มุ่งเน้นไปที่มุมมองนั้นจะต้องเป็นไปตามข้อกำหนดต่อไปนี้:
- สามารถโฟกัสได้
- เปิดใช้งานแล้ว
- มองเห็นได้
- มีค่าความกว้างและความสูงที่ไม่เป็นศูนย์
หากมุมมองไม่ตรงตามข้อกำหนดเหล่านี้ทั้งหมด เช่น ปุ่มที่สามารถโฟกัสได้แต่ปิดใช้งานอยู่ ผู้ใช้จะไม่สามารถใช้ตัวควบคุมแบบหมุนเพื่อโฟกัสไปที่มุมมองนั้นได้ หากคุณต้องการมุ่งเน้นไปที่มุมมองที่ปิดใช้งาน ให้พิจารณาใช้สถานะที่กำหนดเองแทน android:state_enabled
เพื่อควบคุมวิธีที่มุมมองปรากฏโดยไม่ระบุว่า Android ควรพิจารณาว่าถูกปิดใช้งาน แอปของคุณสามารถแจ้งให้ผู้ใช้ทราบว่าเหตุใดมุมมองจึงถูกปิดใช้งานเมื่อแตะ ส่วนถัดไปจะอธิบายวิธีการทำเช่นนี้
สถานะที่กำหนดเอง
หากต้องการเพิ่มสถานะที่กำหนดเอง:
- หากต้องการเพิ่ม แอตทริบิวต์ที่กำหนดเอง ให้กับมุมมองของคุณ ตัวอย่างเช่น หากต้องการเพิ่มสถานะแบบกำหนดเอง
state_rotary_enabled
ให้กับคลาสมุมมองCustomView
ให้ใช้:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- หากต้องการติดตามสถานะนี้ ให้เพิ่มตัวแปรอินสแตนซ์ลงในมุมมองของคุณพร้อมกับวิธีการเข้าถึง:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- หากต้องการอ่านค่าแอตทริบิวต์ของคุณเมื่อสร้างมุมมองของคุณ:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- ในคลาสมุมมองของคุณ ให้แทนที่เมธอด
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; }
- ทำให้ตัวจัดการการคลิกของข้อมูลพร็อพเพอร์ตี้ของคุณทำงานแตกต่างออกไปโดยขึ้นอยู่กับสถานะ ตัวอย่างเช่น ตัวจัดการการคลิกอาจไม่ทำอะไรเลยหรืออาจแสดงข้อความแจ้งเตือนเมื่อ
mRotaryEnabled
เป็นfalse
- หากต้องการให้ปุ่มปรากฏว่าปิดใช้งาน ในพื้นหลังของมุมมองที่วาดได้ ให้ใช้
app:state_rotary_enabled
แทนandroid:state_enabled
หากคุณยังไม่มี คุณจะต้องเพิ่ม:xmlns:app="http://schemas.android.com/apk/res-auto"
- หากมุมมองของคุณถูกปิดใช้งานในรูปแบบใด ๆ ให้แทนที่
android:enabled="false"
ด้วยapp:state_rotary_enabled="false"
จากนั้นเพิ่มเนมสเปซของapp
ดังที่กล่าวข้างต้น - หากมุมมองของคุณถูกปิดใช้งานโดยทางโปรแกรม ให้แทนที่การเรียก
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
ทำงานดังนี้:
- เมื่อจัดการการดำเนินการหมุนและดัน
RotaryService
จะค้นหาอินสแตนซ์ของFocusArea
ในลำดับชั้นการดู - เมื่อได้รับเหตุการณ์การหมุน
RotaryService
จะย้ายโฟกัสไปยังมุมมองอื่นที่สามารถโฟกัสในFocusArea
เดียวกันได้ - เมื่อได้รับเหตุการณ์เขยิบ
RotaryService
ย้ายโฟกัสไปยังมุมมองอื่นที่สามารถมุ่งเน้นไปที่FocusArea
โฟกัสอื่น (โดยทั่วไปอยู่ติดกัน)
หากคุณไม่รวม FocusAreas
ใด ๆ ไว้ในเค้าโครงของคุณมุมมองรูทจะถือว่าเป็นพื้นที่โฟกัสโดยนัย ผู้ใช้ไม่สามารถสะกิดเพื่อนำทางในแอพได้ แต่พวกเขาจะหมุนผ่านมุมมองที่โฟกัสได้ทั้งหมดซึ่งอาจเพียงพอสำหรับบทสนทนา
การปรับแต่งโฟกัส
แอตทริบิวต์มุมมองมาตรฐานสองตัวสามารถใช้ในการปรับแต่งการนำทางแบบโรตารี่:
-
android:nextFocusForward
ช่วยให้นักพัฒนาแอปสามารถระบุลำดับการหมุนในพื้นที่โฟกัส นี่คือแอตทริบิวต์เดียวกับที่ใช้ในการควบคุมลำดับแท็บสำหรับการนำทางแป้นพิมพ์ อย่า ใช้แอตทริบิวต์นี้เพื่อสร้างลูป ให้ใช้app:wrapAround
(ดูด้านล่าง) เพื่อสร้างลูป -
android:focusedByDefault
ช่วยให้นักพัฒนาแอประบุมุมมองโฟกัสเริ่มต้นในหน้าต่าง อย่า ใช้แอตทริบิวต์และapp:defaultFocus
(ดูด้านล่าง) ในFocusArea
เดียวกัน
FocusArea
ยังกำหนดคุณลักษณะบางอย่างเพื่อปรับแต่งการนำทางแบบโรตารี่ พื้นที่โฟกัสโดยนัยไม่สามารถปรับแต่งได้ด้วยแอตทริบิวต์เหล่านี้
- ( Android 11 QPR3, Android 11 Car, Android 12 )
app:defaultFocus
สามารถใช้เพื่อระบุ ID ของมุมมองลูกหลานที่โฟกัสได้ซึ่งควรมุ่งเน้นเมื่อผู้ใช้กดปุ่มFocusArea
นี้ - ( Android 11 QPR3, Android 11 Car, Android 12 )
app:defaultFocusOverridesHistory
สามารถตั้งค่าเป็นtrue
เพื่อให้มุมมองที่ระบุไว้ข้างต้นมุ่งเน้นแม้ว่าจะมีประวัติเพื่อระบุมุมมองอื่นในFocusArea
นี้ได้รับการมุ่งเน้น - ( Android 12 )
ใช้app:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
และapp:nudgeDownShortcut
เพื่อระบุ ID ของมุมมองลูกหลานที่สามารถมุ่งเน้นซึ่งควรมุ่งเน้นไปที่เมื่อผู้ใช้คำสั่งในทิศทางที่กำหนด หากต้องการเรียนรู้เพิ่มเติมดูเนื้อหาสำหรับ ทางลัดเขยิบ ด้านล่าง( Android 11 QPR3, Android 11 Car, เลิกใช้แล้วใน Android 12 )
app:nudgeShortcut
และapp:nudgeShortcutDirection
รองรับทางลัดเท่านั้น - ( Android 11 QPR3, Android 11 Car, Android 12 )
ในการเปิดใช้งานการหมุนรอบตัวในFocusArea
นี้app:wrapAround
สามารถตั้งค่าเป็นtrue
ได้ โดยทั่วไปแล้วจะใช้มากที่สุดเมื่อมีการจัดมุมมองเป็นวงกลมหรือวงรี - ( Android 11 QPR3, Android 11 Car, Android 12 )
ในการปรับช่องว่างของไฮไลต์ในFocusArea
นี้ให้ใช้app:highlightPaddingStart
,app:highlightPaddingEnd
ไฮไลต์ปาดิงเดนด์, แอพ: ไฮไลต์แพดดิงท็อป, แอพ: ไฮไลapp:highlightPaddingBottom
app:highlightPaddingTop
app:highlightPaddingHorizontal
และapp:highlightPaddingVertical
- ( Android 11 QPR3, Android 11 Car, Android 12 )
ในการปรับขอบเขตการรับรู้ของFocusArea
นี้เพื่อค้นหาเป้าหมายเขยิบให้ใช้app:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
app:horizontalBoundOffset
,app:verticalBoundOffset
- ( Android 11 QPR3, Android 11 Car, Android 12 )
หากต้องการระบุ ID ของFocusArea
ที่อยู่ติดกันอย่างชัดเจน (หรือพื้นที่) ในทิศทางที่กำหนดให้ใช้app:nudgeLeft
,app:nudgeRight
,app:nudgeUp
และapp:nudgeDown
ใช้สิ่งนี้เมื่อการค้นหาทางเรขาคณิตที่ใช้โดยค่าเริ่มต้นไม่พบเป้าหมายที่ต้องการ
การสะกิดมักจะนำทางระหว่างโฟกัส แต่ด้วยทางลัดที่สั่นสะเทือนบางครั้งก็นำทางครั้งแรกภายใน FocusArea
เพื่อให้ผู้ใช้อาจต้องเขยิบสองครั้งเพื่อนำทางไปยัง FocusArea
ครั้งต่อไป ทางลัดเขยิบมีประโยชน์เมื่อ FocusArea
มีรายการยาวตามด้วย ปุ่มแอ็คชั่นลอยตัว ดังตัวอย่างด้านล่าง:
หากไม่มีทางลัดเขยิบผู้ใช้จะต้องหมุนผ่านรายการทั้งหมดเพื่อไปถึง Fab
โฟกัสเน้นการปรับแต่ง
ดังที่ได้กล่าวไว้ข้างต้น RotaryService
สร้างขึ้นบนแนวคิดที่มีอยู่ของ Android Framework ของมุมมองโฟกัส เมื่อผู้ใช้หมุนและสะบัด RotaryService
จะย้ายโฟกัสไปรอบ ๆ โดยมุ่งเน้นที่มุมมองหนึ่งและแยกออกจากกัน ใน Android เมื่อมุมมองมุ่งเน้นถ้ามุมมอง:
- ได้ระบุไฮไลต์โฟกัสของตัวเอง Android ดึงไฮไลต์โฟกัสของมุมมอง
- ไม่ได้ระบุไฮไลต์โฟกัสและไฮไลท์โฟกัสเริ่มต้นจะไม่ปิดใช้งาน Android ดึงไฮไลต์โฟกัสเริ่มต้นสำหรับมุมมอง
แอพที่ออกแบบมาสำหรับการสัมผัสมักจะไม่ได้ระบุไฮไลท์โฟกัสที่เหมาะสม
ไฮไลต์โฟกัสเริ่มต้นนั้นจัดทำโดย Android Framework และสามารถแทนที่ได้โดย 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 Car, Android 12 ) การอ้างอิงทรัพยากร ตัวหนา ในตัวอย่างด้านบนระบุทรัพยากรที่กำหนดโดย Car-UI-Library OEM แทนที่สิ่งเหล่านี้ให้สอดคล้องกับไฮไลต์โฟกัสเริ่มต้นที่ระบุไว้ สิ่งนี้ทำให้มั่นใจได้ว่าสีไฮไลต์โฟกัสความกว้างจังหวะและอื่น ๆ จะไม่เปลี่ยนแปลงเมื่อผู้ใช้นำทางระหว่างมุมมองด้วยไฮไลต์โฟกัสแบบกำหนดเองและมุมมองที่มีไฮไลต์โฟกัสเริ่มต้น รายการสุดท้ายคือระลอกคลื่นที่ใช้สำหรับสัมผัส ค่าเริ่มต้นที่ใช้สำหรับทรัพยากรตัวหนาจะปรากฏดังนี้:
นอกจากนี้ไฮไลต์โฟกัสแบบกำหนดเองจะถูกเรียกใช้เมื่อปุ่มได้รับสีพื้นหลังที่เป็นของแข็งเพื่อให้ความสนใจของผู้ใช้ดังตัวอย่างด้านล่าง สิ่งนี้สามารถทำให้โฟกัสไฮไลต์มองเห็นได้ยาก ในสถานการณ์นี้ให้ระบุไฮไลต์โฟกัสแบบกำหนดเองโดยใช้สี รอง :
- ( 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
ตัวอย่างเช่น:
โฟกัสไม่กด | โฟกัสกด |
การเลื่อนแบบหมุน
หากแอปของคุณใช้ RecyclerView
S คุณควรใช้ CarUiRecyclerView
S แทน สิ่งนี้ทำให้มั่นใจได้ว่า UI ของคุณสอดคล้องกับผู้อื่นเพราะการปรับแต่งของ OEM ใช้กับ CarUiRecyclerView
s ทั้งหมด
หากองค์ประกอบในรายการของคุณสามารถมุ่งเน้นได้คุณไม่จำเป็นต้องทำอะไรอีก การนำทางแบบโรตารี่จะย้ายโฟกัสผ่านองค์ประกอบในรายการและรายการเลื่อนเพื่อทำให้องค์ประกอบที่เน้นใหม่สามารถมองเห็นได้
( Android 11 QPR3, Android 11 Car, Android 12 )
หากมีการผสมผสานระหว่างองค์ประกอบที่โฟกัสและไม่สามารถโฟกัสได้หรือหากองค์ประกอบทั้งหมดไม่สามารถโฟกัสได้คุณสามารถเปิดใช้งานการเลื่อนแบบโรตารี่ซึ่งช่วยให้ผู้ใช้สามารถใช้คอนโทรลเลอร์โรตารี่เพื่อค่อยๆเลื่อนดูรายการโดยไม่ต้องข้ามรายการที่ไม่สามารถโฟกัสได้ ในการเปิดใช้งานการเลื่อนแบบโรตารี่ให้ตั้ง app:rotaryScrollEnabled
Attribute เป็น true
( Android 11 QPR3, Android 11 Car, Android 12 )
คุณสามารถเปิดใช้งานการเลื่อนแบบโรตารี่ในมุมมองที่เลื่อนได้ใด ๆ รวมถึง AV CarUiRecyclerView
ด้วยวิธี setRotaryScrollEnabled()
ใน CarUiUtils
ถ้าคุณทำเช่นนั้นคุณต้อง:
- ทำให้มุมมองแบบเลื่อนได้โฟกัสได้เพื่อที่จะสามารถมุ่งเน้นไปที่เมื่อไม่มีมุมมองลูกหลานที่สามารถโฟกัสได้
- ปิดการใช้งานไฮไลต์โฟกัสเริ่มต้นบนมุมมองที่เลื่อนได้โดยเรียก
setDefaultFocusHighlightEnabled(false)
เพื่อให้มุมมองที่เลื่อนได้ไม่ปรากฏขึ้น - ตรวจสอบให้แน่ใจว่ามุมมองที่เลื่อนได้นั้นมุ่งเน้นไปที่ก่อนที่ลูกหลานของมันจะเรียก
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
- ฟัง MotionEvents ด้วย
SOURCE_ROTARY_ENCODER
และAXIS_VSCROLL
หรือAXIS_HSCROLL
เพื่อระบุระยะทางในการเลื่อนและทิศทาง (ผ่านเครื่องหมาย)
เมื่อเปิดใช้งานการเลื่อนแบบโรตารี่บน CarUiRecyclerView
และผู้ใช้จะหมุนไปยังพื้นที่ที่ไม่มีมุมมองที่โฟกัสได้แถบเลื่อนเปลี่ยนจากสีเทาเป็นสีน้ำเงินราวกับว่าระบุว่าแถบเลื่อนจะถูกโฟกัส คุณสามารถใช้เอฟเฟกต์ที่คล้ายกันได้หากคุณต้องการ
MotionEvents นั้นเหมือนกับที่สร้างขึ้นโดยล้อเลื่อนบนเมาส์ยกเว้นแหล่งที่มา
โหมดการจัดการโดยตรง
โดยปกติแล้ว Nudges และการหมุนจะนำทางผ่านส่วนต่อประสานผู้ใช้ในขณะที่ปุ่มกลางกดดำเนินการแม้ว่าจะไม่ได้เป็นเช่นนั้นเสมอไป ตัวอย่างเช่นหากผู้ใช้ต้องการปรับระดับเสียงสัญญาณเตือนพวกเขาอาจใช้คอนโทรลเลอร์โรตารี่เพื่อนำทางไปยังตัวเลื่อนระดับเสียงกดปุ่มกลางหมุนคอนโทรลเลอร์เพื่อปรับระดับเสียงสัญญาณเตือน . สิ่งนี้เรียกว่าโหมด การจัดการโดยตรง (DM) ในโหมดนี้คอนโทรลเลอร์แบบโรตารี่จะใช้ในการโต้ตอบกับมุมมองโดยตรงแทนที่จะนำทาง
ใช้ DM ในหนึ่งในสองวิธี หากคุณต้องการจัดการกับการหมุนและมุมมองที่คุณต้องการจัดการตอบสนองต่อ ACTION_SCROLL_FORWARD
และ ACTION_SCROLL_BACKWARD
AccessibilityEvent
S อย่างเหมาะสมให้ใช้กลไก ง่ายๆ มิฉะนั้นให้ใช้กลไก ขั้นสูง
กลไกง่ายๆเป็นตัวเลือกเดียวใน Windows ระบบ แอพสามารถใช้กลไกอย่างใดอย่างหนึ่ง
กลไกง่ายๆ
( Android 11 QPR3, Android 11 Car, Android 12 )
แอพของคุณควรโทรหา DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
RotaryService
รับรู้เมื่อผู้ใช้อยู่ในโหมด DM และเข้าสู่โหมด DM เมื่อผู้ใช้กดปุ่มกลางในขณะที่มุมมองโฟกัส เมื่ออยู่ในโหมด DM การหมุนจะดำเนิน ACTION_SCROLL_FORWARD
หรือ ACTION_SCROLL_BACKWARD
และออกจากโหมด DM เมื่อผู้ใช้กดปุ่มย้อนกลับ กลไกง่ายๆสลับสถานะของมุมมองที่เลือกเมื่อเข้าและออกจากโหมด DM
เพื่อให้คิวภาพที่ผู้ใช้อยู่ในโหมด DM ทำให้มุมมองของคุณแตกต่างกันเมื่อเลือก ตัวอย่างเช่นเปลี่ยนพื้นหลังเมื่อ android:state_selected
เป็น true
กลไกขั้นสูง
แอพกำหนดเมื่อ RotaryService
เข้าและออกจากโหมด DM สำหรับประสบการณ์การใช้งานที่สอดคล้องกันการกดปุ่มกึ่งกลางด้วยมุมมอง DM ที่เน้นควรเข้าสู่โหมด DM และปุ่มย้อนกลับควรออกจากโหมด DM หากปุ่มกลางและ/หรือการเขยิบไม่ได้ใช้พวกเขาอาจเป็นวิธีอื่นในการออกจากโหมด DM สำหรับแอพเช่นแผนที่ปุ่มเพื่อแสดง DM สามารถใช้ในการเข้าสู่โหมด DM
เพื่อรองรับโหมด DM ขั้นสูงมุมมอง:
- ( Android 11 QPR3, Android 11 Car, Android 12 ) ต้องฟังเหตุการณ์
KEYCODE_DPAD_CENTER
เพื่อเข้าสู่โหมด DM และฟังเหตุการณ์KEYCODE_BACK
เพื่อออกจากโหมด DM โทรDirectManipulationHelper.enableDirectManipulationMode()
ในแต่ละกรณี หากต้องการฟังเหตุการณ์เหล่านี้ทำอย่างใดอย่างหนึ่งต่อไปนี้:- ลงทะเบียน
OnKeyListener
หรือ, - ขยายมุมมองแล้วแทนที่วิธีการ
dispatchKeyEvent()
- ลงทะเบียน
- ควรฟังเหตุการณ์ Nudge (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
หรือKEYCODE_DPAD_RIGHT
) หากมุมมองควรจัดการ nudges - ควรฟัง
MotionEvent
S และรับจำนวนการหมุนในAXIS_SCROLL
หากมุมมองต้องการจัดการการหมุน มีหลายวิธีในการทำเช่นนี้:- ลงทะเบียน
OnGenericMotionListener
- ขยายมุมมองและแทนที่วิธี
dispatchTouchEvent()
- ลงทะเบียน
- เพื่อหลีกเลี่ยงการติดอยู่ในโหมด DM ต้องออกจากโหมด DM เมื่อชิ้นส่วนหรือกิจกรรมมุมมองเป็นของไม่โต้ตอบ
- ควรให้คิวภาพเพื่อระบุว่ามุมมองอยู่ในโหมด DM
ตัวอย่างของมุมมองที่กำหนดเองที่ใช้โหมด DM เพื่อ PAN และซูมแผนที่มีให้ด้านล่าง:
/** 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
เมื่อใช้ ActivityView:
-
ActivityView
ไม่ควรมุ่งเน้น - ( Android 11 QPR3, Android 11 Car, เลิกใช้ใน Android 11 )
เนื้อหาของActivityView
จะต้องมีFocusParkingView
เป็นมุมมองแรกที่สามารถโฟกัสได้และapp:shouldRestoreFocus
ควรจะเป็นfalse
- เนื้อหาของ
ActivityView
ควรไม่มีandroid:focusByDefault
Views
สำหรับผู้ใช้ ActivityViews ไม่ควรมีผลต่อการนำทางยกเว้นพื้นที่โฟกัสไม่สามารถขยายกิจกรรมต่างๆได้ กล่าวอีกนัยหนึ่งคุณไม่สามารถมีพื้นที่โฟกัสเดียวที่มีเนื้อหาทั้งภายใน และ ภายนอก ActivityView
หากคุณไม่ได้เพิ่มโฟกัสใด ๆ ใน 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
ดำเนินการ (เช่นการย้อนกลับ) และกำหนดเวลาให้ตัวเองทำงานหลังจากล่าช้า
โหมดสัมผัส
ผู้ใช้สามารถใช้คอนโทรลเลอร์แบบโรตารี่เพื่อโต้ตอบกับชุดหัวในรถได้สองวิธีไม่ว่าจะโดยใช้คอนโทรลเลอร์โรตารี่หรือโดยการสัมผัสหน้าจอ เมื่อใช้คอนโทรลเลอร์โรตารี่หนึ่งในมุมมองที่โฟกัสได้จะถูกเน้น เมื่อสัมผัสหน้าจอจะไม่มีไฮไลต์โฟกัสปรากฏขึ้น ผู้ใช้สามารถสลับระหว่างโหมดอินพุตเหล่านี้ได้ตลอดเวลา:
- โรตารี่→สัมผัส เมื่อผู้ใช้สัมผัสหน้าจอไฮไลต์โฟกัสจะหายไป
- สัมผัส→โรตารี่ เมื่อผู้ใช้เขยิบหมุนหรือกดปุ่มกึ่งกลางไฮไลท์โฟกัสจะปรากฏขึ้น
ปุ่มด้านหลังและบ้านไม่มีผลต่อโหมดอินพุต
Piggybacks แบบโรตารี่บนแนวคิดที่มีอยู่ของ Android เกี่ยวกับ โหมดสัมผัส คุณสามารถใช้ View.isInTouchMode()
เพื่อกำหนดโหมดอินพุตที่ผู้ใช้ใช้ คุณสามารถใช้ OnTouchModeChangeListener
เพื่อฟังการเปลี่ยนแปลง แม้ว่าสิ่งนี้สามารถใช้ในการปรับแต่งส่วนต่อประสานผู้ใช้ของคุณสำหรับโหมดอินพุตปัจจุบันให้หลีกเลี่ยงการเปลี่ยนแปลงที่สำคัญใด ๆ เนื่องจากสามารถทำให้สับสน
การแก้ไขปัญหา
ในแอพที่ออกแบบมาสำหรับการสัมผัสเป็นเรื่องปกติที่จะมีมุมมองแบบโฟกัสแบบซ้อนกัน ตัวอย่างเช่นอาจมี FrameLayout
รอบ ๆ ImageButton
ซึ่งทั้งคู่สามารถโฟกัสได้ สิ่งนี้ไม่เป็นอันตรายต่อการสัมผัส แต่อาจส่งผลให้ผู้ใช้ไม่ดีสำหรับการหมุนเพราะผู้ใช้จะต้องหมุนคอนโทรลเลอร์สองครั้งเพื่อย้ายไปยังมุมมองแบบโต้ตอบถัดไป สำหรับประสบการณ์การใช้งานที่ดี Google แนะนำให้คุณสร้างมุมมองด้านนอกหรือมุมมองด้านในที่โฟกัสได้ แต่ไม่ใช่ทั้งสองอย่าง
หากปุ่มหรือสวิตช์สูญเสียโฟกัสเมื่อกดผ่านคอนโทรลเลอร์โรตารี่อาจใช้หนึ่งในเงื่อนไขเหล่านี้:
- ปุ่มหรือสวิตช์ถูกปิดใช้งาน (สั้น ๆ หรือไม่มีกำหนด) เนื่องจากปุ่มถูกกด ไม่ว่าในกรณีใดมีสองวิธีในการแก้ไขปัญหานี้:
- ปล่อยให้
android:enabled
เป็นtrue
และใช้สถานะที่กำหนดเองเพื่อเป็นสีเทาออกปุ่มหรือสวิตช์ตามที่อธิบายไว้ใน สถานะที่กำหนดเอง - ใช้คอนเทนเนอร์เพื่อล้อมรอบปุ่มหรือสวิตช์และทำให้คอนเทนเนอร์โฟกัสได้แทนปุ่มหรือสวิตช์ (ผู้ฟังคลิกต้องอยู่ในคอนเทนเนอร์)
- ปล่อยให้
- ปุ่มหรือสวิตช์กำลังถูกแทนที่ ตัวอย่างเช่นการดำเนินการเมื่อกดปุ่มหรือสวิตช์ถูกสลับอาจทำให้เกิดการรีเฟรชของการกระทำที่มีอยู่ซึ่งทำให้ปุ่มใหม่เปลี่ยนปุ่มที่มีอยู่ มีสองวิธีในการแก้ไขปัญหานี้:
- แทนที่จะสร้างปุ่มหรือสวิตช์ใหม่ให้ตั้งค่าไอคอนและ/หรือข้อความของปุ่มหรือสวิตช์ที่มีอยู่
- ดังกล่าวข้างต้นเพิ่มคอนเทนเนอร์ที่โฟกัสได้รอบปุ่มหรือสวิตช์
พื้นแบบโรตารีเพลย์
RotaryPlayground
เป็นแอพอ้างอิงสำหรับโรตารี่ ใช้เพื่อเรียนรู้วิธีการรวมคุณสมบัติโรตารี่เข้ากับแอพของคุณ RotaryPlayground
รวมอยู่ในการสร้าง Emulator และในการสร้างสำหรับอุปกรณ์ที่ใช้ Android Automotive OS (AAOS)
- ที่เก็บ
RotaryPlayground
:packages/apps/Car/tests/RotaryPlayground/
- เวอร์ชัน: Android 11 QPR3, Android 11 Car และ Android 12
แอพ RotaryPlayground
แสดงแท็บต่อไปนี้ทางด้านซ้าย:
- การ์ด ทดสอบการนำทางไปรอบ ๆ พื้นที่โฟกัสข้ามองค์ประกอบที่ไม่สามารถโฟกัสและอินพุตข้อความได้
- การจัดการโดยตรง วิดเจ็ตทดสอบที่รองรับโหมดการจัดการโดยตรงที่เรียบง่ายและขั้นสูง แท็บนี้มีไว้สำหรับการจัดการโดยตรงภายในหน้าต่างแอพ
- การจัดการ SYS UI วิดเจ็ตทดสอบที่รองรับการจัดการโดยตรงในหน้าต่างระบบที่รองรับโหมดการจัดการโดยตรงอย่างง่ายเท่านั้น
- กริด ทดสอบการนำทางโรตารี่ Z-Pattern ด้วยการเลื่อน
- การแจ้งเตือน ทดสอบการผลักดันเข้าและออกจากการแจ้งเตือนแบบหัวขึ้น
- เลื่อน ทดสอบการเลื่อนผ่านการผสมผสานของเนื้อหาที่มุ่งเน้นและไม่สามารถโฟกัสได้
- WebView ทดสอบการนำทางผ่านลิงก์ใน
WebView
-
FocusArea
กำหนดเอง ทดสอบการปรับแต่งFocusArea
:- ห่อรอบ ๆ.
-
android:focusedByDefault
และapp:defaultFocus
. - เป้าหมายเขยิบที่ชัดเจน
- ทางลัดเขยิบ
-
FocusArea
ที่ไม่มีมุมมองที่โฟกัสได้