บางครั้งอาจจำเป็นต้องลบล้างลักษณะการทำงานของการโฟกัสเริ่มต้นขององค์ประกอบ บนหน้าจอ ตัวอย่างเช่น คุณอาจต้องการจัดกลุ่ม Composable ป้องกัน โฟกัสไปที่คำสั่ง Composable ขอโฟกัสที่ชัดเจนกับ 1 จับภาพหรือปล่อยโฟกัส หรือเปลี่ยนเส้นทางโฟกัสเมื่อเข้าหรือออก ช่วงเวลานี้ อธิบายวิธีเปลี่ยนลักษณะการโฟกัสเมื่อค่าเริ่มต้นไม่ใช่สิ่งที่คุณต้องการ ความต้องการ
ให้การนำทางที่สอดคล้องกับการสนทนากลุ่ม
บางครั้ง Jetpack Compose ไม่ได้คาดเดารายการถัดไปที่ถูกต้องโดยทันที
การนำทางแบบแท็บ โดยเฉพาะเมื่อ Composables
ระดับบนสุดที่ซับซ้อน เช่น แท็บและ
รายการที่เข้าร่วม
แม้ว่าการค้นหาโฟกัสมักจะเป็นไปตามลำดับการประกาศของComposables
ซึ่งเป็นไปไม่ได้ในบางกรณี เช่นเมื่อ Composables
ใน
ลำดับชั้นเป็นการเลื่อนในแนวนอนได้และมองไม่เห็นบางส่วน ซึ่งจะแสดงใน
ตัวอย่างด้านล่าง
Jetpack Compose อาจตัดสินใจโฟกัสรายการถัดไปที่ใกล้กับจุดเริ่มต้นของ ดังที่แสดงด้านล่าง แทนการดำเนินการต่อตามเส้นทางที่คุณคาดหวังไว้ การนำทางแบบทิศทางเดียว:
![ภาพเคลื่อนไหวของแอปแสดงการนำทางแนวนอนด้านบนและรายการสินค้าด้านล่าง](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_1.gif?authuser=5&hl=th)
ในตัวอย่างนี้แสดงให้เห็นอย่างชัดเจนว่านักพัฒนาแอปไม่ได้มีเจตนาที่จะมุ่งเน้นที่ ข้ามจากแท็บช็อกโกแลตไปยังรูปภาพแรกด้านล่าง แล้วกลับไปที่ แท็บขนมอบ แต่ต้องการให้โฟกัสที่แท็บนั้นต่อไปจนกว่า แท็บสุดท้าย แล้วเน้นไปที่เนื้อหาด้านใน
![ภาพเคลื่อนไหวของแอปแสดงการนำทางแนวนอนด้านบนและรายการสินค้าด้านล่าง](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_2.gif?authuser=5&hl=th)
ในสถานการณ์ที่กลุ่มของ Composable มีความสำคัญ
ตามลำดับ เช่น ในแถวแท็บจากตัวอย่างก่อนหน้านี้ คุณจะต้องตัดข้อความ
Composable
ในกลุ่มหลักที่มีตัวปรับแต่ง focusGroup()
:
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
การนำทางแบบ 2 ทิศทางจะค้นหา Composable ที่ใกล้เคียงที่สุดสำหรับ
ทิศทาง—หากองค์ประกอบจากกลุ่มอื่นอยู่ใกล้กว่าที่มองไม่เห็นทั้งหมด
รายการในกลุ่มปัจจุบัน การนำทางจะเลือกรายการที่ใกล้เคียงที่สุด เพื่อหลีกเลี่ยงปัญหานี้
คุณสามารถใช้แป้นกดร่วม focusGroup()
FocusGroup
ทำให้ทั้งกลุ่มดูเหมือนบุคคลเดียวในแง่ของการมุ่งเน้น
แต่กลุ่มเองก็จะไม่เป็นจุดสนใจ แต่เด็กที่สนิทที่สุดจะ
เพื่อให้ได้โฟกัสแทน วิธีนี้ทำให้การนำทางรู้ว่าต้องไปยัง URL ที่มองไม่เห็นทั้งหมด
ก่อนออกจากกลุ่ม
ในกรณีนี้ FilterChip
ทั้ง 3 อินสแตนซ์จะโฟกัสก่อน
SweetsCard
รายการ แม้ว่า SweetsCards
จะมองเห็นได้ทั้งหมดกับ
ผู้ใช้และ FilterChip
บางส่วนอาจซ่อนอยู่ ซึ่งเกิดขึ้นเนื่องจาก
ตัวแก้ไข focusGroup
จะบอกตัวจัดการโฟกัสให้ปรับลำดับของรายการ
เพื่อให้ไปยังส่วนต่างๆ ได้ง่ายและสอดคล้องกับ UI มากขึ้น
เมื่อไม่มีตัวปรับ focusGroup
หาก FilterChipC
ไม่ปรากฏ ให้โฟกัส
จะมาเป็นคนเลือกเป็นลำดับสุดท้าย อย่างไรก็ตาม การเพิ่มคีย์ตัวปรับแต่งดังกล่าวจะทำให้
ค้นพบได้เท่านั้น แต่จะได้รับโฟกัสทันทีหลัง FilterChipB
ด้วย
ที่ผู้ใช้คาดหวัง
การทำให้โฟกัสที่ประกอบกันได้
Composable บางประเภทสามารถโฟกัสได้ด้วยการออกแบบ เช่น Button หรือ Composable ที่มี
แป้นกดร่วม clickable
ที่แนบมาด้วย ถ้าคุณต้องการเพิ่ม
ลักษณะการทำงานที่โฟกัสได้ไปยัง Composable คุณจะใช้ตัวแก้ไข focusable
var color by remember { mutableStateOf(Green) } Box( Modifier .background(color) .onFocusChanged { color = if (it.isFocused) Blue else Green } .focusable() ) { Text("Focusable 1") }
การทำให้ Compos ไม่สามารถโฟกัสได้
อาจมีบางสถานการณ์ที่องค์ประกอบบางอย่างของคุณไม่ควรมีส่วนร่วม
เราโฟกัสในเรื่องนี้ ในกรณีที่ไม่ค่อยเกิดขึ้นเหล่านี้ คุณสามารถใช้ canFocus property
เพื่อยกเว้น Composable
ไม่ให้สามารถโฟกัสได้
var checked by remember { mutableStateOf(false) } Switch( checked = checked, onCheckedChange = { checked = it }, // Prevent component from being focused modifier = Modifier .focusProperties { canFocus = false } )
ขอโฟกัสแป้นพิมพ์ด้วย FocusRequester
ในบางกรณี คุณอาจต้องส่งคำขอการมุ่งเน้นอย่างชัดเจนเพื่อตอบกลับ การโต้ตอบของผู้ใช้ เช่น คุณอาจถามผู้ใช้ว่าต้องการรีสตาร์ทไหม กรอกแบบฟอร์ม แล้วกด "ใช่" คุณต้องการโฟกัสฟิลด์แรกใหม่ ของแบบฟอร์มนั้นๆ
สิ่งแรกที่ต้องทำคือเชื่อมโยงออบเจ็กต์ FocusRequester
เข้ากับ
Composable ที่คุณต้องการย้ายโฟกัสของแป้นพิมพ์ไป ในโค้ดต่อไปนี้
ข้อมูลโค้ด ออบเจ็กต์ FocusRequester
จะเชื่อมโยงกับ TextField
โดยการตั้งค่า
แป้นกดร่วมที่ชื่อ Modifier.focusRequester
:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
คุณสามารถเรียกใช้เมธอด requestFocus
ของ FocusRequester เพื่อส่งคำขอโฟกัสจริงได้ คุณควรเรียกใช้เมธอดนี้นอกบริบท Composable
(มิฉะนั้น โค้ดจะถูกเรียกใช้ใหม่ในการจัดองค์ประกอบใหม่ทุกครั้ง) ข้อมูลโค้ดต่อไปนี้
แสดงวิธีขอให้ระบบย้ายโฟกัสของแป้นพิมพ์เมื่อปุ่ม
คลิก:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) ) Button(onClick = { focusRequester.requestFocus() }) { Text("Request focus on TextField") }
จับภาพและปล่อยโฟกัส
คุณสามารถใช้ประโยชน์จากจุดมุ่งเน้นในการนำทางผู้ใช้เพื่อให้ข้อมูลที่เหมาะสมแก่แอปของคุณ ต้องดำเนินงาน เช่น รับอีเมลหรือหมายเลขโทรศัพท์ที่ถูกต้อง หมายเลข แม้ว่าสถานะข้อผิดพลาดจะแจ้งให้ผู้ใช้ทราบเกี่ยวกับสิ่งที่เกิดขึ้น แต่คุณ อาจต้องให้ช่องที่มีข้อมูลที่ผิดพลาดเพื่อให้จดจ่อจนกว่าจะเข้าใจ คงที่
หากต้องการจับโฟกัส ให้เรียกใช้เมธอด captureFocus()
และ
เผยแพร่ในภายหลังโดยใช้เมธอด freeFocus()
แทน ดังตัวอย่างต่อไปนี้
ตัวอย่าง:
val textField = FocusRequester() TextField( value = text, onValueChange = { text = it if (it.length > 3) { textField.captureFocus() } else { textField.freeFocus() } }, modifier = Modifier.focusRequester(textField) )
ลำดับความสำคัญของตัวปรับแต่งโฟกัส
Modifiers
อาจมองว่าเป็นองค์ประกอบที่มีเพียงลูกเดียว ดังนั้นเมื่อคุณอยู่ในคิว
Modifier
แต่ละรายการที่อยู่ทางซ้าย (หรือด้านบน) จะตัด Modifier
ที่ตามหลัง
ด้านขวา (หรือต่ำกว่า) ซึ่งหมายความว่า Modifier
เครื่องที่ 2 อยู่ใน
รายการแรก ดังนั้นเมื่อประกาศ focusProperties
สองรายการ ให้แสดงเฉพาะระดับบนสุด
รายการแรกใช้งานได้ โดยมีชิ้นงานต่อไปนี้อยู่บนสุด
หากต้องการอธิบายแนวคิดให้ชัดเจนมากขึ้น โปรดดูโค้ดต่อไปนี้
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
ในกรณีนี้ focusProperties
ที่ระบุว่า item2
เป็นโฟกัสที่ถูกต้องจะ
ไม่มีการใช้ เนื่องจากมีอยู่ในข้อความก่อนหน้านี้ ดังนั้น item1
จะเป็นค่า
ใช้ไป 1 รูป
หากใช้วิธีนี้ ผู้ปกครองสามารถรีเซ็ตลักษณะการทำงานให้เป็นค่าเริ่มต้นได้
โดยใช้ FocusRequester.Default
:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
ระดับบนสุดไม่จำเป็นต้องเป็นส่วนหนึ่งของกลุ่มตัวปรับแต่งเดียวกัน ผู้ปกครอง
Composable สามารถเขียนทับคุณสมบัติโฟกัสของ Composable ย่อยได้ ตัวอย่างเช่น
ให้ลองพิจารณา FancyButton
นี้ที่ทำให้โฟกัสปุ่มไม่ได้:
@Composable fun FancyButton(modifier: Modifier = Modifier) { Row(modifier.focusProperties { canFocus = false }) { Text("Click me") Button(onClick = { }) { Text("OK") } } }
ผู้ใช้สามารถทำให้ปุ่มนี้โฟกัสได้อีกครั้งโดยการตั้งค่า canFocus
เป็น true
ดังนี้
FancyButton(Modifier.focusProperties { canFocus = true })
แอปที่เกี่ยวข้องกับการโฟกัสจะทำงานแตกต่างกันไปตามลำดับ เช่นเดียวกับ Modifier
ทั้งหมด
ที่คุณประกาศ ตัวอย่างเช่น โค้ดต่อไปนี้ทำให้ Box
โฟกัสได้ แต่ FocusRequester
ไม่เชื่อมโยงกับการโฟกัสนี้ เนื่องจาก
จะต้องประกาศหลังจากโฟกัสได้
Box( Modifier .focusable() .focusRequester(Default) .onFocusChanged {} )
โปรดอย่าลืมว่า focusRequester
มีการเชื่อมโยงกับ
โฟกัสได้ที่ด้านล่างในลําดับชั้น ดังนั้น focusRequester
นี้จึงชี้ไปที่
เด็กที่โฟกัสได้คนแรก ในกรณีที่ไม่มีรายการที่พร้อมใช้งาน ก็จะไม่ชี้ไปที่สิ่งใด
อย่างไรก็ตาม เนื่องจาก Box
สามารถโฟกัสได้ (เพราะตัวปรับแต่ง focusable()
)
คุณจะไปยังส่วนต่างๆ ได้โดยใช้การนำทางแบบ 2 ทิศทาง
อีกตัวอย่างหนึ่งคือตัวอย่างต่อไปนี้ onFocusChanged()
ตัวปรับหมายถึงองค์ประกอบแรกที่โฟกัสได้ที่ปรากฏหลังจาก
แป้นกดร่วม focusable()
หรือ focusTarget()
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
เปลี่ยนเส้นทางโฟกัสเมื่อเข้าหรือออก
ในบางครั้ง คุณอาจต้องระบุการนำทางที่เฉพาะเจาะจงมาก ที่แสดงในภาพเคลื่อนไหวด้านล่าง
![ภาพเคลื่อนไหวของหน้าจอแสดงปุ่ม 2 คอลัมน์ที่วางอยู่ข้างกันและย้ายโฟกัสจากคอลัมน์หนึ่งไปยังอีกคอลัมน์หนึ่ง](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_1.gif?authuser=5&hl=th)
ก่อนที่เราจะเจาะลึกถึงวิธีสร้างโฆษณานี้ คุณควรเข้าใจค่าเริ่มต้น
พฤติกรรมของการค้นหาโฟกัส โดยไม่ต้องแก้ไขใดๆ เมื่อการค้นหาโฟกัส
ไปถึงรายการ Clickable 3
การกด DOWN
บน D-pad (หรือจำนวนที่เทียบเท่า
แป้นลูกศร) จะเลื่อนโฟกัสไปยังสิ่งที่แสดงใต้ Column
ออกจากกลุ่มและไม่สนใจกลุ่มทางขวา หากไม่มี
มีรายการที่สามารถโฟกัสได้ โฟกัสจะไม่ย้ายไปที่อื่น แต่ยังคงอยู่
Clickable 3
หากต้องการเปลี่ยนลักษณะการทำงานนี้และให้การนำทางตามที่ต้องการ คุณสามารถใช้ประโยชน์จาก
แป้นกดร่วม focusProperties
ซึ่งจะช่วยให้คุณจัดการสิ่งที่จะเกิดขึ้นเมื่อโฟกัส
การค้นหาจะเข้าหรือออกจาก Composable
:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
คุณสามารถส่งโฟกัสไปที่ Composable
ที่เฉพาะเจาะจงเมื่อใดก็ตามที่
หรือออกจากบางส่วนของลำดับชั้น เช่น เมื่อ UI มี
และคุณต้องการแน่ใจว่าเมื่อใดก็ตามที่คอลัมน์แรกได้รับการประมวลผล
โฟกัสจะสลับไปที่ตัวที่สอง:
![ภาพเคลื่อนไหวของหน้าจอแสดงปุ่ม 2 คอลัมน์ที่วางอยู่ข้างกันและย้ายโฟกัสจากคอลัมน์หนึ่งไปยังอีกคอลัมน์หนึ่ง](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_2.gif?authuser=5&hl=th)
ใน GIF นี้ เมื่อโฟกัสอยู่ที่ Clickable 3 Composable
ใน Column
1
รายการถัดไปที่โฟกัสอยู่คือ Clickable 4
ใน Column
อีกรายการ ลักษณะการทำงานนี้
ด้วยการรวม focusDirection
เข้ากับ enter
และ exit
ภายในตัวปรับแต่ง focusProperties
ทั้งคู่ต้องการแลมบ์ดาที่ใช้เวลา
เป็นพารามิเตอร์ว่าจุดโฟกัสมาจากไหนและส่งคืน
FocusRequester
แลมบ์ดานี้มีลักษณะ 3 ประการ ได้แก่ การย้อนกลับ
FocusRequester.Cancel
จะหยุดโฟกัสไม่ให้ใช้งานต่อ
FocusRequester.Default
จะไม่เปลี่ยนลักษณะการทำงาน การป้อนพารามิเตอร์
FocusRequester
ที่แนบมากับ Composable
อีกรายการทำให้โฟกัสข้ามไปที่นั่น
Composable
ที่เฉพาะเจาะจง
เปลี่ยนทิศทางการโฟกัสที่ไปข้างหน้า
หากต้องการเลื่อนโฟกัสไปยังรายการถัดไปหรือไปยังทิศทางที่แน่นอน คุณสามารถ
ใช้ประโยชน์จากตัวแก้ไข onPreviewKey
และบอกเป็นนัยว่า LocalFocusManager
เป็น
โฟกัสด้วยส่วนขยาย moveFocus
ตัวอย่างต่อไปนี้แสดงลักษณะการทำงานเริ่มต้นของกลไกการโฟกัสเมื่อ
ตรวจพบการกดปุ่ม tab
โฟกัสจะเลื่อนไปยังองค์ประกอบถัดไปในโฟกัส
รายการ แม้ว่านี่จะไม่ใช่สิ่งที่คุณจะต้องกำหนดค่าตามปกติ แต่ก็เป็นเรื่องสำคัญ
เพื่อให้ทราบการทำงานภายในของระบบ เพื่อที่จะสามารถเปลี่ยนค่าเริ่มต้น
พฤติกรรมของคุณ
val focusManager = LocalFocusManager.current var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.onPreviewKeyEvent { when { KeyEventType.KeyUp == it.type && Key.Tab == it.key -> { focusManager.moveFocus(FocusDirection.Next) true } else -> false } } )
ในตัวอย่างนี้ ฟังก์ชัน focusManager.moveFocus()
จะย้ายโฟกัสไปที่
รายการที่ระบุ หรือทิศทางโดยนัยในพารามิเตอร์ฟังก์ชัน
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- แสดงความรู้สึกเพื่อให้โฟกัส
- โฟกัสในการเขียน
- เปลี่ยนลำดับการส่งผ่านโฟกัส