요소의 기본 포커스 동작을 재정의해야 할 때가 있습니다. 화면에 나타납니다. 예를 들어 컴포저블을 그룹화하거나 컴포저블이 특정 컴포저블에 포커스를 요청하고, 그 중 하나에 명시적으로 포커스 요청 들어가거나 나올 때 포커스를 캡처 또는 해제하거나 포커스를 리디렉션합니다. 이 섹션에서는 기본값이 아닌 경우 포커스 동작을 변경하는 방법을 설명합니다. 알 수 있습니다
포커스 그룹으로 일관된 탐색 기능 제공
Jetpack Compose가 올바른 다음 항목을 즉시 추측하지 못하는 경우도
탭 탐색, 특히 복잡한 상위 Composables
(예: 탭 및
목록이 중요합니다.
포커스 검색은 일반적으로 Composables
의 선언 순서를 따르지만
Composables
중 하나가
완전히 보이지 않는 가로 스크롤이 가능합니다. 이 내용은 다음에서 확인할 수 있습니다.
다음 예시를 참조하세요.
Jetpack Compose는 화면의 경로를 따라 이동하지 말고 단방향 탐색:
![상단 가로 탐색 메뉴와 그 아래에 항목 목록을 표시하는 앱의 애니메이션입니다.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_1.gif?authuser=9&hl=ko)
이 예에서는 개발자가 앱에 초점을 맞출 의도가 없었다는 것을 초콜릿 탭에서 아래의 첫 번째 이미지로 이동한 다음 페이스트리 탭. 그 대신, 팀이 보고 싶어 할 때까지 내부 콘텐츠에 초점을 맞추세요.
![상단 가로 탐색 메뉴와 그 아래에 항목 목록을 표시하는 앱의 애니메이션입니다.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_2.gif?authuser=9&hl=ko)
컴포저블 그룹에 포커스를 두는 것이 중요한 상황
순차적으로, 이전 예의 탭 행과 같이
focusGroup()
수정자가 있는 상위 요소의 Composable
는 다음과 같습니다.
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
양방향 탐색은 주어진
방향 - 다른 그룹의 요소가 완전히 표시되지 않는 요소보다 가까운 경우
항목을 선택하면 탐색에서 가장 가까운 항목을 선택합니다. 이를 방지하려면
focusGroup()
수정자를 적용하면 됩니다.
FocusGroup
는 전체 그룹이 포커스 측면에서 단일 항목처럼 보이게 합니다.
그룹 자체는 포커스를 받지 않으며, 대신 가장 가까운 하위 그룹이
대신 초점을 맞출 수 있습니다. 이렇게 하면 탐색이 완전하지 않은 지도로 이동하는 것을 알 수 있습니다.
항목을 삭제할 수 있습니다.
여기서는 FilterChip
의 세 인스턴스가
SweetsCard
항목(SweetsCards
이 다음 항목에 완전히 표시되는 경우에도 해당)
사용자 및 일부 FilterChip
이(가) 숨겨져 있을 수 있습니다. 이는
focusGroup
수정자는 포커스 관리자에게 항목의 순서를 조정하도록 지시합니다.
탐색이 더 쉽고 일관성이 있도록 UI에 중점을 둡니다.
focusGroup
수정자가 없는 경우 FilterChipC
가 표시되지 않으면 포커스
내비게이션이 마지막에 잡습니다. 그러나 이러한 수정자를 추가하면
검색만 가능하지만 FilterChipB
직후에 포커스를 획득합니다.
사용자가 예상할 수 있는 내용입니다.
컴포저블을 포커스 가능하게 만들기
Button 또는
연결된 clickable
수정자 예를 들어
포커스 가능 동작을 컴포저블에 적용하려면 focusable
수정자를 사용하면 됩니다.
var color by remember { mutableStateOf(Green) } Box( Modifier .background(color) .onFocusChanged { color = if (it.isFocused) Blue else Green } .focusable() ) { Text("Focusable 1") }
컴포저블에 포커스 불가능 만들기
일부 요소가 참여해서는 안 되는 상황이 있을 수 있습니다.
초점을 맞추고 있습니다. 드물지만 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
객체를
컴포저블 함수를 선택합니다. 다음 코드에서
스니펫, FocusRequester
객체는 TextField
Modifier.focusRequester
라는 수정자를 사용합니다.
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
FocusRequester의 requestFocus
메서드를 호출하여 실제 포커스 요청을 보낼 수 있습니다. 이 메서드는 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
가
두 개의 focusProperties
를 선언할 때
다음 항목이 최상위에 포함되어 있기 때문입니다.
개념을 더 명확히 하려면 다음 코드를 참고하세요.
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
이 경우 item2
를 오른쪽 포커스로 나타내는 focusProperties
가
이전 문구에 포함되어 있으므로 사용해서는 안 됩니다. 따라서 item1
가
있습니다.
이 접근 방식을 활용하면 부모는
FocusRequester.Default
사용:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
상위 요소가 동일한 수정자 체인의 일부가 아니어도 됩니다. 부모
하위 컴포저블의 포커스 속성을 덮어쓸 수 있습니다. 예를 들어
버튼에 포커스를 둘 수 없도록 하는 다음 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
는
첫 번째 포커스 가능 하위 요소 사용할 수 있는 항목이 없는 경우 아무것도 가리키지 않습니다.
그러나 focusable()
수정자 덕분에 Box
는 포커스 가능하므로
양방향 탐색을 사용하여 해당 페이지로 이동할 수 있습니다.
또 다른 예로 onFocusChanged()
수정자는
focusable()
또는 focusTarget()
수정자
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
들어가거나 나갈 때 포커스 리디렉션
경우에 따라 다음과 같이 매우 구체적인 종류의 탐색을 제공해야 할 때가 있습니다. 아래 애니메이션에서 볼 수 있습니다.
![나란히 놓인 두 개의 열과 한 열에서 다른 열로 포커스를 애니메이션으로 보여주는 화면의 애니메이션입니다.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_1.gif?authuser=9&hl=ko)
만드는 방법을 알아보기 전에 먼저 Cloud Functions의
포커스 검색의 동작을 보여 줍니다. 수정하지 않고 포커스 검색이
D패드 (또는 동등한 항목)의 DOWN
를 눌러 Clickable 3
항목에 도달합니다.
화살표 키)을 누르면 포커스가 Column
아래에 표시된 항목으로 이동합니다.
그룹에서 나가고 오른쪽 사람은 무시합니다. 값이 없는 경우
포커스 가능 항목을 사용할 수 있는 경우 포커스가 어디로도 이동하지 않고
Clickable 3
이 동작을 변경하고 의도한 탐색을 제공하려면
focusProperties
수정자: 포커스가 있을 때 발생하는 작업을 관리하는 데 도움이 됩니다.
검색에서 Composable
를 입력하거나 종료합니다.
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
특정 Composable
가 들어올 때마다 포커스를 특정 Composable
로 보낼 수 있음
계층 구조의 특정 부분에서 나갑니다(예: UI에 두 개의 요소가
첫 번째 열이 처리될 때마다
포커스는 두 번째로 전환됩니다.
![나란히 놓인 두 개의 열과 한 열에서 다른 열로 포커스를 애니메이션으로 보여주는 화면의 애니메이션입니다.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_2.gif?authuser=9&hl=ko)
이 gif에서 포커스가 Column
1의 Clickable 3 Composable
에 도달하면
포커스가 있는 다음 항목은 다른 Column
의 Clickable 4
입니다. 이 동작
focusDirection
를 enter
및 exit
와 결합하여 얻을 수 있습니다.
값을 focusProperties
수정자 내로 변경할 수 있습니다. 둘 다
를 매개변수로 사용하면 포커스가 맞춰지고
FocusRequester
이 람다는 세 가지 방식으로 작동할 수 있습니다.
FocusRequester.Cancel
는 포커스가 계속되는 것을 중지하며,
FocusRequester.Default
는 동작을 변경하지 않습니다. 대신
다른 Composable
에 연결된 FocusRequester
를 사용하면 해당 항목으로 포커스가 점프합니다.
구체적인 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가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- 집중에 대한 반응
- Compose의 포커스
- 포커스 순회 순서 변경