Đôi khi, cần ghi đè hành vi lấy tiêu điểm mặc định của các phần tử trên màn hình của bạn. Ví dụ: bạn nên nhóm các thành phần kết hợp lại ngăn chặn tập trung vào một thành phần kết hợp nhất định, yêu cầu trọng tâm rõ ràng vào một thành phần kết hợp, lấy hoặc thả tiêu điểm hoặc tiêu điểm chuyển hướng khi vào hoặc thoát. Chiến dịch này mô tả cách thay đổi hành vi của tiêu điểm khi không phải là chế độ mặc định của bạn.
Điều hướng nhất quán qua các nhóm trọng điểm
Đôi khi, Jetpack Compose không dự đoán ngay mục tiếp theo chính xác cho
điều hướng bằng thẻ, đặc biệt là khi Composables
mẹ phức tạp như thẻ và
vào danh sách.
Mặc dù tính năng tìm kiếm tiêu điểm thường tuân theo thứ tự khai báo của Composables
,
điều này là không thể trong một số trường hợp, chẳng hạn như khi một trong các Composables
trong
hệ phân cấp là một trình đơn có thể cuộn theo chiều ngang không hiển thị đầy đủ. Nội dung này được thể hiện trong
ví dụ bên dưới.
Jetpack Compose có thể quyết định tập trung vào mục tiếp theo gần nhất với điểm bắt đầu của như được hiển thị bên dưới, thay vì tiếp tục trên đường dẫn mà bạn mong muốn điều hướng một chiều:
Trong ví dụ này, rõ ràng là các nhà phát triển không có ý định tập trung vào chuyển từ thẻ Sô cô la sang hình ảnh đầu tiên bên dưới rồi quay lại Thẻ Bánh ngọt. Thay vào đó, họ muốn tập trung tiếp tục vào các thẻ cho đến khi thẻ cuối cùng rồi tập trung vào nội dung bên trong:
Trong trường hợp cần phải có một nhóm thành phần kết hợp cần lấy trọng tâm
tuần tự, như trong hàng Thẻ ở ví dụ trước, bạn cần gói
Composable
trong phần tử mẹ có đối tượng sửa đổi focusGroup()
:
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
Điều hướng hai chiều sẽ tìm thành phần kết hợp gần nhất cho
hướng – nếu một phần tử từ một nhóm khác ở gần hơn so với một phần tử không hiển thị hoàn toàn
mục trong nhóm hiện tại, điều hướng sẽ chọn mục gần nhất. Để tránh tình trạng này
hành vi, bạn có thể áp dụng đối tượng sửa đổi focusGroup()
.
FocusGroup
làm cho toàn bộ nhóm trông giống như một thực thể duy nhất về tiêu điểm,
nhưng nhóm đó sẽ không nhận được tiêu điểm — thay vào đó, trẻ gần nhất sẽ
lấy tiêu điểm. Bằng cách này, điều hướng biết chuyển đến vùng hiển thị không hiển thị đầy đủ
trước khi rời khỏi nhóm.
Trong trường hợp này, 3 thực thể của FilterChip
sẽ được lấy làm tâm điểm trước
SweetsCard
mục, ngay cả khi SweetsCards
hoàn toàn hiển thị với
người dùng và một số FilterChip
có thể bị ẩn. Điều này xảy ra vì
Đối tượng sửa đổi focusGroup
yêu cầu trình quản lý tiêu điểm điều chỉnh thứ tự các mục
đều được lấy làm tâm điểm để điều hướng dễ dàng và nhất quán hơn với giao diện người dùng.
Nếu không có đối tượng sửa đổi focusGroup
, nếu FilterChipC
không xuất hiện, hãy lấy tiêu điểm
hoạt động điều hướng sẽ hiển thị quảng cáo đó cuối cùng. Tuy nhiên, việc thêm một đối tượng sửa đổi như vậy khiến nó không được
chỉ có thể phát hiện, nhưng cũng sẽ có được tiêu điểm ngay sau FilterChipB
, vì
mà người dùng mong đợi.
Đặt một thành phần kết hợp có thể làm tâm điểm
Một số thành phần kết hợp có thể làm tâm điểm theo thiết kế, chẳng hạn như Nút hoặc thành phần kết hợp có
đối tượng sửa đổi clickable
đi kèm. Nếu bạn muốn thêm cụ thể
hành vi có thể làm tâm điểm cho một thành phần kết hợp, hãy sử dụng đối tượng sửa đổi focusable
:
var color by remember { mutableStateOf(Green) } Box( Modifier .background(color) .onFocusChanged { color = if (it.isFocused) Blue else Green } .focusable() ) { Text("Focusable 1") }
Đặt một thành phần kết hợp không thể làm tâm điểm
Có thể có những tình huống trong đó một số thành phần của bạn không nên tham gia
tiêu điểm. Trong những trường hợp hiếm hoi này, bạn có thể tận dụng canFocus property
để loại trừ Composable
khỏi phạm vi làm tâm điểm.
var checked by remember { mutableStateOf(false) } Switch( checked = checked, onCheckedChange = { checked = it }, // Prevent component from being focused modifier = Modifier .focusProperties { canFocus = false } )
Yêu cầu tiêu điểm bàn phím với FocusRequester
Trong một số trường hợp, bạn nên yêu cầu rõ ràng tiêu điểm dưới dạng phản hồi cho một tương tác của người dùng. Ví dụ: bạn có thể hỏi người dùng xem họ có muốn khởi động lại hay không điền vào biểu mẫu và nếu họ nhấn vào "có" bạn muốn lấy nét lại trường đầu tiên của biểu mẫu đó.
Việc đầu tiên cần làm là liên kết đối tượng FocusRequester
với
thành phần kết hợp mà bạn muốn di chuyển tiêu điểm bàn phím đến. Trong mã sau
thì đối tượng FocusRequester
được liên kết với TextField
bằng cách đặt một giá trị
đối tượng sửa đổi có tên là Modifier.focusRequester
:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
Bạn có thể gọi phương thức requestFocus
của FocusRequester để gửi yêu cầu lấy tiêu điểm thực tế. Bạn nên gọi phương thức này bên ngoài ngữ cảnh Composable
(nếu không, hệ thống sẽ thực thi lại ở mọi quá trình kết hợp lại). Đoạn mã sau
cho biết cách yêu cầu hệ thống di chuyển tiêu điểm bàn phím khi nút này được
đã nhấp vào:
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") }
Chụp và thả tiêu điểm
Bạn có thể tận dụng tiêu điểm để hướng dẫn người dùng cung cấp đúng dữ liệu cho ứng dụng của bạn cần thực hiện tác vụ của mình— ví dụ: nhận được địa chỉ email hoặc số điện thoại hợp lệ số. Mặc dù trạng thái lỗi thông báo cho người dùng về những gì đang xảy ra, bạn có thể cần trường có thông tin không chính xác để luôn lấy tiêu điểm cho đến khi trường được đã được khắc phục.
Để lấy nét, bạn có thể gọi phương thức captureFocus()
và
hãy giải phóng phương thức đó sau bằng phương thức freeFocus()
, như trong ví dụ sau
ví dụ:
val textField = FocusRequester() TextField( value = text, onValueChange = { text = it if (it.length > 3) { textField.captureFocus() } else { textField.freeFocus() } }, modifier = Modifier.focusRequester(textField) )
Quyền ưu tiên của đối tượng sửa đổi tiêu điểm
Modifiers
có thể được xem là các phần tử chỉ có một phần tử con, do đó, khi bạn hàng đợi
chúng, mỗi Modifier
ở bên trái (hoặc trên cùng) sẽ gói Modifier
theo sau
bên phải (hoặc bên dưới). Điều này có nghĩa là Modifier
thứ hai nằm bên trong
phần tử đầu tiên, để khi khai báo hai focusProperties
, chỉ phần tử trên cùng
một hoạt động, vì các phần sau nằm trong lớp trên cùng.
Để làm rõ khái niệm này, hãy xem mã sau:
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
Trong trường hợp này, focusProperties
cho biết item2
là tiêu điểm phù hợp sẽ
không được sử dụng, vì thuộc tính này có trong phần trước; do đó, item1
sẽ là
một mật khẩu đã sử dụng.
Khi tận dụng phương pháp này, cha mẹ cũng có thể đặt lại hành vi về trạng thái mặc định theo
sử dụng FocusRequester.Default
:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
Thành phần mẹ không nhất thiết phải nằm trong cùng một chuỗi đối tượng sửa đổi. Phụ huynh
thành phần kết hợp có thể ghi đè thuộc tính tâm điểm của thành phần kết hợp con. Ví dụ:
hãy xem xét FancyButton
sau đây khiến nút không thể làm tâm điểm:
@Composable fun FancyButton(modifier: Modifier = Modifier) { Row(modifier.focusProperties { canFocus = false }) { Text("Click me") Button(onClick = { }) { Text("OK") } } }
Người dùng có thể đặt lại nút này làm tâm điểm bằng cách đặt canFocus
thành true
:
FancyButton(Modifier.focusProperties { canFocus = true })
Giống như mọi Modifier
, các lệnh liên quan đến tiêu điểm sẽ hoạt động khác nhau tuỳ theo thứ tự
bạn khai báo chúng. Ví dụ: mã như sau sẽ làm cho Box
có thể làm tâm điểm, nhưng FocusRequester
không liên kết với thành phần có thể làm tâm điểm này vì nó
được khai báo sau có thể làm tâm điểm.
Box( Modifier .focusable() .focusRequester(Default) .onFocusChanged {} )
Bạn cần nhớ rằng focusRequester
được liên kết với
có thể lấy tiêu điểm bên dưới nó trong hệ phân cấp, vì vậy focusRequester
này trỏ đến
phần tử con đầu tiên có thể làm tâm điểm. Trong trường hợp không có biểu tượng nào, cảnh báo sẽ không trỏ đến bất kỳ vị trí nào.
Tuy nhiên, vì Box
có thể làm tâm điểm (nhờ đối tượng sửa đổi focusable()
),
bạn có thể điều hướng đến vị trí đó bằng tính năng điều hướng hai chiều.
Một ví dụ khác là một trong hai cách sau đây sẽ hoạt động, dưới dạng onFocusChanged()
đối tượng sửa đổi là phần tử có thể làm tâm điểm đầu tiên xuất hiện sau
Đối tượng sửa đổi focusable()
hoặc focusTarget()
.
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
Chuyển hướng tiêu điểm khi truy cập hoặc thoát
Đôi khi, bạn cần cung cấp một loại điều hướng rất cụ thể, chẳng hạn như như trong ảnh động dưới đây:
Trước khi chúng ta tìm hiểu sâu về cách tạo đối tượng này, điều quan trọng là phải hiểu chế độ cài đặt mặc định
của tính năng tìm kiếm tiêu điểm. Sau khi tìm kiếm tiêu điểm, bạn không cần sửa đổi gì cả
chuyển đến mục Clickable 3
, nhấn DOWN
trên D-Pad (hoặc mục tương đương
phím mũi tên) sẽ di chuyển tiêu điểm đến bất kỳ mục nào hiển thị bên dưới Column
,
đang rời khỏi nhóm và bỏ qua nút ở bên phải. Nếu không có
Các mục có thể làm tâm điểm có sẵn, tiêu điểm không di chuyển đi đâu nhưng vẫn ở trên
Clickable 3
.
Để thay đổi hành vi này và cung cấp khả năng điều hướng như mong muốn, bạn có thể tận dụng
Đối tượng sửa đổi focusProperties
, giúp bạn quản lý những gì sẽ xảy ra khi tiêu điểm
công cụ tìm kiếm sẽ nhập hoặc thoát khỏi Composable
:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
Bạn có thể chuyển tiêu điểm đến một Composable
cụ thể bất cứ khi nào tiêu điểm đó nhập
hoặc thoát khỏi một phần nhất định của hệ phân cấp, ví dụ: khi giao diện người dùng của bạn có hai
và bạn muốn đảm bảo rằng bất cứ khi nào cột đầu tiên được xử lý,
tiêu điểm sẽ chuyển sang vị trí thứ hai:
Trong ảnh GIF này, sau khi tiêu điểm đạt đến Clickable 3 Composable
trong Column
1,
mục tiếp theo được lấy tiêu điểm là Clickable 4
trong Column
khác. Hành vi này
có thể đạt được bằng cách kết hợp focusDirection
với enter
và exit
các giá trị bên trong đối tượng sửa đổi focusProperties
. Cả hai đều cần một lambda nhận
dưới dạng tham số, hướng của tiêu điểm và trả về
FocusRequester
. Hàm lambda này có thể hoạt động theo ba cách khác nhau: trả về
FocusRequester.Cancel
ngăn tiêu điểm tiếp tục, trong khi
FocusRequester.Default
không thay đổi hành vi. Cung cấp thay vì
FocusRequester
được đính kèm vào một Composable
khác làm cho tiêu điểm chuyển đến vị trí đó
Composable
cụ thể.
Thay đổi hướng tiến của trọng tâm
Để chuyển tiêu điểm đến mục tiếp theo hoặc về một hướng chính xác, bạn có thể
tận dụng đối tượng sửa đổi onPreviewKey
và ngụ ý LocalFocusManager
để
chuyển tâm điểm bằng Đối tượng sửa đổi moveFocus
.
Ví dụ sau đây cho thấy hành vi mặc định của cơ chế lấy nét: khi một
Đã phát hiện thấy thao tác nhấn phím tab
, tiêu điểm sẽ chuyển sang phần tử tiếp theo trong tiêu điểm
danh sách. Mặc dù đây không phải là điều bạn thường cần định cấu hình, nhưng điều quan trọng là
biết được hoạt động bên trong của hệ thống để có thể thay đổi chế độ mặc định
hành vi.
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 } } )
Trong mẫu này, hàm focusManager.moveFocus()
chuyển tâm điểm lên
mục được chỉ định hoặc theo hướng ngụ ý trong tham số hàm.
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Thể hiện cảm xúc để tập trung
- Tiêu điểm trong Compose
- Thay đổi thứ tự truyền tải tiêu điểm