맞춤 텍스트 편집기

맞춤 텍스트 편집기는 EditText 구성요소나 WebView 텍스트 위젯은 아니지만 뷰에 포커스가 있고 뷰에서 뷰의 InputConnection을 요청할 때 호출되는 onCreateInputConnection() 콜백을 구현하여 텍스트 입력을 지원하지 않는 뷰입니다.

맞춤 텍스트 편집기에서 onCheckIsTextEditor()를 호출하면 true가 반환되어야 합니다.

맞춤 텍스트 편집기에서 스타일러스 필기 입력 지원

Android 14 (API 수준 34) 이상에서는 기본적으로 표준 Android 텍스트 입력 구성요소의 스타일러스 입력을 지원합니다 (텍스트 필드의 스타일러스 입력 참고). 그러나 맞춤 텍스트 입력란 (또는 편집기)에는 추가 개발이 필요합니다.

맞춤 텍스트 편집기를 만들려면 다음 단계를 따르세요.

  1. 필기 입력 시작 사용 설정
  2. 필기 입력 지원 선언
  3. 필기 입력 동작 지원 (선택, 삭제, 삽입 등)
  4. IME에 커서 위치 및 기타 위치 데이터 제공
  5. 스타일러스 필기 입력 마우스 오버 아이콘 표시

필기 입력 시작 사용 설정

뷰가 단일 텍스트 편집기로만 구성된 경우 뷰 시스템이 뷰의 스타일러스 필기 입력을 자동으로 시작할 수 있습니다. 그러지 않으면 뷰가 자체 필기 입력 시작 로직을 구현해야 합니다.

자동 필기 입력 시작

뷰에 단일 텍스트 편집기만 표시되고 다른 콘텐츠는 표시되지 않는 경우 뷰는 setAutoHandwritingEnabled(true)를 호출하여 뷰 시스템의 자동 필기 입력 시작을 선택할 수 있습니다.

자동 필기 입력을 사용 설정하면 뷰의 필기 입력 경계 내에서 시작되는 스타일러스 움직임이 자동으로 필기 입력 모드를 시작합니다. 입력 방식 편집기 (IME)는 스타일러스 모션 이벤트를 수신하고 인식된 텍스트를 커밋합니다.

스타일러스 모션 이벤트 감지 범위를 나타내는 직사각형이 둘러싸인 입력 필드.
그림 1. EditText 필드 경계 내의 필기 입력

맞춤 필기 입력 시작

뷰에 단일 텍스트 편집기 외에 여러 텍스트 편집기 또는 콘텐츠가 포함되어 있는 경우 뷰는 다음과 같이 자체 필기 입력 시작 로직을 구현해야 합니다.

  1. setAutoHandwritingEnabled(false)를 호출하여 뷰 시스템의 자동 필기 입력 시작을 선택 해제합니다.

  2. 뷰 내에 표시되는 모든 텍스트 편집기를 추적합니다.

  3. dispatchTouchEvent()의 뷰에서 수신한 모션 이벤트를 모니터링합니다.

    • 텍스트 편집기의 필기 입력 경계 내에서 스타일러스 모션이 발생하면 텍스트 편집기에 포커스를 둡니다 (아직 포커스가 없는 경우).

    • 편집기에 아직 포커스가 없었다면 InputMethodManager#restartInput()를 호출하여 새 콘텐츠로 편집기의 IME를 다시 시작합니다.

    • InputMethodManager#startStylusHandwriting()를 호출하여 스타일러스 필기 입력 세션을 시작합니다.

텍스트 편집기가 스크롤 가능한 뷰 내에 있는 경우 편집기의 필기 입력 범위 내의 스타일러스 이동은 스크롤이 아닌 필기 입력으로 간주해야 합니다. 스크롤 가능한 상위 뷰가 텍스트 편집기에서 터치 이벤트를 가로채지 않도록 ViewParent#requestDisallowInterceptTouchEvent()를 사용합니다.

API 세부정보

  • MotionEvent#getToolType() - MotionEvent가 스타일러스에서 비롯되었는지를 나타냅니다. 이 경우 반환 값은 TOOL_TYPE_STYLUS 또는 TOOL_TYPE_ERASER입니다.

  • InputMethodManager#isStylusHandwritingAvailable(): IME가 스타일러스 필기 입력을 지원하는지를 나타냅니다. 필기 입력 사용 가능 여부가 변경되었을 수 있으므로 모든 InputMethodManager#startStylusHandwriting() 호출 전에 이 메서드를 호출합니다.

  • InputMethodManager#startStylusHandwriting(): IME가 필기 입력 모드로 전환됩니다. ACTION_CANCEL 모션 이벤트가 앱에 전달되어 현재 동작을 취소합니다. 스타일러스 모션 이벤트가 더 이상 앱에 전달되지 않습니다.

    이미 앱에 전달된 현재 동작의 스타일러스 모션 이벤트는 IME에 전달됩니다. IME는 IME가 다음의 모든 MotionEvent 객체를 수신하는 스타일러스 잉크 창을 표시해야 합니다. IME는 InputConnection API를 사용하여 인식된 필기 입력 텍스트를 커밋합니다.

    IME가 필기 입력 모드로 전환할 수 없는 경우 이 메서드 호출은 작동하지 않습니다.

필기 입력 지원 선언

View#onCreateInputConnection(EditorInfo)EditorInfo 인수를 채울 때 setStylusHandwritingEnabled()를 호출하여 텍스트 편집기에서 필기 입력을 지원한다고 IME에 알립니다. setSupportedHandwritingGestures()setSupportedHandwritingGesturePreviews()를 사용하여 지원되는 동작을 선언합니다.

필기 입력 동작 지원

IME는 텍스트를 선택하기 위해 원을 그리거나 텍스트를 낙서하여 삭제하는 등 다양한 필기 입력 동작을 지원할 수 있습니다.

그림 2. 텍스트를 선택하려면 원을 그리세요.
그림 3. 낙서로 텍스트를 삭제합니다.

맞춤 편집기는 InputConnection#performHandwritingGesture()InputConnection#previewHandwritingGesture()를 구현하여 SelectGesture, DeleteGesture, InsertGesture와 같은 다양한 HandwritingGesture 유형을 지원합니다.

View#onCreateInputConnection(EditorInfo)EditorInfo 인수를 채울 때 지원되는 필기 입력 동작을 선언합니다 (필기 입력 지원 선언 섹션 참고).

API 세부정보

  • InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer) - 동작을 구현합니다. HandwritingGesture 인수에는 텍스트에서 동작을 실행할 위치를 결정하는 데 사용할 수 있는 위치 정보가 포함됩니다. 예를 들어 SelectGesture는 선택된 텍스트 범위를 지정하는 RectF 객체를 제공하고 InsertGesture는 텍스트를 삽입할 텍스트 오프셋을 지정하는 PointF 객체를 제공합니다.

    ExecutorIntConsumer 매개변수를 사용하여 작업 결과를 다시 전송합니다. 실행자와 소비자 인수가 모두 제공되면 실행자를 사용하여 IntConsumer#accept()를 호출합니다. 예를 들면 다음과 같습니다.

    
    executor.execute { consumer.accept(HANDWRITING_GESTURE_RESULT_SUCCESS) }
    
    
  • HandwritingGesture#getFallbackText() - 필기 입력 동작 영역 아래에 적용 가능한 텍스트가 없는 경우 IME가 커서 위치에서 커밋하는 대체 텍스트를 제공합니다.

    IME에서 스타일러스 동작이 동작 작업을 실행하는지 아니면 텍스트를 필기로 쓰기 위한 것인지 확인할 수 없는 경우가 있습니다. 맞춤 텍스트 편집기는 사용자의 의도를 확인하고 동작 위치에서 컨텍스트에 따라 적절한 작업을 실행해야 합니다.

    예를 들어 IME에서 사용자가 아래쪽 캐럿 ⋁을 그려 공백 삽입 동작을 하려고 했는지 아니면 문자 'v'를 필기하려고 했는지 확인할 수 없는 경우 IME는 대체 텍스트 'v'와 함께 InsertGesture를 전송할 수 있습니다.

    편집기에서 먼저 공백 삽입 동작을 실행해야 합니다. 동작을 실행할 수 없는 경우 (예: 지정된 위치에 텍스트가 없는 경우) 편집기는 다시 커서 위치에 'v'를 삽입해야 합니다.

  • InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal): 진행 중인 동작을 미리 봅니다. 예를 들어 사용자가 텍스트 주위에 원을 그리기 시작하면 결과 선택의 실시간 미리보기가 표시되고 사용자가 그리기를 계속할 때 계속 업데이트됩니다. 특정 동작 유형만 미리 볼 수 있습니다 (PreviewableHandwritingGesture 참고).

    CancellationSignal 매개변수는 IME에서 미리보기를 취소하는 데 사용할 수 있습니다. 다른 이벤트가 미리보기를 방해하는 경우 (예: 텍스트가 프로그래매틱 방식으로 변경되거나 새로운 InputConnection 명령어가 발생하는 경우) 맞춤 편집기에서 미리보기를 취소할 수 있습니다.

    미리보기 동작은 표시 전용이며 편집기의 상태를 변경하지 않습니다. 예를 들어 SelectGesture 미리보기는 편집기의 현재 선택 범위를 숨기고 동작 미리보기 범위를 강조표시합니다. 하지만 미리보기가 취소되면 편집기에서 이전 선택 범위를 복원해야 합니다.

커서 위치 및 기타 위치 데이터 제공

필기 입력 모드에서는 IME가 InputConnection#requestCursorUpdates()를 사용하여 커서 위치와 기타 위치 데이터를 요청할 수 있습니다. 커스텀 편집기는 InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo) 호출로 응답합니다. 스타일러스 필기 입력과 관련된 CursorAnchorInfo의 데이터는 다음 CursorAnchorInfo.Builder 메서드를 통해 제공됩니다.

  • setInsertionMarkerLocation() - 커서 위치를 설정합니다. IME는 이 값을 사용하여 필기 입력 잉크를 커서 위치에 애니메이션으로 표시합니다.
  • setEditorBoundsInfo(): 편집기의 경계와 필기 입력 범위를 설정합니다. IME는 이 데이터를 사용하여 IME의 필기 입력 툴바를 화면에 배치합니다.
  • addVisibleLineBounds() - 편집기의 모든 표시되는 (또는 부분적으로 표시되는) 텍스트 줄의 경계를 설정합니다. IME는 선 경계를 사용하여 필기 입력 동작 인식의 정확도를 높입니다.
  • setTextAppearanceInfo() - 텍스트 입력 필드에서 파생된 정보로 텍스트 모양을 설정합니다. IME는 이 정보를 사용하여 필기 입력 잉크의 스타일을 지정합니다.

스타일러스 필기 입력 마우스 오버 아이콘 표시

스타일러스가 맞춤 텍스트 편집기의 필기 입력 경계 위로 마우스를 가져가고 선택한 IME가 스타일러스 필기 입력을 지원할 때 스타일러스 필기 입력 마우스 오버 아이콘을 표시합니다(InputMethodManager#isStylusHandwritingAvailable()).

스타일러스 필기 입력용 마우스 오버 아이콘을 가져오려면 View#onResolvePointerIcon()를 재정의합니다. 재정의에서 PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING)를 호출하여 시스템의 스타일러스 필기 입력 마우스 오버 아이콘에 액세스합니다.

추가 리소스