최적의 그리기 성능을 얻으려면 InProgressStrokesView
클래스의 startStroke()
, addToStroke()
, finishStroke()
메서드를 사용하여 MotionEvent
객체를 입력으로 전달합니다.
UI 구성요소 설정
InProgressStrokesView
를 뷰 계층 구조에 통합합니다.<FrameLayout> <ScrollView android:id="@+id/my_content" android:width="match_parent" android:height="match_parent" > <!-- Your content here. --> </ScrollView> <androidx.ink.authoring.InProgressStrokesView android:id="@+id/in_progress_strokes_view" android:width="match_parent" android:height="match_parent" /> </FrameLayout>
InProgressStrokesView 인스턴스화
활동이나 프래그먼트의 [
onCreate()
][ink-draw-include6] 메서드 내에서InProgressStrokesView
참조를 가져오고 사용자 입력을 관리하기 위한 터치 리스너를 설정합니다.class MyActivity : View.OnTouchListener { private lateinit var contentView: ScrollView private lateinit var inProgressStrokesView: InProgressStrokesView private lateinit var predictor: MotionEventPredictor // ... other variables override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) predictor = MotionEventPredictor.newInstance(contentView) contentView = findViewById(R.id.my_content) contentView.setOnTouchListener(touchListener) inProgressStrokesView = findViewById(R.id.in_progress_strokes_view) } // ... (touchListener implementation) }
터치 이벤트 처리
UI 구성요소를 설정했으므로 이제 터치 이벤트를 기반으로 그리기를 시작할 수 있습니다.
MotionEvent
작업InProgressStrokesView
메서드설명
획 렌더링 시작
획 렌더링 계속
획 렌더링 완료
손바닥 움직임 무시 기능을 구현하고 획을 취소합니다.
class MyActivity : View.OnTouchListener { private lateinit var contentView: ScrollView private lateinit var inProgressStrokesView: InProgressStrokesView private var pointerId = -1 private var strokeId: InProgressStrokeId? = null private lateinit var predictor: MotionEventPredictor override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) contentView = findViewById(R.id.my_content) predictor = MotionEventPredictor.create(contentView) contentView.setOnTouchListener(touchListener) inProgressStrokesView = findViewById(R.id.in_progress_strokes_view) } private val touchListener = { view: View, event: MotionEvent -> predictor.record(event) when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // First pointer - treat it as inking. view.requestUnbufferedDispatch(event) val pointerIndex = event.actionIndex pointerIdToStrokeId[event.getPointerId(pointerIndex)] = inProgressStrokesView.startStroke(event, pointerId) return true } MotionEvent.ACTION_POINTER_DOWN -> { val stroke = strokeId ?: return false inProgressStrokesView.cancelStroke(stroke, event) strokeId = null pointerId = -1 return false } MotionEvent.ACTION_MOVE -> { val predictedEvent = predictor.predict() try { for (pointerIndex in 0 until pointerCount) { val strokeId = pointerIdToStrokeId[event.getPointerId(pointerIndex)] ?: continue inProgressStrokesView.addToStroke(event, pointerId, strokeId, predictedEvent) } finally { predictedEvent?.recycle() } } } MotionEvent.ACTION_UP -> { val pointerIndex = event.actionIndex val strokeId = pointerIdToStrokeId[event.getPointerId(pointerIndex)] ?: return false inProgressStrokesView.finishStroke(event, pointerId, strokeId) return true } MotionEvent.ACTION_CANCEL -> { val pointerIndex = event.actionIndex val strokeId = pointerIdToStrokeId[event.getPointerId(pointerIndex)] ?: return false inProgressStrokesView.cancelStroke(strokeId, event) return true } } return false } }
완료된 획 처리
finishStroke()
를 호출하면 획이 완료로 표시됩니다. 하지만 완료 프로세스는 즉각적으로 이루어지지 않습니다. 획은 완전히 처리되고finishStroke()
이 호출된 직후, 특히 진행 중인 다른 획이 없는 경우 애플리케이션에서 액세스할 수 있게 됩니다. 이렇게 하면 획이 완료됨으로 클라이언트에 전달되기 전에 모든 그리기 작업이 완료됩니다.완료된 획을 검색하는 방법에는 두 가지가 있습니다.
- 활동 또는 ViewModel 내에서
InProgressStrokesFinishedListener
인터페이스를 구현하고addFinishedStrokesListener
를 사용하여InProgressStrokesView
에 리스너를 등록합니다. InProgressStrokesView
의getFinishedStrokes()
메서드를 사용하여 완료된 모든 획을 직접 가져올 수 있습니다.
class MyActivity : ComponentActivity(), InProgressStrokesFinishedListener { ... private val finishedStrokesState = mutableStateOf(emptySet<Stroke>()) override fun onCreate(savedInstanceState: Bundle?) { ... inProgressStrokesView.addFinishedStrokesListener(this) } // ... (handle touch events) @UiThread override fun onStrokesFinished(strokes: Map<InProgressStrokeId, Stroke>) { finishedStrokesState.value += strokes.values inProgressStrokesView.removeFinishedStrokes(strokes.keys) } }
완성된 획을 가져온 후에는
ViewStrokeRenderer
를CanvasStrokeRenderer
위에 빌드된 상위 수준의 추상화로 사용할 수 있습니다. 이렇게 하면 뷰 계층 구조 내의 렌더링 프로세스를 더욱 단순화할 수 있습니다.class DrawingView(context: Context) : View(context) { private val viewStrokeRenderer = ViewStrokeRenderer(myCanvasStrokeRenderer, this) override fun onDraw(canvas: Canvas) { viewStrokeRenderer.drawWithStrokes(canvas) { scope -> canvas.scale(myZoomLevel) canvas.rotate(myRotation) canvas.translate(myPanX, myPanY) scope.drawStroke(myStroke) // Draw other objects including more strokes, apply more transformations, ... } } }
- 활동 또는 ViewModel 내에서