Jetpack Compose로 키보드, 마우스, 트랙패드, 스타일러스 지원 추가

1. 소개

표준 휴대전화에서 앱을 사용할 수 있다면 태블릿, 폴더블, ChromeOS 기기와 같은 대형 화면 기기에서도 앱을 사용할 수 있습니다.

사용자는 대형 화면에서 앱이 작은 화면에서의 UX와 같거나 그보다 나은 사용자 환경을 제공할 것으로 기대합니다.

또한 대형 화면 기기에서는 사용자가 앱을 사용할 때 실제 키보드와 포인팅 기기(예: 마우스, 트랙패드)를 함께 사용할 가능성이 더 높습니다. Chromebook과 같은 일부 대형 화면 기기에는 실제 키보드와 포인팅 기기가 포함됩니다. USB 또는 블루투스로 키보드와 포인팅 기기를 연결하는 기기도 있습니다. 사용자는 실제 키보드와 포인팅 기기로 앱을 사용할 때 터치스크린으로 하는 것과 동일한 작업을 실행할 수 있기를 기대합니다.

기본 요건

  • Compose를 사용하여 앱을 빌드한 경험
  • 람다 및 코루틴을 비롯한 Kotlin 관련 기본 지식

빌드할 항목

Jetpack Compose 기반 앱에 실제 키보드 및 마우스 지원을 추가합니다. 이 단계는 다음과 같이 구성됩니다.

  1. 대형 화면 앱 품질 가이드라인에 정의된 기준에 따라 앱을 확인합니다.
  2. 감사 결과를 검토하고 실제 키보드 및 마우스 지원과 관련된 문제를 파악합니다.
  3. 문제를 해결합니다.

더 구체적으로는 다음과 같은 내용으로 샘플 앱을 업데이트합니다.

  • 키보드 탐색
  • 위아래로 스크롤하는 단축키
  • 단축키 도우미

학습할 내용

  • 가상 기기 지원을 위해 앱을 감사하는 방법
  • Compose를 사용하여 키보드 탐색을 관리하는 방법
  • Compose를 사용하여 단축키를 추가하는 방법

필요한 항목

  • Android 스튜디오 Hedgehog 이상
  • 샘플 앱을 실행할 수 있는 기기:
  • 실제 키보드와 마우스가 있는 대형 화면 기기
  • 데스크톱 기기 정의 카테고리에 포함된 프로필이 있는 Android Virtual Device

2. 설정

  1. large-screen-codelabs GitHub 저장소를 클론합니다.
git clone https://github.com/android/large-screen-codelabs

또는 large-screen-codelabs ZIP 파일을 다운로드하고 보관 취소해도 됩니다.

  1. add-keyboard-and-mouse-support-with-compose 폴더로 이동합니다.
  2. Android 스튜디오에서 프로젝트를 엽니다. add-keyboard-and-cursor-support-with-compose 폴더에는 하나의 프로젝트가 포함됩니다.
  3. Android 태블릿 또는 폴더블 기기가 없거나 실제 키보드와 마우스가 있는 ChromeOS 기기가 없는 경우 Android 스튜디오에서 Device Manager를 연 다음 Desktop 카테고리에 가상 기기를 만듭니다.

데스크톱 카테고리의 가상 기기

3. 앱 둘러보기

샘플 앱은 기사 목록을 표시합니다. 사용자는 목록에서 선택한 기사를 읽을 수 있습니다.

앱은 앱의 창 너비에 따라 유연하게 레이아웃을 업데이트합니다. 앱의 창 너비를 분류하는 데는 세 가지 창 클래스(소형, 중형, 확장)가 있습니다.

창 너비를 나타내는 창 크기 클래스: 소형, 중형, 확장 애플리케이션 창 너비가 600dp보다 작으면 창 너비를 소형으로 분류합니다. 창 너비가 640dp보다 크거나 같으면 창 너비를 확장으로 분류합니다. 창이 소형이나 확장에 속하지 않으면 창 크기 클래스는 중형입니다.

소형 및 중형 창 크기 클래스의 레이아웃

앱은 단일 창 레이아웃을 사용합니다. 앱은 홈 화면에 기사 목록을 표시합니다. 사용자가 목록에서 기사를 선택하면 화면이 전환되고 기사가 표시됩니다.

전역 탐색은 탐색 창으로 구현됩니다.

앱이 데스크톱 에뮬레이터의 소형 창에서 실행되고 있습니다. 기사 목록이 표시됩니다.

확장 창 크기 클래스의 레이아웃

앱에서 목록-세부정보 레이아웃을 사용합니다. 목록 창에 기사 목록이 표시됩니다. 세부정보 창에 선택한 기사가 표시됩니다.

전역 탐색은 탐색 레일을 사용하여 구현됩니다.

앱이 데스크톱 에뮬레이터의 확장된 창 크기 클래스에서 실행되고 있습니다.

4. 배경

Compose는 앱이 실제 키보드와 마우스의 이벤트를 처리할 수 있도록 다양한 API를 제공합니다. 일부 API는 터치 이벤트 처리와 비슷하게 키보드 및 마우스 이벤트를 처리할 수 있습니다. 따라서 많은 사용 사례에서 개발자 측의 개발 작업 없이 앱이 실제 키보드와 마우스를 지원합니다.

일반적인 예로는 클릭을 감지할 수 있는 clickable 수정자가 있습니다. 손가락 탭이 클릭으로 감지됩니다. 마우스를 클릭하거나 Enter 키를 누르는 것도 클릭으로 감지됩니다. 앱에서 clickable 수정자로 클릭을 감지하면 사용자가 입력 장치에 관계없이 구성요소와 상호작용할 수 있습니다.

그러나 이러한 높은 수준의 API 지원에도 불구하고 실제 키보드와 마우스를 지원하기 위해서는 여전히 어느 정도의 개발 노력이 필요합니다. 한 가지 이유는 앱을 테스트하여 특수한 사례를 찾아야 하기 때문입니다. 또한 다음과 같은 기기 특성으로 인한 사용자의 불편을 줄이기 위한 노력도 필요합니다.

  • 사용자가 클릭할 수 있는 구성요소를 알지 못함
  • 사용자가 원하는 대로 키보드 포커스를 이동할 수 없음
  • 사용자가 실제 키보드를 사용할 때 위 또는 아래로 스크롤할 수 없음

키보드 포커스

키보드 포커스는 실제 키보드와 화면 터치 간의 주요 차이점입니다. 사용자는 이전에 터치한 구성요소의 위치와 관계없이 화면의 구성요소를 탭할 수 있습니다. 반대로 키보드는 사용자가 실제 상호작용이 시작되기 전에 상호작용할 구성요소를 선택해야 합니다. 이 선택 항목을 키보드 포커스라고 합니다.

사용자는 Tab 키와 방향(또는 화살표) 키를 사용하여 키보드 포커스를 이동할 수 있습니다. 키보드 포커스는 기본적으로 인접 구성요소로만 이동합니다.

실제 키보드 사용의 불편한 점은 대부분 키보드 포커스와 관련이 있습니다. 다음 목록은 일반적인 문제를 보여줍니다.

  • 사용자가 상호작용하려는 구성요소로 키보드 포커스를 이동할 수 없음
  • 사용자가 Enter 키를 누를 때 구성요소가 클릭을 감지하지 않음
  • 키보드 포커스가 사용자의 예상과 다르게 움직임
  • 사용자가 화면 전환 후 상호작용하려는 구성요소로 키보드 포커스를 이동하려면 여러 키를 눌러야 함
  • 시각적 신호가 키보드 포커스를 나타내지 않으면 사용자는 어떤 구성요소에 키보드 포커스가 있는지 확인할 수 없음
  • 사용자가 새 화면으로 이동할 때 포커스가 있는 기본 구성요소를 확인할 수 없음

키보드 포커스의 시각적 표시가 중요합니다. 키보드 포커스가 표시되지 않으면 사용자가 앱에서 길을 잃을 수 있고 Enter 키를 누를 때 어떤 상황이 발생할지 알지 못합니다. 강조표시는 키보드 포커스를 나타내는 일반적인 시각적 신호입니다. 오른쪽 카드의 버튼이 강조 표시되어 버튼에 키보드 포커스가 있음을 사용자가 알 수 있습니다.

53ee7662b764f2dd.png

단축키

사용자는 실제 키보드로 앱을 사용할 때 일반적인 단축키를 사용할 수 있을 것으로 기대합니다. 일부 구성요소는 기본적으로 표준 단축키를 사용 설정합니다. 일반적인 예로 BasicTextField를 들 수 있습니다. 이를 통해 사용자는 다음과 같은 표준 텍스트 편집 단축키를 사용할 수 있습니다.

단축키

기능

Ctrl+C

복사

Ctrl+X

잘라내기

Ctrl+V

붙여넣기

Ctrl+Z

실행취소

Ctrl+Y

다시 실행

앱에서 키 이벤트를 처리하여 단축키를 추가할 수 있습니다. onKeyEvent 수정자와 onPreviewKeyEvent 수정자를 사용하면 키 이벤트를 모니터링할 수 있습니다.

포인팅 기기: 마우스, 트랙패드, 스타일러스

앱은 마우스, 트랙패드, 스타일러스를 동일한 방식으로 처리할 수 있습니다. 트랙패드에서 탭하면 clickable 수정자를 사용한 클릭으로 감지됩니다. 스타일러스를 사용한 탭도 클릭으로 감지됩니다.

사용자가 구성요소를 클릭할 수 있는지 여부를 시각적으로 알 수 있어야 합니다. 이런 이유로 대형 화면 앱 품질 가이드라인에 마우스 오버 상태가 언급됩니다.

Material 3 구성요소는 기본적으로 마우스 오버 상태를 지원합니다. Material 3은 마우스 오버 상태에 맞는 시각적 효과를 제공합니다. indication 수정자를 사용하여 이 효과를 대화형 구성요소에 적용할 수 있습니다.

스크롤

스크롤 가능한 컨테이너는 기본적으로 마우스 휠 스크롤, 트랙패드의 스크롤 동작, Page upPage down 키를 사용한 스크롤을 지원합니다.

가로 스크롤의 경우 사용자가 버튼을 클릭하여 콘텐츠를 스크롤할 수 있도록 마우스 오버 상태일 때 왼쪽 및 오른쪽 화살표 버튼을 표시하면 앱이 매우 사용자 친화적일 것입니다.

17feb4d3bf08831e.png

기기 연결 및 분리로 인한 구성 변경

사용자는 앱이 실행되는 동안 키보드와 마우스를 연결하거나 분리할 수 있습니다. 많은 양의 텍스트를 입력하는 텍스트 필드가 표시되면 사용자가 실제 키보드를 연결할 수도 있습니다. 블루투스로 연결된 마우스가 절전 모드로 전환되면 마우스 연결이 해제됩니다. USB로 연결된 키보드가 실수로 분리되었을 수도 있습니다.

주변기기 하드웨어가 연결 또는 분리되면 구성 변경이 트리거됩니다. 앱은 구성이 변경되는 동안 상태를 유지해야 합니다. 자세한 내용은 UI 상태 저장을 참고하세요.

5. 키보드 및 마우스로 샘플 앱 확인

실제 키보드 및 마우스 지원을 위한 개발을 시작하려면 샘플 앱을 시작하고 다음을 확인하세요.

  • 사용자가 키보드 포커스를 모든 대화형 구성요소로 이동할 수 있어야 함
  • 사용자가 Enter 키로 포커스가 있는 구성요소를 '클릭'할 수 있어야 함
  • 대화형 구성요소가 키보드 포커스를 받을 때 이를 표시해야 함
  • 키보드 포커스는 사용자가 예상하는 대로(즉, 정해진 규칙에 따라) Tab 키, Shift+Tab 키, 방향(화살표) 키를 사용하여 이동함
  • 대화형 구성요소에 마우스 오버 상태가 있어야 함
  • 사용자가 대화형 구성요소를 클릭할 수 있어야 함
  • 컨텍스트 메뉴를 표시하려면 적절한 구성요소를 마우스 오른쪽 버튼으로 클릭(보조 컨트롤 클릭)합니다. 예를 들어 길게 탭하거나 텍스트를 선택하면 컨텍스트 메뉴가 표시됩니다.

이 Codelab에서는 모든 항목을 두 번 진행해야 합니다. 한 번은 단일 창 레이아웃이고 한 번은 목록-세부정보 레이아웃입니다.

이 Codelab에서 해결할 문제

문제를 발견해야 합니다. 이 Codelab에서는 다음 문제를 수정합니다.

  • 사용자가 기사를 아래로 스크롤할 수 없기 때문에 실제 키보드만으로는 전체 기사를 읽을 수 없음
  • 사용자가 세부정보 창에 키보드 포커스가 있는지 확인할 수 없음

6. 사용자가 세부정보 창에서 전체 기사를 읽을 수 있도록 사용 설정

세부정보 창에는 선택한 기사가 표시됩니다. 일부 기사는 너무 길어서 스크롤하지 않고는 전체 기사를 읽을 수 없습니다. 하지만 실제 키보드만으로는 사용자가 기사를 위아래로 스크롤할 수 없습니다.

4627289223e5cfbc.gif

스크롤 가능한 컨테이너(예: LazyColumn)를 사용하면 사용자가 Page down 키로 아래로 스크롤할 수 있습니다. 이 문제의 근본 원인은 사용자가 키보드 포커스를 세부정보 창으로 이동할 수 없는 것입니다.

구성요소는 키보드 이벤트를 수신하기 위해 키보드 포커스를 가져올 수 있어야 합니다. focusable 수정자를 사용하면 이 수정자를 사용한 구성요소로 키보드 포커스를 가져올 수 있습니다.

이 문제를 해결하려면 다음 단계를 따르세요.

  1. ui/article/PostContent.kt 파일에서 PostContent 컴포저블 함수에 액세스합니다.
  2. focusable 수정자로 LazyColumn 컴포저블 함수를 수정합니다.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .focusable(),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

기사에 키보드 포커스가 있음을 표시

이제 사용자는 Page down 키로 기사를 아래로 스크롤하여 전체 기사를 읽을 수 있습니다. 그러나 PostContent 구성요소에 키보드 포커스가 있는지는 시각적 효과가 이를 나타내지 않으므로 사용자가 알기 어렵습니다.

Indication을 구성요소와 연결하여 앱에서 키보드 포커스를 시각적으로 표시할 수 있습니다. Indication은 상호작용에 따라 시각적 효과를 렌더링하는 객체를 만듭니다. 예를 들어 Material 3의 기본 표시는 키보드 포커스가 있는 구성요소를 강조표시합니다.

샘플 앱에는 BorderIndication이라는 Indication이 있습니다. 이 Indication은 키보드 포커스가 있는 구성요소 옆에 선으로 표시됩니다(다음 스크린샷 참고). 코드는 ui/components/BorderIndication.kt 파일에 저장되어 있습니다.

키보드 포커스가 기사에 있으면 기사 옆에 밝은 회색 선이 표시됩니다.

PostConent 컴포저블에 키보드 포커스가 있을 때 BorderIndication을 표시하도록 하려면 다음 단계를 따르세요.

  1. ui/article/PostContent.kt 파일에서 PostContent 컴포저블 함수에 액세스합니다.
  2. remember() 함수의 반환 값과 연결된 interactionSource 값을 선언합니다.
  3. 생성된 MutableInteractionSource 객체가 interactionSource 값과 연결되도록 remember() 함수에서 MutableInteractionSource() 함수를 호출합니다.
  4. interactionSource 매개변수를 사용하여 interactionSource 값을 focusable 수정자에 전달합니다.
  5. indication 수정자를 호출한 후 focusable 수정자를 호출하도록 PostContent 컴포저블의 수정자를 변경합니다.
  6. interactionSource 값과 BorderIndication 함수의 반환 값을 indication 수정자로 전달합니다.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    val interactionSource = remember { MutableInteractionSource() }

    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .indication(interactionSource, BorderIndication())
            .focusable(interactionSource = interactionSource),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

위아래로 스크롤하는 단축키 추가

사용자가 Spacebar를 사용하여 위아래로 스크롤할 수 있도록 하는 일반적인 기능입니다. 앱은 다음 표와 같이 추가하여 단축키를 이 기능을 구현할 수 있습니다.

단축키

기능

Spacebar

기사를 아래로 스크롤

Shift + Spacebar

기사를 위로 스크롤

onKeyEvent 수정자를 사용하면 앱이 수정자를 사용한 구성요소에서 발생하는 키 이벤트를 처리할 수 있습니다. 이 수정자는 키 이벤트를 설명하는 KeyEvent 객체를 사용하여 호출되는 람다를 사용합니다. 람다는 키 이벤트를 소비했는지 나타내는 Boolean 값을 반환해야 합니다.

LazyColumnLazyRow의 스크롤 위치는 LazyListState 객체에 캡처됩니다. 앱은 LazyListState 객체에 animateScrollBy() 정지 메서드를 호출하여 스크롤을 트리거할 수 있습니다. 이 메서드는 지정된 픽셀 수만큼 LazyColumn을 아래로 스크롤합니다. 정지 함수가 음의 부동 소수점 값으로 호출되면 함수는 LazyColumn을 위로 스크롤합니다.

이러한 단축키를 구현하려면 다음 단계를 따르세요.

  1. ui/article/PostContent.kt 파일에서 PostContent 컴포저블 함수에 액세스합니다.
  2. onKeyEvent 수정자로 LazyColumn 컴포저블 함수를 수정합니다.
  3. 다음과 같이 onKeyEvent 수정자에 전달된 람다에 if 표현식을 추가합니다.
  • 다음 조건이 충족되면 true를 반환합니다.
  • Spacebar가 눌렸습니다. type 속성이 KeyType.KeyDown이고 key 속성이 Key.Spacebar인지 테스트하여 이를 감지할 수 있습니다.
  • isCtrlPressed 속성이 false이면 Ctrl 키가 눌리지 않은 것입니다.
  • isAltPressed 속성이 false이면 Alt 키가 눌리지 않은 것입니다.
  • isMetaPressed 속성이 false이면 Meta 키(참고 확인)가 눌리지 않은 것입니다.
  • 그 밖의 경우 false가 반환됩니다.
  1. 다음과 같이 Spacebar의 양으로 스크롤을 결정합니다.
  • Shift 키를 눌렀을 때 -0.4f(지정된 KeyEvent 객체의 isShiftPressed 속성으로 설명됨)
  • 그 밖의 경우 0.4f
  1. 컴포저블 함수 PostContent의 매개변수인 coroutineScope에 대해 launch() 메서드를 호출합니다.
  2. 이전 단계에서 계산된 상대적인 스크롤 양과 launch 메서드의 람다 매개변수에 있는 state.layoutInfo.viewportSize.height 속성을 곱하여 실제 스크롤 양을 계산합니다. 이 속성은 컴포저블 함수 PostContent에서 호출되는 LazyColumn의 높이를 나타냅니다.
  3. launch() 메서드의 람다에서 state.animateScrollBy() 메서드를 호출하여 세로 스크롤을 트리거합니다.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    val interactionSource = remember { MutableInteractionSource() }

    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .onKeyEvent {
                if (
                    it.type == KeyEventType.KeyDown &&
                    it.key == Key.Spacebar &&
                    !it.isCtrlPressed &&
                    !it.isAltPressed &&
                    !it.isMetaPressed
                ) {

                    val relativeAmount = if (it.isShiftPressed) {
                        -0.4f
                    } else {
                        0.4f
                    }
                    coroutineScope.launch {
                        state.animateScrollBy(relativeAmount * state.layoutInfo.viewportSize.height)
                    }
                    true
                } else {
                    false
                }
            }
            .indication(interactionSource, BorderIndication())
            .focusable(interactionSource = interactionSource),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

사용자에게 단축키 알려주기

사용자가 단축키를 숙지하지 않으면 추가된 키보드를 최대한 활용할 수 없습니다. 앱은 Android 시스템 UI의 일부인 단축키 도우미를 통해 사용자에게 사용 가능한 단축키를 알려줄 수 있습니다. 사용자는 Meta+/를 사용하여 단축키 도우미를 열 수 있습니다.

단축키 도우미에는 이전 섹션에서 추가된 단축키가 표시됩니다.

앱은 앱 기본 활동의 onProvideKeyboardShortcuts() 메서드를 재정의하여 단축키 도우미에 단축키 목록을 제공합니다.

더 구체적으로는 앱이 onProvideKeyboardShortcuts()에 전달된 변경 가능한 목록에 단축키를 추가하여 여러 KeyboardShortcutGroup 객체를 제공합니다. 각 KeyboardShortcutGroup은 이름이 지정된 단축키 카테고리를 나타냅니다. 이를 통해 앱은 사용 가능한 단축키를 용도나 컨텍스트에 따라 그룹화할 수 있습니다.

샘플 앱에는 SpacebarShift+Spacebar, 두 가지 단축키가 있습니다.

단축키 도우미에서 이 두 가지 단축키를 사용할 수 있도록 하려면 다음 단계를 따르세요.

  1. MainActivity.kt 파일을 엽니다.
  2. MainActivity에서 onProvideKeyboardShortcuts() 메서드를 재정의합니다.
  3. 단축키 도우미를 사용할 수 있도록 Android SDK 버전이 Android 7.0(API 수준 24) 이상인지 확인합니다.
  4. 메서드의 첫 번째 매개변수가 null아닌지 확인합니다.
  5. 다음 매개변수를 사용하여 Spacebar 키를 위한 KeyboardShortcutInfo 객체를 만듭니다.
  • 단축키 설명 텍스트
  • android.view.KeyEvent.KEYCODE_SPACE
  • 0(수정자가 없음을 나타냄)
  1. 다음 매개변수를 사용하여 Shift+Spacebar를 위한 또 다른 KeyboardShortcutInfo를 만듭니다.
  • 단축키 설명 텍스트
  • android.view.KeyEvent.KEYCODE_SPACE
  • android.view.KeyEvent.META_SHIFT_ON
  1. KeyboardShortcutInfo 객체가 포함된 변경 불가능한 목록을 만듭니다.
  2. 다음 매개변수를 사용하여 KeyboardShortcutGroup 객체를 만듭니다.
  • 텍스트로 된 그룹 이름
  • 이전 단계에서 만든 변경 불가능한 목록
  1. onProvideKeyboardShortcuts() 메서드의 첫 번째 매개변수로 전달된 변경 가능한 목록에 KeyboardShortcutGroup 객체를 추가합니다.

재정의된 메서드는 다음과 같습니다.

   override fun onProvideKeyboardShortcuts(
        data: MutableList<KeyboardShortcutGroup>?,
        menu: Menu?,
        deviceId: Int
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && data != null) {
            val shortcutGroup = KeyboardShortcutGroup(
                "To read articles",
                listOf(
                    KeyboardShortcutInfo("Scroll down", KeyEvent.KEYCODE_SPACE, 0), // 0 means no modifier key is pressed
                    KeyboardShortcutInfo("Scroll up", KeyEvent.KEYCODE_SPACE, KeyEvent.META_SHIFT_ON),
                )
            )
            data.add(shortcutGroup)
        }
    }

실행하기

이제 사용자는 Spacebar로 기사를 스크롤하여 기사 전체를 읽을 수 있습니다. Tab 키 또는 방향 키를 사용하여 키보드 포커스를 기사로 이동해 보세요. Spacebar를 누르도록 알려주는 메시지가 표시됩니다.

단축키 도우미에 추가한 두 개의 단축키가 표시됩니다(Meta+/ 누르기). 추가된 단축키는 Current app 탭에 표시됩니다.

7. 세부정보 창에서 키보드 탐색 신속히 처리

앱이 확장된 창-크기 클래스에서 실행 중일 때 세부정보 창으로 키보드 포커스를 이동하려면 사용자는 Tab 키를 여러 번 눌러야 합니다. 사용자가 오른쪽 방향 키를 사용하면 한 번의 작업으로 기사 목록에서 기사로 키보드 포커스를 이동할 수 있지만 여전히 키보드 포커스를 이동해야 합니다. 초기 포커스가 기사 읽기라는 사용자의 기본 목표를 지원하지 않습니다.

앱에서 FocusRequester 객체를 사용하여 키보드 포커스를 특정 구성요소로 이동하도록 요청할 수 있습니다. focusRequester 수정자는 FocusRequester 객체를 수정자를 사용하는 구성요소와 연결합니다. 앱은 FocusRequester 객체의 requestFocus() 메서드를 호출하여 포커스 이동에 관한 실제 요청을 보낼 수 있습니다.

키보드 포커스를 이동하도록 요청을 보내는 것은 구성요소의 부작용입니다. 앱은 LaunchedEffect 함수를 사용하여 적절한 방식으로 메서드를 호출해야 합니다.

사용자가 기사 목록에서 기사를 선택할 때 키보드 포커스를 가져오도록 PostContent 컴포저블을 설정하려면 다음 단계를 따르세요.

  1. ui/article/ PostContent.kt 파일에서 PostContent 컴포저블 함수에 액세스합니다.
  2. focusRequester 값을 focusRequester 수정자를 사용하는 컴포저블 함수 LazyColumn과 연결합니다. focusRequester 값은 PostContent 컴포저블 함수의 선택적 매개변수로 지정됩니다.
  3. PostContent 컴포저블 함수의 첫 번째 매개변수인 postLaunchedEffect를 호출합니다. 그러면 사용자가 기사를 선택할 때 전달된 람다가 호출됩니다.
  4. LaunchedEffect 함수에 전달된 람다에서 focusRequester.requestFocus() 메서드를 호출합니다.

업데이트된 PostContent 컴포저블은 다음과 같습니다.

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostContent(
    post: Post,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    state: LazyListState = rememberLazyListState(),
    coroutineScope: CoroutineScope = rememberCoroutineScope(),
    focusRequester: FocusRequester = remember { FocusRequester() },
    header: (@Composable () -> Unit)? = null
) {
    val interactionSource = remember { MutableInteractionSource() }

    LaunchedEffect(post) {
        focusRequester.requestFocus()
    }

    LazyColumn(
        contentPadding = contentPadding,
        modifier = modifier
            .padding(horizontal = defaultSpacerSize)
            .onKeyEvent {
                if (it.type == KeyEventType.KeyDown && it.key == Key.Spacebar) {
                    val relativeAmount = if (it.isShiftPressed) {
                        -0.4f
                    } else {
                        0.4f
                    }
                    coroutineScope.launch {
                        state.animateScrollBy(relativeAmount * state.layoutInfo.viewportSize.height)
                    }
                    true
                } else {
                    false
                }
            }
            .focusRequester(focusRequester),
            .indication(interactionSource, BorderIndication())
            .focusable(interactionSource = interactionSource),
        state = state,
    ) {
      // Code to layout the selected article.
    }
}

실행하기

이제 사용자가 기사 목록에서 기사를 선택하면 키보드 포커스가 기사로 이동합니다. 기사를 선택하면 Spacebar를 사용하여 기사를 아래로 스크롤하라는 메시지가 표시되는 것을 볼 수 있습니다.

8. 마무리

수고하셨습니다. 샘플 앱에 실제 키보드 및 마우스 지원을 추가했습니다. 이제 사용자는 실제 키보드나 마우스만 사용하여 기사 목록에서 기사를 선택하고 선택한 기사를 읽을 수 있습니다.

실제 키보드와 마우스 지원을 추가하는 데 필요한 다음 사항을 알아보았습니다.

  • 앱이 에뮬레이터를 포함하여 실제 키보드와 마우스를 지원하는지 확인하는 방법
  • Compose를 사용하여 키보드 탐색을 관리하는 방법
  • Compose를 사용하여 단축키를 추가하는 방법

또한 소량의 코드 수정으로 실제 키보드와 마우스 지원을 추가했습니다.

Compose를 사용하여 프로덕션 앱에 실제 키보드 및 마우스 지원을 추가할 준비가 되었습니다.

조금만 더 학습하면 다음 기능을 위한 단축키를 추가할 수 있습니다.

  • 선택한 기사에 '좋아요'를 표시합니다.
  • 선택한 기사를 북마크에 추가합니다.
  • 선택한 기사를 다른 앱과 공유합니다.

자세히 알아보기