Wear에서 손목 동작 사용

터치스크린이 불편할 때 손목 동작을 사용하면 한 손으로 빠르게 앱과 상호작용할 수 있습니다.

예를 들어 사용자가 한 손에 물컵을 들고 다른 한 손으로 알림을 스크롤할 수 있습니다. 손목 동작의 사용 예는 다음과 같습니다.

  • 조깅 앱에서 걸음 수, 경과 시간, 현재 속도를 보여주는 세로 화면 탐색
  • 수하물이 있는 공항에서 비행기 및 게이트 정보 스크롤
  • 뉴스 기사 스크롤

시계의 손목 동작을 검토하려면 설정 > 동작 > 손목 동작 사용을 선택하여 동작이 켜지는지 확인합니다. 그런 다음 시계에서 동작 가이드(설정 > 동작 > 가이드 시작하기)를 완료합니다.

Wear OS 도움말의 다음 동작은 앱에 사용할 수 없습니다.

  • 손목 흔들기

손목 동작은 다음과 같은 방법으로 사용할 수 있습니다.

각 손목 동작은 다음 표에 나와 있는 것처럼 KeyEvent 클래스의 int 상수로 매핑됩니다.

동작 키 이벤트 설명
손목을 바깥쪽으로 휙 돌리기 KEYCODE_NAVIGATE_NEXT 이 키 코드를 실행하면 다음 항목으로 이동합니다.
손목을 안쪽으로 휙 돌리기 KEYCODE_NAVIGATE_PREVIOUS 이 키 코드를 실행하면 이전 항목으로 이동합니다.

손목 동작을 지원하는 곡선형 레이아웃 사용

WearableRecyclerView 클래스는 목록에 곡선형 레이아웃을 제공하고 손목 동작을 자동으로 지원합니다. 이 클래스에는 뷰에 포커스가 있을 때 손목 동작이 발생하는 경우를 위한 미리 정의된 동작이 있습니다. WearableRecyclerView 클래스 사용에 관한 자세한 내용은 목록 만들기를 참조하세요. 권장사항도 참조하세요.

참고: WearableRecyclerView 클래스는 웨어러블 지원 라이브러리에서 지원 중단된 유사 클래스를 대체합니다.

WearableRecyclerView를 사용하더라도 KeyEvent 클래스의 상수를 사용할 수 있습니다. 미리 정의된 작업은 WearableRecyclerView를 서브클래스로 분류하고 onKeyDown() 콜백을 다시 구현하여 재정의할 수 있습니다. 동작은 setEnableGestureNavigation(false)를 사용하여 완전히 중지할 수 있습니다. 키보드 작업 처리도 참조하세요.

키 이벤트 직접 사용

WearableRecyclerView 이외의 키 이벤트를 사용하여 동작 이벤트에 관한 응답으로 새 작업을 트리거할 수 있습니다. 이러한 동작 이벤트의 중요 사항은 다음과 같습니다.

  • 기기가 활성 모드일 때 인식됩니다.
  • 모든 키 이벤트와 동일한 방식으로 전달됩니다.

특히 이러한 이벤트는 상위 활동, 키보드 포커스가 있는 뷰로 전달됩니다. 다른 키 이벤트와 마찬가지로, KeyEvent.Callback을 구현하는 사용자 상호작용(예: 뷰 또는 활동)과 관련된 클래스는 손목 동작과 관련된 키 이벤트를 수신할 수 있습니다. Android 프레임워크는 키 이벤트에 포커스가 있는 뷰 또는 활동을 호출합니다. 동작의 경우 동작 발생 시 onKeyDown() 메서드 콜백이 호출됩니다.

예를 들어, 앱은 다음과 같이 뷰 또는 활동의 미리 정의된 작업을 재정의할 수 있습니다(둘 다 KeyEvent.Callback 구현).

Kotlin

    class GesturesActivity : Activity() {

        /* KeyEvent.Callback */
        override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
            return when (keyCode) {
                KeyEvent.KEYCODE_NAVIGATE_NEXT ->
                    // Do something that advances a user View to the next item in an ordered list.
                    moveToNextItem()
                KeyEvent.KEYCODE_NAVIGATE_PREVIOUS ->
                    // Do something that advances a user View to the previous item in an ordered list.
                    moveToPreviousItem()
                else -> {
                    // If you did not handle it, let it be handled by the next possible element as deemed
                    // by the Activity.
                    super.onKeyDown(keyCode, event)
                }
            }
        }

        /** Shows the next item in the custom list.  */
        private fun moveToNextItem(): Boolean {
            …
            // Return true if handled successfully, otherwise return false.
            return false
        }

        /** Shows the previous item in the custom list.  */
        private fun moveToPreviousItem(): Boolean {
            …
            // Return true if handled successfully, otherwise return false.
            return false
        }
    }
    

자바

    public final class GesturesActivity extends Activity {

     @Override /* KeyEvent.Callback */
     public boolean onKeyDown(int keyCode, KeyEvent event) {
      switch (keyCode) {
       case KeyEvent.KEYCODE_NAVIGATE_NEXT:
        // Do something that advances a user View to the next item in an ordered list.
        return moveToNextItem();
       case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS:
        // Do something that advances a user View to the previous item in an ordered list.
        return moveToPreviousItem();
      }
      // If you did not handle it, let it be handled by the next possible element as deemed by the Activity.
      return super.onKeyDown(keyCode, event);
     }

     /** Shows the next item in the custom list. */
     private boolean moveToNextItem() {
      boolean handled = false;
      …
      // Return true if handled successfully, otherwise return false.
      return handled;
     }

     /** Shows the previous item in the custom list. */
     private boolean moveToPreviousItem() {
      boolean handled = false;
      …
      // Return true if handled successfully, otherwise return false.
      return handled;
     }
    }
    

권장사항

  • 키 이벤트를 뷰와 활동에 전달하는 방법에 관해 KeyEvent KeyEvent.Callback 페이지를 검토합니다.
  • 일관된 방향을 유지합니다.
    • 다음 항목에는 '손목을 바깥쪽으로 휙 돌리기', 이전 항목에는 '손목을 안쪽으로 휙 돌리기'를 사용합니다.
  • 동작과 일관적인 터치를 사용합니다.
  • 시각적 피드백을 제공합니다.
  • 시스템의 나머지에 직관적이지 않을 수 있는 기능을 구현하기 위해 키 코드를 사용하지 않습니다. 예를 들어, 작업을 취소하거나 획 돌리기로 좌우 축을 이동하는 데 KEYCODE_NAVIGATE_NEXT를 사용하지 않습니다.
  • 예를 들어 화면을 벗어나거나 부분적으로 덮인 뷰와 같이 사용자 인터페이스의 일부가 아닌 요소에서 키 이벤트를 가로채지 않습니다. 이는 다른 키 이벤트와 동일합니다.
  • 반복되는 획 돌리기 동작을 자체적인 새로운 동작으로 재해석하지 않습니다. 그런 경우 시스템의 '손목 흔들기' 동작과 충돌할 수 있습니다.
  • 동작 키 이벤트를 수신할 뷰에는 포커스가 있어야 합니다. View::setFocusable()을 참조하세요. 동작은 키 이벤트로 처리되기 때문에 '터치 모드'에서 전환을 트리거합니다. 이는 예기치 않은 동작으로 이어질 수 있습니다. 따라서 사용자는 터치와 동작을 번갈아 사용할 수 있으므로 View::setFocusableInTouchmode() 메서드가 필요할 수 있습니다. 경우에 따라, '터치 모드'를 오갈 때 포커스가 변경될 경우 의도한 뷰에 포커스가 오도록 하려면 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS)를 사용해야 할 수 있습니다.
  • 신중하게 requestFocus()clearFocus()를 사용합니다.
    • requestFocus()를 호출할 때는 뷰에 실제로 포커스가 있는지 확인합니다. 뷰가 화면을 벗어나거나 다른 뷰로 덮여 있는 경우 동작이 콜백을 트리거하면 사용자가 놀랄 수 있습니다.
    • clearFocus()는 다른 적절한 뷰를 찾기 위해 포커스 검색을 시작합니다. 뷰 계층 구조에 따라, 검색에 상당한 계산이 필요할 수 있습니다. 또한 포커스를 받을 것으로 예상하지 않은 뷰에 포커스가 할당될 수도 있습니다.
  • 키 이벤트는 뷰 계층 구조에서 포커스가 있는 뷰로 먼저 전달됩니다. 포커스가 놓인 뷰가 이벤트를 처리하지 않으면(즉, false를 반환), 포커스를 받을 수 있고 KeyListener를 가지고 있더라도 이벤트는 상위 뷰로 전달되지 않습니다. 오히려 포커스가 있는 뷰 계층 구조를 보유한 현재 활동으로 이벤트가 전달됩니다. 따라서 상위 레벨에 있는 모든 이벤트를 가져와 관련 코드를 전달해야 할 수 있습니다. 또는 필요 시 키를 가로채거나 하위 레이어에서 처리되지 않는 키를 처리하기 위해 활동을 서브클래스로 분류하고 dispatchKeyEvent(KeyEvent event) 메서드를 재정의할 수 있습니다.