Wear에서 손목 동작 사용

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

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

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

시계 기기에서 손목 동작을 검토하려면 설정 > 고급 기능 > 동작 > 손목 동작 사용을 선택하여 동작이 켜져 있는지 확인합니다. 그런 다음 튜토리얼 시작을 선택하여 시계에서 동작 튜토리얼을 완료합니다.

참고: 손목 흔들기는 시스템 전체 뒤로 또는 실행취소 동작이므로 앱에서 맞춤설정할 수 없습니다.

손목 동작은 이 가이드에 설명된 대로 다음과 같은 방식으로 사용할 수 있습니다.

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

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

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

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

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

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

키 이벤트 직접 사용

WearableRecyclerView 이외의 키 이벤트를 사용하여 동작 이벤트에 대한 응답으로 새 작업을 트리거할 수 있습니다. 무엇보다도 이러한 동작 이벤트는 기기가 활성 모드일 때 인식되며 모든 키 이벤트와 동일한 방식으로 전달됩니다.

View 또는 Activity와 같은 사용자 상호작용과 관련되고 KeyEvent.Callback을 구현하는 클래스는 다른 키 이벤트에 나열할 수 있는 것처럼 손목 동작과 관련된 키 이벤트를 수신 대기할 수 있습니다. Android 프레임워크는 키 이벤트에 포커스가 있는 View 또는 Activity를 호출합니다. 동작의 경우 동작이 발생할 때 onKeyDown() 메서드 콜백이 호출됩니다.

예를 들어 앱은 다음과 같이 KeyEvent.Callback을 구현하는 View 또는 Activity에서 사전 정의된 작업을 재정의할 수 있습니다.

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 determined
                // 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
    }
}

Java

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 determined 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;
 }
}

권장사항

  • 키 이벤트를 ViewActivity에 전달하는 방법에 관해서는 KeyEvent KeyEvent.Callback 페이지를 검토합니다.
  • 일관된 방향 어포던스를 유지합니다. 다음 항목으로 이동하려면 '손목을 바깥쪽으로 휙 돌리기', 이전 항목으로 이동하려면 '손목을 안쪽으로 휙 돌리기'를 사용합니다.
  • 동작과 일관적인 터치를 사용합니다.
  • 시각적 피드백을 제공합니다.
  • 시스템의 나머지에 직관적이지 않을 수 있는 기능을 구현하기 위해 키 코드를 사용하지 않습니다. 예를 들어 작업을 취소하거나 획 돌리기로 좌우 축을 이동하는 데 KEYCODE_NAVIGATE_NEXT를 사용하지 마세요.
  • 사용자 인터페이스의 일부가 아닌 요소(예: 화면을 벗어나거나 부분적으로 덮인 뷰)에서 키 이벤트를 가로채지 않습니다. 이는 모든 키 이벤트에 동일합니다.
  • 반복되는 획 돌리기 동작을 자체적인 새로운 동작으로 재해석하지 않습니다. 이는 시스템의 '손목 흔들기' 동작과 충돌할 수도 있습니다.
  • 동작 키 이벤트를 수신할 뷰에는 포커스가 있어야 합니다. View.setFocusable()을 참고하세요.

    동작은 키 이벤트로 처리되기 때문에 '터치 모드'에서 전환을 트리거하여 예기치 않은 작업으로 이어질 수 있습니다. 사용자는 터치와 동작을 번갈아 사용할 수 있으므로 View::setFocusableInTouchmode() 메서드가 필요할 수 있습니다. 경우에 따라, '터치 모드'를 오갈 때 포커스가 변경될 경우 의도한 뷰에 포커스가 오도록 하려면 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS)를 사용해야 할 수 있습니다.

  • 신중하게 requestFocus()clearFocus()를 사용합니다.
    • requestFocus()를 호출할 때 뷰에 포커스가 있는 것이 적절한지 확인합니다. 뷰가 화면을 벗어나거나 다른 뷰로 덮여 있는 경우 동작이 콜백을 트리거하면 사용자가 놀랄 수 있습니다.
    • clearFocus() 메서드는 다른 적절한 뷰를 찾기 위해 포커스 검색을 시작합니다. 뷰 계층 구조에 따라, 검색에 상당한 계산이 필요할 수 있습니다. 또한 포커스를 받을 것으로 예상하지 않은 뷰에 포커스가 할당될 수도 있습니다.
  • 키 이벤트는 뷰 계층 구조에서 포커스가 있는 뷰로 먼저 전달됩니다. 포커스가 놓인 뷰가 이벤트를 처리하지 않으면(즉, false를 반환함) 포커스를 받을 수 있고 KeyListener를 보유하고 있더라도 이벤트가 상위 뷰에 전달되지 않습니다. 오히려 포커스가 있는 뷰 계층 구조를 보유한 현재 활동으로 이벤트가 전달됩니다.

    따라서 상위 수준의 모든 이벤트를 포착한 다음 관련 코드를 전달해야 할 수 있습니다. 또는 활동을 서브클래스로 분류하고 dispatchKeyEvent(KeyEvent event) 메서드를 재정의하여 필요한 경우 키를 가로채거나 하위 레이어에서 처리되지 않는 키를 처리할 수도 있습니다.